docker
Docker Compose with PostgreSQL
Docker Compose config for PostgreSQL with persistent volumes, health checks, initialization scripts, and pgAdmin.
Overview
A Docker Compose setup for running PostgreSQL in local development or staging environments. Includes persistent data storage, health checks, custom initialization, and an optional pgAdmin web interface for database management.
Configuration
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine # Lightweight Alpine-based image
container_name: app-postgres
restart: unless-stopped # Auto-restart on failure
environment:
POSTGRES_USER: appuser # Superuser username
POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme} # Use env var or default
POSTGRES_DB: appdb # Default database to create
PGDATA: /var/lib/postgresql/data/pgdata # Custom data directory
ports:
- "5432:5432" # Expose on host for local tools
volumes:
- postgres_data:/var/lib/postgresql/data # Persistent storage
- ./init-scripts:/docker-entrypoint-initdb.d # Run SQL on first start
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 10s # Check every 10 seconds
timeout: 5s # Fail if check takes > 5s
retries: 5 # Mark unhealthy after 5 failures
start_period: 30s # Grace period on startup
# Performance tuning via command flags
command: >
postgres
-c shared_buffers=256MB
-c effective_cache_size=768MB
-c work_mem=16MB
-c maintenance_work_mem=128MB
-c max_connections=100
-c log_statement=all
pgadmin:
image: dpage/pgadmin4:latest
container_name: app-pgadmin
restart: unless-stopped
profiles:
- tools # Only starts with --profile tools
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD:-admin}
PGADMIN_LISTEN_PORT: 5050
ports:
- "5050:5050" # pgAdmin web interface
volumes:
- pgadmin_data:/var/lib/pgadmin # Persist pgAdmin settings
depends_on:
postgres:
condition: service_healthy # Wait for Postgres to be ready
volumes:
postgres_data: # Named volume for database files
driver: local
pgadmin_data:
driver: local
Key Options Explained
postgres:16-alpine— Alpine variant is significantly smaller (~80MB vs ~400MB) with the same functionality.PGDATA— Custom data directory avoids conflicts with the volume mount point and ensures clean initialization.docker-entrypoint-initdb.d— Any.sqlor.shfiles in this directory run automatically on first container start (empty database only).healthcheck— Usespg_isreadyto verify the database is accepting connections. Other services can usedepends_on: condition: service_healthy.profiles: [tools]— pgAdmin only starts when you explicitly rundocker compose --profile tools up. Keeps the default stack lean.- Command flags — PostgreSQL tuning parameters passed directly.
shared_buffersshould be ~25% of available RAM for the container.
Common Modifications
- Custom
postgresql.conf: Mount a full config file with- ./postgresql.conf:/etc/postgresql/postgresql.confand add-c config_file=/etc/postgresql/postgresql.confto the command. - Backups: Add a backup service using
prodrigestivill/postgres-backup-localimage to schedule automated pg_dump backups. - Multiple databases: Add more
POSTGRES_DBentries via init scripts since the env var only creates one database. - Network isolation: Add a custom network to prevent the database from being accessible to unrelated containers.
- Resource limits: Add
deploy.resources.limitswithmemory: 1Gandcpus: '1.0'to prevent runaway resource usage.