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 .sql or .sh files in this directory run automatically on first container start (empty database only).
  • healthcheck — Uses pg_isready to verify the database is accepting connections. Other services can use depends_on: condition: service_healthy.
  • profiles: [tools] — pgAdmin only starts when you explicitly run docker compose --profile tools up. Keeps the default stack lean.
  • Command flags — PostgreSQL tuning parameters passed directly. shared_buffers should be ~25% of available RAM for the container.

Common Modifications

  • Custom postgresql.conf: Mount a full config file with - ./postgresql.conf:/etc/postgresql/postgresql.conf and add -c config_file=/etc/postgresql/postgresql.conf to the command.
  • Backups: Add a backup service using prodrigestivill/postgres-backup-local image to schedule automated pg_dump backups.
  • Multiple databases: Add more POSTGRES_DB entries 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.limits with memory: 1G and cpus: '1.0' to prevent runaway resource usage.