
5 Best Practices for Postgres in Docker in 2026
Jonas ScholzPostgres runs perfectly fine in Docker. The problems usually come from treating a database container like a stateless web app container.
If you are using PostgreSQL in Docker in 2026, these are the five practices that matter most.
1. Pin the Postgres image version
Do not use postgres:latest for anything you care about. Pin a major version at minimum, and pin a minor version when you need reproducible production builds.
For local development, this is usually good enough:
docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=change-me \
postgres:17
For production-like environments, be more explicit:
services:
postgres:
image: postgres:17.5
Docker Hub currently lists actively maintained official Postgres image tags, including stable major tags and beta tags. Before copying a minor version into a long-lived setup, check the official image page and pick the version your application actually supports.
Why this matters:
- you avoid surprise major upgrades.
- your local, staging, and production environments match.
- upgrades become deliberate instead of accidental.
2. Put database files on a real volume
Never rely on the writable layer of the container for database data. If the container is removed, the data goes with it.
Use a Docker volume:
docker volume create pgdata
docker run -d \
--name postgres \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=change-me \
-e POSTGRES_DB=mydb \
-v pgdata:/var/lib/postgresql/data \
postgres:17
Or with Compose:
services:
postgres:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: change-me
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Bind mounts can work too, but Docker volumes are usually the simpler default. They are portable, easy to inspect with Docker tooling, and less likely to break because of host filesystem permissions.
3. Keep Postgres off the public internet
The easiest way to secure a Dockerized Postgres instance is to avoid exposing it publicly in the first place.
For local development, port publishing is fine:
services:
postgres:
image: postgres:17
ports:
- "5432:5432"
For production, prefer private networking between the app and the database:
services:
app:
image: my-app
environment:
DATABASE_URL: postgresql://myuser:change-me@postgres:5432/mydb
depends_on:
- postgres
postgres:
image: postgres:17
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: change-me
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
In that setup, the app reaches Postgres at postgres:5432 over the internal Docker network. There is no public database port unless you intentionally expose one.
Production checklist:
- use strong passwords or secret files.
- restrict network access.
- enable TLS if clients connect over untrusted networks.
- use separate database users for separate apps.
4. Treat backups as part of the container setup
A Postgres container is not a backup strategy. A Docker volume is not a backup strategy either.
At minimum, create logical backups with pg_dump:
docker exec -t postgres \
pg_dump -U myuser -d mydb -Fc \
> backup-$(date +%F).dump
Restore test that backup:
cat backup-2026-07-02.dump | docker exec -i postgres \
pg_restore -U myuser -d mydb --clean --if-exists
For production, you usually want more:
- scheduled backups.
- off-server backup storage.
- restore tests.
- alerts when backups fail.
- point-in-time recovery if losing a few hours of data would hurt.
5. Add health checks, metrics, and upgrade discipline
Postgres needs boring operational care. Docker makes it easier to start, but it does not remove the need to observe and maintain the database.
Add a health check:
services:
postgres:
image: postgres:17
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
interval: 30s
timeout: 5s
retries: 5
Enable useful Postgres visibility:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
Then watch the boring things:
- disk usage.
- memory pressure.
- long-running queries.
- connection count.
- backup freshness.
- replication lag if you run replicas.
For upgrades, do not just change postgres:16 to postgres:17 and hope. Read the Postgres release notes, take a backup, test the upgrade on a copy, and keep rollback time.
Final recommendation
Docker is great for Postgres development and can be fine for production if you actually operate it. The moment you do not want to own backups, restore testing, monitoring, upgrades, TLS, and private networking, use managed Postgres instead.
For a deeper beginner walkthrough, read how to run PostgreSQL in Docker. For backup mechanics, read backup and restore PostgreSQL via SSH tunnel.