Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/dockerhub-description.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Docker Hub description

# Keeps the Docker Hub page in sync with README.md, which is what people see
# when they find the image there.

on:
push:
branches: [main]
paths:
- README.md
- .github/workflows/dockerhub-description.yml
workflow_dispatch:

permissions:
contents: read

jobs:
update:
name: Update description
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PERSONAL_KEY }}
repository: ruxwez/postgres
short-description: PostgreSQL 14-18 with pgvector, PostGIS and pgmq. Official image, amd64 + arm64.
readme-filepath: README.md
# Turns relative links (./.docs/...) into absolute GitHub URLs.
enable-url-completion: true
129 changes: 114 additions & 15 deletions README.es.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,100 @@
# PostgreSQL Extensor

[![CI](https://github.com/ruxwez/postgres/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ruxwez/postgres/actions/workflows/ci.yml)
[![Docker Pulls](https://img.shields.io/docker/pulls/ruxwez/postgres)](https://hub.docker.com/r/ruxwez/postgres)
[![Release](https://img.shields.io/github/v/release/ruxwez/postgres)](https://github.com/ruxwez/postgres/releases)
[![License: MIT](https://img.shields.io/github/license/ruxwez/postgres)](./LICENSE)

> [Read in English](./README.md)

Imágenes Docker de PostgreSQL con extensiones populares preinstaladas.
**La imagen oficial de PostgreSQL con pgvector, PostGIS y pgmq preinstalados.** Búsqueda vectorial, consultas geoespaciales y colas de mensajes en un solo Postgres: sin servicios extra y sin tener que mantener una imagen propia.

- **Sustituye directamente** a `postgres`. Mismo entrypoint, mismas variables de entorno, todo igual; solo se añaden las extensiones.
- **PostgreSQL 14 a 18**, en `linux/amd64` y `linux/arm64`. Las dos arquitecturas se prueban antes de cada publicación.
- **Tags inmutables con versión** (`18-v1.0.0`): una actualización nunca te llega si no la pides.
- **Se reconstruye cada semana** con los parches de seguridad de la imagen base.

## Propósito
## Inicio rápido

Las imágenes oficiales de `postgres` no incluyen extensiones como pgvector, PGMQ o PostGIS. Instalarlas a mano en cada despliegue es tedioso y lento. Este proyecto publica las imágenes oficiales con esas extensiones ya instaladas, sin cambiar nada más.
```sh
docker run -d --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 ruxwez/postgres:18-v1
```

## Imágenes Docker
O con Docker Compose:

```yaml
services:
db:
image: ruxwez/postgres:18-v1
environment:
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql # PostgreSQL 17 o anterior: /var/lib/postgresql/data

volumes:
pgdata:
```

Disponibles en [Docker Hub](https://hub.docker.com/r/ruxwez/postgres):
Activa las extensiones que necesites en cada base de datos y úsalas juntas:

```sql
CREATE EXTENSION vector;
CREATE EXTENSION postgis;
CREATE EXTENSION pgmq;

CREATE TABLE places (
id bigserial PRIMARY KEY,
name text,
location geography(Point),
embedding vector(3)
);

INSERT INTO places (name, location, embedding) VALUES
('Café Central', 'POINT(-3.7038 40.4168)', '[0.9, 0.1, 0.0]'),
('Tapas Bar', 'POINT(-3.7010 40.4180)', '[0.2, 0.8, 0.1]'),
('Café del Mar', 'POINT( 1.3090 38.9800)', '[0.9, 0.2, 0.0]');

-- El sitio más parecido a "café" a menos de 5 km de la Puerta del Sol → Café Central
SELECT name
FROM places
WHERE ST_DWithin(location, 'POINT(-3.7037 40.4169)', 5000)
ORDER BY embedding <=> '[1, 0, 0]'
LIMIT 1;

-- Encola un trabajo en segundo plano para recalcular embeddings
SELECT pgmq.create('embeddings');
SELECT pgmq.send(queue_name => 'embeddings', msg => '{"place_id": 1}');
SELECT msg_id, message FROM pgmq.read(queue_name => 'embeddings', vt => 30, qty => 1);
```

PostgreSQL 14, 15, 16, 17 y 18, para `linux/amd64` y `linux/arm64`.
## Por qué esta imagen

### Tags
Si solo necesitas una extensión, su propia imagen es una gran opción. Esta es para cuando necesitas varias:

| | `postgres` | `pgvector/pgvector` | `postgis/postgis` | **`ruxwez/postgres`** |
|---|:-:|:-:|:-:|:-:|
| pgvector | — | ✅ | — | ✅ |
| PostGIS | — | — | ✅ | ✅ |
| pgmq | — | — | — | ✅ |
| `linux/arm64` (Apple Silicon, Graviton, Raspberry Pi) | ✅ | ✅ | — | ✅ |

<sub>Comprobado en los tags de PostgreSQL 18 de cada imagen (`pg18`, `18-3.6`) en octubre de 2026.</sub>

## Tags

Elige el tag según cuántos cambios estés dispuesto a aceptar:

| Tag | Ejemplo | ¿Cambia? |
|-----|---------|----------|
| `<pg>-v<X.Y.Z>` | `18-v1.0.0` | **Nunca.** Se publica una vez por release y no se sobrescribe. |
| `<pg>-v<X>` | `18-v1` | Con cada release `v1.x.y`: solo cambios compatibles. |
| `<pg>-v<X>` | `18-v1` | Con cada release `v1.x.y`: solo cambios compatibles. **Recomendado.** |
| `<pg>` | `18` | En cada build, incluidos los rebuilds semanales y los cambios incompatibles. |
| `latest` | | Igual que el `<pg>` más reciente (ahora `18`). |

La versión `vX.Y.Z` es la de este proyecto, no la de PostgreSQL: cambia cuando cambia una extensión. Cada [release](https://github.com/ruxwez/postgres/releases) indica las versiones exactas de PostgreSQL y de las extensiones de cada imagen. En [Releases](./.docs/releases.es.md) se explica qué significa cada tipo de subida.

```sh
docker pull ruxwez/postgres:18-v1 # recomendado para producción
```

Las versiones también están en los labels de la imagen:

```sh
Expand All @@ -48,16 +112,51 @@ Para una reproducibilidad exacta, fija un digest: `ruxwez/postgres@sha256:…`.
| [PostGIS](https://postgis.net/) | 3.6.4 | Paquete apt de PGDG (`postgresql-N-postgis-3`) |
| [contrib](https://www.postgresql.org/docs/current/contrib.html) | — | Incluida en la imagen base (`pg_stat_statements`, `pg_trgm`, …) |

## Úsala en tests y CI

**GitHub Actions**, como service container:

```yaml
services:
postgres:
image: ruxwez/postgres:18-v1
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
```

**Testcontainers (Java).** Declárala como sustituta de `postgres` para que el módulo de PostgreSQL la acepte:

```java
var image = DockerImageName.parse("ruxwez/postgres:18-v1").asCompatibleSubstituteFor("postgres");
try (var postgres = new PostgreSQLContainer<>(image)) {
postgres.start();
}
```

**Testcontainers (Python):**

```python
from testcontainers.postgres import PostgresContainer

with PostgresContainer("ruxwez/postgres:18-v1") as postgres:
url = postgres.get_connection_url()
```

## Cómo funciona

Todo está en dos archivos:

- [`Dockerfile`](./Dockerfile): parte de `postgres:N`, instala las extensiones que hay en apt y copia las que se compilan en etapas de build separadas. Los compiladores y headers nunca llegan a la imagen final.
- [`docker-bake.hcl`](./docker-bake.hcl): la lista de versiones de PostgreSQL y sus tags. El CI también la lee.

## Uso local

Requiere Docker con Buildx.
Para construir y probar en local (requiere Docker con Buildx):

```sh
make test # construye PG 18 y ejecuta los tests
Expand Down
129 changes: 114 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,100 @@
# PostgreSQL Extensor

[![CI](https://github.com/ruxwez/postgres/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ruxwez/postgres/actions/workflows/ci.yml)
[![Docker Pulls](https://img.shields.io/docker/pulls/ruxwez/postgres)](https://hub.docker.com/r/ruxwez/postgres)
[![Release](https://img.shields.io/github/v/release/ruxwez/postgres)](https://github.com/ruxwez/postgres/releases)
[![License: MIT](https://img.shields.io/github/license/ruxwez/postgres)](./LICENSE)

> [Leer en español](./README.es.md)

Pre-built PostgreSQL Docker images with popular extensions.
**The official PostgreSQL image with pgvector, PostGIS and pgmq preinstalled.** Vector search, geospatial queries and message queues in a single Postgres: no extra services, and no custom image to maintain.

- **Drop-in replacement** for `postgres`. Same entrypoint, same environment variables, same everything; only the extensions are added.
- **PostgreSQL 14 to 18**, on `linux/amd64` and `linux/arm64`. Both architectures are tested before every publish.
- **Immutable versioned tags** (`18-v1.0.0`), so an update never reaches you unless you ask for it.
- **Rebuilt weekly** with the base image's security fixes.

## Purpose
## Quick start

Official `postgres` images don't include extensions like pgvector, PGMQ or PostGIS. Installing them by hand in every deployment is tedious and slow. This project publishes the official images with those extensions already installed, and nothing else changed.
```sh
docker run -d --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 ruxwez/postgres:18-v1
```

## Docker images
Or with Docker Compose:

```yaml
services:
db:
image: ruxwez/postgres:18-v1
environment:
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql # PostgreSQL 17 and older: /var/lib/postgresql/data

volumes:
pgdata:
```

Available on [Docker Hub](https://hub.docker.com/r/ruxwez/postgres):
Enable the extensions you need in each database, and use them together:

```sql
CREATE EXTENSION vector;
CREATE EXTENSION postgis;
CREATE EXTENSION pgmq;

CREATE TABLE places (
id bigserial PRIMARY KEY,
name text,
location geography(Point),
embedding vector(3)
);

INSERT INTO places (name, location, embedding) VALUES
('Café Central', 'POINT(-3.7038 40.4168)', '[0.9, 0.1, 0.0]'),
('Tapas Bar', 'POINT(-3.7010 40.4180)', '[0.2, 0.8, 0.1]'),
('Café del Mar', 'POINT( 1.3090 38.9800)', '[0.9, 0.2, 0.0]');

-- The place most similar to "coffee" within 5 km of Puerta del Sol → Café Central
SELECT name
FROM places
WHERE ST_DWithin(location, 'POINT(-3.7037 40.4169)', 5000)
ORDER BY embedding <=> '[1, 0, 0]'
LIMIT 1;

-- Queue a background job to recompute embeddings
SELECT pgmq.create('embeddings');
SELECT pgmq.send(queue_name => 'embeddings', msg => '{"place_id": 1}');
SELECT msg_id, message FROM pgmq.read(queue_name => 'embeddings', vt => 30, qty => 1);
```

PostgreSQL 14, 15, 16, 17 and 18, for `linux/amd64` and `linux/arm64`.
## Why this image

### Tags
If you only need one extension, its own image is a great choice. This one is for when you need several:

| | `postgres` | `pgvector/pgvector` | `postgis/postgis` | **`ruxwez/postgres`** |
|---|:-:|:-:|:-:|:-:|
| pgvector | — | ✅ | — | ✅ |
| PostGIS | — | — | ✅ | ✅ |
| pgmq | — | — | — | ✅ |
| `linux/arm64` (Apple Silicon, Graviton, Raspberry Pi) | ✅ | ✅ | — | ✅ |

<sub>Checked on the PostgreSQL 18 tags of each image (`pg18`, `18-3.6`) in October 2026.</sub>

## Tags

Pick a tag depending on how much change you're willing to accept:

| Tag | Example | Changes? |
|-----|---------|----------|
| `<pg>-v<X.Y.Z>` | `18-v1.0.0` | **Never.** Published once per release, never overwritten. |
| `<pg>-v<X>` | `18-v1` | With every `v1.x.y` release: compatible updates only. |
| `<pg>-v<X>` | `18-v1` | With every `v1.x.y` release: compatible updates only. **Recommended.** |
| `<pg>` | `18` | On every build, including weekly rebuilds and breaking changes. |
| `latest` | | Same as the newest `<pg>` (currently `18`). |

The `vX.Y.Z` version belongs to this project, not to PostgreSQL: it changes when an extension changes. Each [release](https://github.com/ruxwez/postgres/releases) lists the exact PostgreSQL and extension versions of every image. See [Releases](./.docs/releases.md) for what each kind of bump means.

```sh
docker pull ruxwez/postgres:18-v1 # recommended for production
```

The versions are also in the image labels:

```sh
Expand All @@ -48,16 +112,51 @@ For byte-for-byte reproducibility, pin a digest: `ruxwez/postgres@sha256:…`.
| [PostGIS](https://postgis.net/) | 3.6.4 | PGDG apt package (`postgresql-N-postgis-3`) |
| [contrib](https://www.postgresql.org/docs/current/contrib.html) | — | Included in the base image (`pg_stat_statements`, `pg_trgm`, …) |

## Use it in tests and CI

**GitHub Actions** service container:

```yaml
services:
postgres:
image: ruxwez/postgres:18-v1
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
```

**Testcontainers (Java).** Declare it as a substitute for `postgres`, so the PostgreSQL module accepts it:

```java
var image = DockerImageName.parse("ruxwez/postgres:18-v1").asCompatibleSubstituteFor("postgres");
try (var postgres = new PostgreSQLContainer<>(image)) {
postgres.start();
}
```

**Testcontainers (Python):**

```python
from testcontainers.postgres import PostgresContainer

with PostgresContainer("ruxwez/postgres:18-v1") as postgres:
url = postgres.get_connection_url()
```

## How it works

Everything is in two files:

- [`Dockerfile`](./Dockerfile): starts from `postgres:N`, installs the apt-packaged extensions, and copies in the ones compiled in separate build stages. Compilers and headers never reach the final image.
- [`docker-bake.hcl`](./docker-bake.hcl): the list of PostgreSQL versions and their tags. CI reads it too.

## Local usage

Requires Docker with Buildx.
To build and test locally (requires Docker with Buildx):

```sh
make test # build PG 18 and run the tests
Expand Down
Loading