PostgreSQL images with pgx_ulid and pgvector preinstalled
6.6K
lcdss/postgres is the official PostgreSQL Docker image with extensions
already built in, so you get them without compiling anything:
pgx_ulid — a native ulid column
type and gen_ulid()pgvector — the vector type with
HNSW and IVFFlat indexesIf you already use the official Postgres image, this one behaves the same way. The only difference is that those extensions are available inside the database.
linux/amd64 only. Upstream postgres publishes more architectures, but these
images do not, so on Apple Silicon and other arm64 hosts Docker falls back to
emulation.
docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
lcdss/postgres:18-alpine
docker exec -it postgres psql -U postgres
CREATE EXTENSION pgx_ulid;
CREATE EXTENSION vector;
CREATE TABLE users (
id ulid PRIMARY KEY DEFAULT gen_ulid(),
email text NOT NULL UNIQUE,
embedding vector(3),
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO users (email, embedding)
VALUES ('[email protected]', '[1,2,3]')
RETURNING id;
CREATE INDEX ON users USING hnsw (embedding vector_l2_ops);
SELECT id FROM users ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
services:
postgres:
image: lcdss/postgres:18-alpine
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: app
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/18/docker
volumes:
pgdata:
Postgres 18 images store data under /var/lib/postgresql/18/docker; older
majors use /var/lib/postgresql/data. Match whatever the tag you pin uses.
The image ships the extension files and enables nothing. Run CREATE EXTENSION
in each database that needs one, exactly as you would on the official image:
CREATE EXTENSION IF NOT EXISTS pgx_ulid;
CREATE EXTENSION IF NOT EXISTS vector;
To see what is available and what is installed:
SELECT name, default_version FROM pg_available_extensions;
SELECT extname, extversion FROM pg_extension;
If you would rather have them created for you, POSTGRES_ENABLE_EXTENSIONS
runs CREATE EXTENSION on first startup, in postgres, template1, and
POSTGRES_DB:
docker run -e POSTGRES_ENABLE_EXTENSIONS="pgx_ulid vector" ...
That reaches template1, so every database created afterwards inherits those
extensions and pg_dump of them emits CREATE EXTENSION, which only restores
where the extension exists. Leave it unset if your dumps have to restore on a
stock image. Like every initialization script, it runs on a fresh data
directory only.
gen_monotonic_ulid() needs the library preloaded, which the image does not do
by default, and it only works on a tag that carries pgx_ulid:
docker run -d \
--name postgres-monotonic \
-e POSTGRES_PASSWORD=postgres \
lcdss/postgres:18-alpine \
postgres -c shared_preload_libraries=pgx_ulid
CREATE EXTENSION IF NOT EXISTS pgx_ulid;
SELECT gen_monotonic_ulid();
In compose, pass the same override as command:.
Building an HNSW index on a large table wants more memory than the Docker
default shared memory size, so raise it when you hit could not resize shared memory segment:
docker run --shm-size=1g ...
Everything the official image supports applies here unchanged: the
POSTGRES_* variables, PGDATA, POSTGRES_INITDB_ARGS, scripts dropped into
/docker-entrypoint-initdb.d, the data volume layout, and the entrypoint
behavior. See https://hub.docker.com/_/postgres for that reference.
Tags mirror the official postgres image:
14 and neweralpine and trixie image familiesalpine and trixie tags, which move with the newest majorA plain tag carries every extension. A suffixed tag carries exactly one:
| Tag | Contents |
|---|---|
18-alpine, 18-trixie, alpine, trixie | pgx_ulid + vector |
18-alpine-ulid, alpine-ulid, … | pgx_ulid only |
18-alpine-vector, alpine-vector, … | vector only |
The live list is at https://hub.docker.com/r/lcdss/postgres/tags.
A scheduled job checks upstream daily and rebuilds a tag when the upstream digest or the build inputs change, so pinning a floating tag means the underlying major can change under you. Pin an exact major, or a digest, when that matters.
Each image records where it came from, which you can inspect before trusting a mirror:
docker image inspect lcdss/postgres:18-alpine \
--format '{{ json .Config.Labels }}'
org.opencontainers.image.base.name — the upstream tag it was built fromio.github.lcdss.postgres.source-digest — the upstream manifest digestio.github.lcdss.postgres.build-signature — the build inputs usedio.github.lcdss.postgres.variant — which extension set the tag carriesio.github.lcdss.postgres.extensions — each bundled extension and its reflcdss/postgres-ulid keeps being published and carries the same pgx_ulid
only content as before, so nothing breaks if you stay. New extensions land here
instead, so move when you want them:
-image: lcdss/postgres-ulid:18-alpine
+image: lcdss/postgres:18-alpine-ulid # same contents
+image: lcdss/postgres:18-alpine # also pgvector
Two things changed for everyone, on both repositories:
pgx_ulid is no longer created for you. Older images ran
CREATE EXTENSION pgx_ulid on a fresh data directory; these do not. Run it
yourself, or start the container with
POSTGRES_ENABLE_EXTENSIONS=pgx_ulid to keep the old behavior. Existing data
directories are unaffected — the extension is already there.io.github.lcdss.postgres-ulid.* to
io.github.lcdss.postgres.*, and the entrypoint init script is now
10-enable-extensions.sh.Each extension is compiled once per Postgres major and image family, published
as a standalone artifact, and copied into the mirrored image, along with one
initialization script that stays inert unless POSTGRES_ENABLE_EXTENSIONS is
set. Nothing else about the official image is modified: same entrypoint, same
defaults, no extensions created, no preloaded libraries.
Every extension is pinned to a known revision, listed in
extensions.json
and recorded on each image in the io.github.lcdss.postgres.extensions label.
Build, test, and automation details live in CONTRIBUTING.md.
This repository's own files are MIT licensed, see LICENSE. The images also carry everything upstream ships: PostgreSQL under the PostgreSQL License, the base image contents under their respective licenses, and each bundled extension under its own terms (pgx_ulid, pgvector).
Unofficial image, not affiliated with Docker, Inc., the PostgreSQL Global Development Group, or the maintainers of the bundled extensions.
Content type
Image
Digest
sha256:b7014943d…
Size
111 MB
Last updated
about 17 hours ago
docker pull lcdss/postgres:16-alpine-ulid