Imagen Minimal
Base debian:13-slim sin paquetes innecesarios. Build-tools eliminados después de compilación.
El container mother ejecuta PostgreSQL 16 en una imagen Docker personalizada y endurecida, basada en Debian 13 (trixie-slim). Está diseñado para entornos de desarrollo y producción con foco en seguridad y estabilidad.
La imagen está construida con los siguientes principios de seguridad:
Imagen Minimal
Base debian:13-slim sin paquetes innecesarios. Build-tools eliminados después de compilación.
Usuario No-Root
PostgreSQL corre como usuario postgres (UID 999) usando gosu para cambio seguro.
Filesystem Read-Only
Container en modo read_only: true con tmpfs para directorios de escritura.
Capabilities Mínimas
cap_drop: ALL con solo SETUID y SETGID habilitados para gosu.
# Base mínimaFROM debian:13-slim
# PostgreSQL 16 desde repositorio oficial PGDGARG PG_MAJOR=16ARG PG_VERSION=16.10-1.pgdg13+1
# Autenticación SCRAM obligatoria desde initdbgosu postgres initdb -D "$PGDATA" \ --auth-local=scram-sha-256 --auth-host=scram-sha-256 \ --encoding=UTF8 --locale="en_US.utf8"
# Healthcheck integradoHEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=5 \ CMD pg_isready -U "${POSTGRES_USER:-postgres}" || exit 1Control de acceso basado en host con SCRAM-SHA-256 obligatorio:
# Local: SCRAM obligatoriolocal all all scram-sha-256
# IPv4 - Solo localhost y red Docker bridgehost all all 127.0.0.1/32 scram-sha-256host all all 172.17.0.0/16 scram-sha-256
# IPv6 loopbackhost all all ::1/128 scram-sha-256| Regla | Propósito |
|---|---|
local ... scram-sha-256 | Conexiones via Unix socket requieren password SCRAM |
172.17.0.0/16 | Permite conexiones desde otros containers Docker |
No hay trust | Ninguna conexión sin autenticación |
No hay md5 | MD5 es vulnerable, solo SCRAM |
SCRAM-SHA-256: Hash de passwords resistente a ataques de diccionario y replay. Reemplaza MD5 deprecado.
Read-Only Filesystem: El container no puede escribir en su filesystem excepto en volúmenes montados y tmpfs.
CAP_DROP ALL: Elimina todas las Linux capabilities excepto las mínimas necesarias.
No Root: El proceso PostgreSQL nunca corre como root. El entrypoint usa gosu para cambiar a postgres.
Volumen Externo: Los datos viven en nostromo_pgdata, un volumen Docker externo y persistente.
# Conexioneslisten_addresses = '*'password_encryption = 'scram-sha-256'
# Memoriashared_buffers = 256MBwork_mem = 16MBmaintenance_work_mem = 128MB
# WAL (Write-Ahead Logging)wal_level = replicamax_wal_size = 1GBmin_wal_size = 80MB
# Logginglog_destination = 'stderr'logging_collector = onlog_min_duration_statement = 500mslog_line_prefix = '%m [%p] %u@%d '| Parámetro | Valor | Justificación |
|---|---|---|
shared_buffers | 256MB | Cache de datos en memoria, ~25% de RAM disponible |
work_mem | 16MB | Memoria por operación de sort/hash |
wal_level | replica | Preparado para streaming replication |
log_min_duration_statement | 500ms | Loguea queries lentos para optimización |
El script docker-entrypoint.sh maneja la inicialización del cluster:
flowchart TD
A[Container Start] --> B{PG_VERSION existe?}
B -->|Si| C[Cluster existente]
B -->|No| D[Ejecutar initdb]
D --> E[Copiar configs endurecidas]
E --> F[Iniciar server temporal]
F --> G{Scripts en initdb.d?}
G -->|Si| H[Ejecutar .sh/.sql/.sql.gz]
G -->|No| I[Saltar]
H --> I
I --> J[Detener server temporal]
J --> K[Marcar .nostromo_init_done]
C --> L[Iniciar PostgreSQL]
K --> L
# Si existe PG_VERSION, el cluster ya está inicializadoif gosu postgres bash -lc "test -f '$PGDATA/PG_VERSION'"; then echo ">> existing cluster detected, skipping initdb"else # Ejecutar initdb con SCRAM gosu postgres initdb -D "$PGDATA" \ --auth-local=scram-sha-256 \ --auth-host=scram-sha-256fiservices: mother: image: nostromo/postgres:16-hardened-stable container_name: mother restart: unless-stopped environment: POSTGRES_USER: "chris" TZ: "America/Santiago" ports: - "5432:5432" volumes: - nostromo_pgdata:/var/lib/postgresql/data - ./initdb:/docker-entrypoint-initdb.d:ro read_only: true tmpfs: - "/run/postgresql:rw,nosuid,nodev,mode=3777,size=32m" - "/var/lib/postgresql/tmp:rw,nosuid,nodev,mode=1777,size=256m" cap_drop: - ALL cap_add: - SETUID - SETGID healthcheck: test: ["CMD-SHELL", "pg_isready -U chris -d postgres"] interval: 30s timeout: 5s retries: 5| Mount | Tipo | Propósito |
|---|---|---|
nostromo_pgdata | Volume externo | Datos persistentes del cluster |
./initdb | Bind mount (ro) | Scripts de inicialización |
/run/postgresql | tmpfs | Socket Unix y locks |
/var/lib/postgresql/tmp | tmpfs | Archivos temporales de queries |
El container incluye healthcheck integrado:
pg_isready -U chris -d postgrescd c:\pg16-cleandocker compose build --no-cachedocker compose up -d# Desde hostpsql -h localhost -U chris -d postgres
# Desde otro containerpsql -h mother -U chris -d nostromo_commanddocker logs mother -f --tail 100
# Logs de PostgreSQL dentro del containerdocker exec mother cat /var/lib/postgresql/data/log/postgresql*.log# Dump completo del clusterdocker exec mother pg_dumpall -U chris | gzip > backup.sql.gz
# Backup de base de datos específicadocker exec mother pg_dump -U chris -d nostromo_common | gzip > common.sql.gz