Deploying PostgreSQL
Self-hosted Postgres with Docker, tuning, backups, and high availability.
PlatformsAdvanced
Why
Self-hosting Postgres gives full control over config, extensions, and tuning. With Docker + pgBackRest + repmgr, you get HA, PITR, and extension support not always in managed offerings.
Platform-as-a-Service adoption grew 24% in 2024 as teams shift from VM management to git-push deploys. the cloud infrastructure market reached $330B in 2024, growing 22% year-over-year (Synergy Research), and PaaS layers capture the majority of new workloads.
When
Use self-hosted Postgres for custom extensions, cost optimization at scale, and compliance. Prefer managed (RDS, Supabase) for simplicity.
How
1. Docker — postgres:16 image with volume for data
2. Config — postgresql.conf tuning (shared_buffers, work_mem)
3. Init — init.sql in docker-entrypoint-initdb.d
4. Backup — pg_dump or pgBackRest for incremental
5. HA — repmgr or Patroni for replication + failover
6. Monitoring — pg_stat_statements, pg_stat_activity
7. Extensions — CREATE EXTENSION for pgvector, postgis
Example
# docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
command: >
postgres
-c shared_buffers=256MB
-c work_mem=16MB
-c max_connections=200
volumes:
pgdata:
# Backup
docker exec mydb pg_dump -U postgres mydb | gzip > backup.sql.gz
# Restore
gunzip < backup.sql.gz | docker exec -i mydb psql -U postgres mydb
Common Problems
- ⚠shared_buffers too low — default 128MB, set to 25% of RAM for production
- ⚠No backups — pg_dump is not enough, use pgBackRest for PITR
- ⚠Configuration drift between environments — use infrastructure-as-code (Terraform/Pulumi) to keep dev/staging/prod identical
Best Practices
- ✓Set shared_buffers to 25% of RAM — biggest single performance win
- ✓Use pgBackRest for backups — incremental, compressed, PITR
- ✓Set up health checks and graceful shutdown — handle SIGTERM to drain in-flight requests before exit