diff --git a/.github/workflows/dockerhub-description.yml b/.github/workflows/dockerhub-description.yml new file mode 100644 index 0000000..f6b4b9f --- /dev/null +++ b/.github/workflows/dockerhub-description.yml @@ -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 diff --git a/README.es.md b/README.es.md index a851988..09a6cda 100644 --- a/README.es.md +++ b/README.es.md @@ -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) | ✅ | ✅ | — | ✅ | + +Comprobado en los tags de PostgreSQL 18 de cada imagen (`pg18`, `18-3.6`) en octubre de 2026. + +## Tags Elige el tag según cuántos cambios estés dispuesto a aceptar: | Tag | Ejemplo | ¿Cambia? | |-----|---------|----------| | `-v` | `18-v1.0.0` | **Nunca.** Se publica una vez por release y no se sobrescribe. | -| `-v` | `18-v1` | Con cada release `v1.x.y`: solo cambios compatibles. | +| `-v` | `18-v1` | Con cada release `v1.x.y`: solo cambios compatibles. **Recomendado.** | | `` | `18` | En cada build, incluidos los rebuilds semanales y los cambios incompatibles. | | `latest` | | Igual que el `` 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 @@ -48,6 +112,43 @@ 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: @@ -55,9 +156,7 @@ 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 diff --git a/README.md b/README.md index 70029a6..485f51e 100644 --- a/README.md +++ b/README.md @@ -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) | ✅ | ✅ | — | ✅ | + +Checked on the PostgreSQL 18 tags of each image (`pg18`, `18-3.6`) in October 2026. + +## Tags Pick a tag depending on how much change you're willing to accept: | Tag | Example | Changes? | |-----|---------|----------| | `-v` | `18-v1.0.0` | **Never.** Published once per release, never overwritten. | -| `-v` | `18-v1` | With every `v1.x.y` release: compatible updates only. | +| `-v` | `18-v1` | With every `v1.x.y` release: compatible updates only. **Recommended.** | | `` | `18` | On every build, including weekly rebuilds and breaking changes. | | `latest` | | Same as the newest `` (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 @@ -48,6 +112,43 @@ 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: @@ -55,9 +156,7 @@ 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