5 Best Practices for Postgres in Docker in 2026

5 Best Practices for Postgres in Docker in 2026

Jonas Scholz - Co-Founder von sliplane.ioJonas Scholz
6 min

Postgres 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:

Terminal
docker run -d \
  --name postgres \
  -e POSTGRES_PASSWORD=change-me \
  postgres:17

For production-like environments, be more explicit:

compose.yml
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:

Terminal
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:

compose.yml
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:

compose.yml
services:
  postgres:
    image: postgres:17
    ports:
      - "5432:5432"

For production, prefer private networking between the app and the database:

compose.yml
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:

Terminal
docker exec -t postgres \
  pg_dump -U myuser -d mydb -Fc \
  > backup-$(date +%F).dump

Restore test that backup:

Terminal
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.
Want the easy version?

Managed Postgres on Sliplane includes automated point-in-time backups, SSL, metrics, logs, free egress, and 10 GB included storage.

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:

compose.yml
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.

Welcome to your cloud platform

Sliplane makes it simple to deploy and scale your apps in the cloud. Start with a container or your favorite framework and grow from there. Try it now and get started in minutes!