Das offizielle postgres-Image auf Docker Hub ist eine der saubersten Varianten, Postgres lokal oder im CI laufen zu lassen — definierte Version, schnelles Wegwerfen, keine Reste auf dem Host. In diesem Artikel zeigen wir die wichtigsten Patterns: Single-Container-Setup, persistente Volumes, Initialdaten und ein typisches docker-compose.yaml für die Entwicklung.
Schnellstart
docker run --name pg18 \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
-d postgres:18Was passiert hier:
--name pg18gibt dem Container einen festen Namen, sodass spätere Befehle (docker exec,docker logs) ihn referenzieren können.-e POSTGRES_PASSWORD=secretsetzt das Passwort des Default-Userspostgres. Diese Variable ist Pflicht; ohne sie startet das Image nicht (außer mitPOSTGRES_HOST_AUTH_METHOD=trust, was du nur in Wegwerf-Tests nutzen solltest).-p 5432:5432mappt Port 5432 vom Container auf den Host.postgres:18ist die feste Version. Verwende möglichst nichtlatest— auch lokal nicht. Sonst landest du nach einem Major-Release auf einer neuen Version, die das alte Daten-Verzeichnis nicht öffnet.
Verbindung von außen:
psql -h localhost -U postgres
# Password: secretOder direkt im Container:
docker exec -it pg18 psql -U postgresDaten persistent speichern
Das obige Setup verliert alle Daten, sobald der Container entfernt wird. Für ernsthafte Verwendung brauchst du ein Volume:
docker run --name pg18 \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
-v pg18_data:/var/lib/postgresql/data \
-d postgres:18Das Image schreibt sein Daten-Verzeichnis nach /var/lib/postgresql/data. Mit -v pg18_data:/var/lib/postgresql/data legst du ein benanntes Docker-Volume an, das zwischen Container-Neustarts erhalten bleibt. Auflisten mit:
docker volume ls
docker volume inspect pg18_dataBind-Mount statt benanntem Volume? Möglich, aber nicht empfohlen — auf macOS und Windows leidet die Performance, und Permissions zwischen Host und Container sind eine ständige Reibungsquelle. Benannte Volumes sind die saubere Default-Wahl.
Wichtige Environment-Variablen
Das Image kennt eine kleine, klar definierte Schnittstelle:
| Variable | Wirkung |
|---|---|
POSTGRES_PASSWORD | Pflicht (außer Trust). Passwort des Superusers. |
POSTGRES_USER | Name des Superusers. Default: postgres. |
POSTGRES_DB | Datenbank, die beim ersten Start angelegt wird. Default: gleicher Name wie POSTGRES_USER. |
POSTGRES_INITDB_ARGS | Zusätzliche Argumente für initdb, z. B. --data-checksums. |
POSTGRES_HOST_AUTH_METHOD | Authentifizierungsmethode in der generierten pg_hba.conf. Default: scram-sha-256. |
PGDATA | Pfad innerhalb des Containers für das Daten-Verzeichnis. Hat einen sinnvollen Default. |
TZ | Zeitzone des Containers. Wirkt sich auf now()-Timestamps aus, sofern keine TZ in der Spalte steht. |
Wichtig: Diese Variablen wirken nur beim ersten Start, also wenn das Daten-Verzeichnis leer ist. Wer später POSTGRES_PASSWORD ändert, bekommt damit nichts geändert — das Passwort liegt schon im Volume. Für nachträgliche Änderungen: ALTER USER postgres WITH PASSWORD '…'; in psql.
Initialdaten beim ersten Start einspielen
Das Image führt beim ersten Start alle *.sql, *.sql.gz und *.sh-Dateien aus dem Verzeichnis /docker-entrypoint-initdb.d/ aus — alphabetisch sortiert.
docker run --name pg18 \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
-v pg18_data:/var/lib/postgresql/data \
-v ./initdb:/docker-entrypoint-initdb.d:ro \
-d postgres:18Praktisch für lokale Entwicklung: ein 01-schema.sql, ein 02-seed.sql, schon hat jeder Entwickler beim ersten Start denselben Stand. Wieder gilt: nur einmalig, beim leeren Daten-Verzeichnis. Wenn du zwischendurch das Schema änderst, brauchst du eine richtige Migration (Flyway, Liquibase, sqitch, golang-migrate, …) — Init-Scripts sind kein Migrations-Werkzeug.
docker-compose-Setup
Für die meisten Projekte ist docker-compose.yaml lesbarer und stabiler als ein einzelner docker run-Befehl:
services:
db:
image: postgres:18
container_name: pg18
restart: unless-stopped
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
ports:
- "5432:5432"
volumes:
- pg18_data:/var/lib/postgresql/data
- ./initdb:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 5s
retries: 10
volumes:
pg18_data:Starten und stoppen:
docker compose up -d
docker compose logs -f db
docker compose down # Container weg, Volume bleibt
docker compose down -v # Container UND Volume wegdocker compose down -v ist der bequeme „Schema neu, Daten weg“-Knopf für lokale Entwicklung. Im CI ist das oft genau, was man will.
Backup und Restore aus dem Container
Quick-and-dirty-Backup aus dem laufenden Container:
docker exec pg18 pg_dump -U app app > backup.sqlRestore in einen frischen Container:
cat backup.sql | docker exec -i pg18 psql -U app -d appFür produktionsähnliche Backups gibt es ein eigenes Kapitel (pg_dump, pg_basebackup, WAL-Archivierung, Point-in-Time-Recovery).
FAQ
Warum schlägt der Start mit „database files are incompatible“ fehl?
Du hast ein Volume aus einer älteren Postgres-Version und im Compose image: postgres:18 stehen. Major-Versionen lesen die Daten der Vorgänger nicht direkt — entweder bewusst auf der alten Version bleiben, oder mit pg_upgrade (am einfachsten über Tools wie tianon/postgres-upgrade) das Volume migrieren.
Wie verbinde ich aus einem anderen Container auf den Datenbank-Container?
Innerhalb desselben docker-compose-Netzwerks erreicht man den DB-Container unter dem Service-Namen. Aus einem app-Service heraus also host: db, nicht localhost. localhost zeigt im Container immer auf den Container selbst.
Das Image ist ~400 MB groß. Geht das kleiner?
Es gibt das Tag postgres:18-alpine — auf Alpine basierend, deutlich schlanker. Die Funktionalität ist identisch; der einzige Unterschied ist die libc-Implementierung (musl statt glibc), was bei sehr exotischen Locales selten zu Unterschieden führen kann. Für Produktion sind beide Varianten verbreitet.
Kann ich Extensions wie postgis oder pgvector nachinstallieren?
Das offizielle Image hat eine begrenzte Auswahl. Für Extensions, die nicht enthalten sind, gibt es spezialisierte Images: postgis/postgis, pgvector/pgvector. Alternativ: eigenes Dockerfile FROM postgres:18 mit apt-get install postgresql-18-<ext>.
Die Logs sind sehr leise. Wo kommt mehr raus?
Per Default loggt das Image nur Verbindungen und Fehler. Mehr Detail bekommst du über command-Overrides in compose, z. B. command: ["postgres", "-c", "log_statement=all", "-c", "log_min_duration_statement=0"]. Vorsicht damit in Produktion — log_statement=all schreibt jedes SQL ins Log, inkl. Passwörtern in CREATE USER-Statements.
Warum ist pg_isready der Standard-Healthcheck?
pg_isready ist im Image enthalten, leichtgewichtig und prüft, ob der Server tatsächlich Verbindungen akzeptiert — nicht nur, ob der Prozess läuft. Während eines Recoveries oder beim Booten antwortet er mit Exit-Code != 0, was Compose den Service als „starting“ oder „unhealthy“ markieren lässt. Andere Services können mit depends_on: { db: { condition: service_healthy } } darauf warten.
Weiterführende Ressourcen
Externe Quellen
- Docker Hub: postgres – Image-Dokumentation
- PostgreSQL Server Application: pg_isready
- docker-compose-Spezifikation – compose-spec.io
- Authentication Methods – PostgreSQL Documentation
- pgvector: Postgres-Extension für Vektor-Suche