PostgreSQL distribution with automatic version upgrades, password recovery, performance auto-tuning, and 16 pre-compiled extensions. Optimized for Kubernetes deployments.
- Automatic PostgreSQL upgrades - Handles upgrade paths from 14→15→16→17 using pg_upgrade with hard links
- Password recovery - Reset passwords without data loss using single-user mode in init containers
- PgTune auto-configuration - Calculates optimal settings based on container memory/CPU limits
- 16 pre-compiled extensions - pgvector, pgsodium, pgjwt, pgaudit, pg_cron, and more included
- Connection pooling - PgBouncer integrated with transaction pooling mode
- REST API - PostgREST generates RESTful APIs from database schema
- Backup integration - WAL-G configured for S3/GCS/Azure backups
- Helm charts - Kubernetes deployment with StatefulSets, probes, and PVCs
Version 2.x introduces a security improvement where the container now runs as the postgres user (UID 999) by default instead of root. This follows container security best practices and reduces the attack surface.
- Previous behavior: Container ran as root user
- New behavior: Container runs as
postgresuser (UID 999) by default - Impact: Existing volumes with incorrect ownership will cause permission errors
If you encounter permission errors after upgrading, fix volume ownership:
# Check current volume ownership
docker run --rm -v your-volume:/data alpine ls -la /data
# Fix permissions (if owned by root or other user)
docker run --rm --user root -v your-volume:/data alpine chown -R 999:999 /data
# Then start normally (will run as postgres user)
docker run -v your-volume:/var/lib/postgresql/data flanksource/postgres:17Recommended: Use named volumes (Docker handles permissions automatically):
docker run -d \
-v pgdata:/var/lib/postgresql/data \
-e PGPASSWORD=mypassword \
ghcr.io/flanksource/postgres:17Add securityContext to your Pod/StatefulSet spec:
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsUser: 999
runAsGroup: 999
fsGroup: 999 # Ensures PVC is owned by postgres
containers:
- name: postgres
image: ghcr.io/flanksource/postgres:17
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/dataIf you need to fix permissions on existing volumes, temporarily run as root:
# Run once as root to fix permissions
docker run --user root \
-v your-volume:/var/lib/postgresql/data \
ghcr.io/flanksource/postgres:17
# Container will detect wrong ownership, fix it, and exit
# Then restart without --user flag (runs as postgres by default)
docker run -v your-volume:/var/lib/postgresql/data \
ghcr.io/flanksource/postgres:17Use --dry-run to validate permissions before starting:
docker run --rm \
-v your-volume:/var/lib/postgresql/data \
--entrypoint postgres-cli \
ghcr.io/flanksource/postgres:17 \
auto-start --dry-run --data-dir /var/lib/postgresql/data- Security: Running as non-root reduces attack surface and follows least-privilege principle
- Compliance: Aligns with container security best practices and PodSecurityStandards
- Consistency: Matches official PostgreSQL Docker image behavior
# Add repository
helm repo add flanksource https://flanksource.github.io/charts
helm repo update
# Install
helm install my-postgres flanksource/postgres \
--set database.password=your-passworddocker run -d \
-e PGPASSWORD=mypassword \
-p 5432:5432 \
ghcr.io/flanksource/postgres:17# Install
go install github.com/flanksource/postgres/cmd@17
# Generate configuration
postgres-cli generate conf --memory=4GB --connections=200The postgres-cli tool provides intelligent PostgreSQL version upgrades with zero data loss through a multi-phase orchestration process:
- Version Detection: Reads
/var/lib/postgresql/data/PG_VERSIONto identify current version - Upgrade Path Planning: Determines sequential upgrade steps (e.g., 14→15→16→17)
- Validation: Ensures data directory exists and PostgreSQL is stopped
Current Data (v14) → Backup → Sequential Upgrades → Final Version (v17)
↓
/backups/data-14 (preserved)
The Postgres.Upgrade() method in pkg/server/postgres.go orchestrates:
-
Pre-upgrade Backup (
backupDataDirectory):- Creates timestamped backup in
/var/lib/postgresql/data/backups/data-{version} - Preserves original data for rollback capability
- Excludes recursive backup/upgrade directories
- Creates timestamped backup in
-
Sequential Version Upgrades (
upgradeSingle):- For each version hop (14→15, 15→16, 16→17):
- Validates current cluster with
pg_controldata - Initializes new cluster in
/var/lib/postgresql/data/upgrades/{version} - Runs
pg_upgrade --checkfor compatibility verification - Executes
pg_upgradewith hard links (no data duplication) - Validates upgraded cluster state
- Moves upgraded data to main location
- Validates current cluster with
- For each version hop (14→15, 15→16, 16→17):
-
Data Migration (
moveUpgradedData):- Safely removes old version files from main directory
- Moves upgraded data from staging to production location
- Preserves backup and upgrade directories
- Backup Preservation: Original data always preserved in
/backups/data-{version} - Validation Checks: Pre and post-upgrade cluster validation using
pg_controldata - Atomic Operations: Each version upgrade is atomic with rollback capability
- Detailed Logging: Captures stdout/stderr for debugging failed upgrades
The Docker container orchestrates upgrades through a layered approach:
-
Entry Point (
docker-entrypoint.sh):# Auto-detection mode when no arguments provided if [ $# -eq 0 ]; then # Delegates to Task runner for orchestration exec gosu postgres task auto-upgrade fi
-
Task Runner (
Taskfile.run.yaml:auto-upgrade):# Detects current version from PG_VERSION CURRENT_VERSION=$(cat /var/lib/postgresql/data/PG_VERSION) TARGET_VERSION="${PG_VERSION:-17}" # Calls postgres-cli for actual upgrade exec postgres-cli upgrade --target-version $TARGET_VERSION
-
CLI Command (
cmd/server.go:createUpgradeCommand):- Parses target version from flags
- Creates
Postgresinstance with data directory - Calls
Postgres.Upgrade(targetVersion)for orchestration
-
Permission Handling:
- Container starts as root for flexibility
- Uses
gosu postgresto switch to postgres user - Ensures proper file ownership throughout upgrade
| Image | Description |
|---|---|
ghcr.io/flanksource/postgres:17 |
PostgreSQL 17 with the ability to upgrade from 14 |
ghcr.io/flanksource/postgres:16 |
PostgreSQL 16 with the ability to upgrade from 14 |
The container detects resource limits and configures PostgreSQL accordingly:
| Parameter | Calculation | Example (8GB RAM) |
|---|---|---|
shared_buffers |
25% of RAM | 2GB |
effective_cache_size |
75% of RAM | 6GB |
work_mem |
RAM Ă— 0.5% Ă· max_connections | 40MB |
maintenance_work_mem |
6.25% of RAM | 512MB |
wal_buffers |
3% of shared_buffers | 64MB |
max_wal_size |
2GB default | 2GB |
# Kubernetes
resources:
limits:
memory: 8Gi # Triggers auto-configuration
cpu: 4
# Docker
docker run --memory="8g" --cpus="4" ...database:
config:
shared_buffers: "4GB" # Override calculated value
max_connections: "200" # Set explicitlyPassword recovery without data loss:
helm upgrade my-postgres flanksource/postgres \
--set database.resetPassword=true \
--set database.password=new-password \
--reuse-valuesdocker run --rm \
-v postgres_data:/var/lib/postgresql/data \
-e RESET_PASSWORD=true \
-e PGPASSWORD=new-password \
ghcr.io/flanksource/postgres:17-17- Starts PostgreSQL in single-user mode
- Updates password in pg_authid
- Restarts in normal multi-user mode
- No downtime for existing connections
database:
version: "17"
password: "change-me"
autoUpgrade: true
resetPassword: false
resources:
limits:
cpu: 4
memory: 8Gi
requests:
cpu: 2
memory: 4Gi
persistence:
size: 100Gi
storageClass: fast-ssd
extensions:
enabled: "pgvector,pgsodium,pgaudit,pg_cron"
pgbouncer:
enabled: true
poolMode: transaction
maxClientConn: 1000
postgrest:
enabled: true
schemas: public
walg:
enabled: true
s3:
bucket: postgres-backups
region: us-east-1helm install my-postgres flanksource/postgres -f values.yamlhelm upgrade my-postgres flanksource/postgres \
--set database.version=17 \
--reuse-values
kubectl rollout restart statefulset/my-postgresThe postgres-cli orchestrates safe, sequential PostgreSQL upgrades with full data preservation:
-
Pre-Upgrade Validation (
validateCluster):- Verifies PG_VERSION file matches expected version
- Runs
pg_controldatato check cluster state - Ensures data directory permissions are correct
-
New Cluster Initialization (
initNewCluster):- Creates upgrade workspace:
/data/upgrades/{version} - Runs
initdbfor target PostgreSQL version - Configures new cluster with same settings
- Creates upgrade workspace:
-
pg_upgrade Execution (
runPgUpgrade):# Compatibility check first pg_upgrade --check \ --old-bindir=/usr/lib/postgresql/14/bin \ --new-bindir=/usr/lib/postgresql/15/bin \ --old-datadir=/var/lib/postgresql/data \ --new-datadir=/data/upgrades/15 # Actual upgrade with hard links (no data duplication) pg_upgrade \ --old-bindir=/usr/lib/postgresql/14/bin \ --new-bindir=/usr/lib/postgresql/15/bin \ --old-datadir=/var/lib/postgresql/data \ --new-datadir=/data/upgrades/15
-
Data Migration (
moveUpgradedData):- Removes old version files from main directory
- Moves upgraded data from
/data/upgrades/{version}to main location - Preserves backup directories for rollback capability
| Variable | Default | Description |
|---|---|---|
AUTO_UPGRADE |
true |
Enable automatic version detection and upgrade |
PG_VERSION |
17 |
Target PostgreSQL version for upgrades |
START_POSTGRES |
false |
Start PostgreSQL after successful upgrade |
If an upgrade fails:
- Original data remains in
/data/backups/data-{version} - Each upgrade step logs detailed output for debugging
- Manual recovery:
cp -r /data/backups/data-{version}/* /var/lib/postgresql/data/
| Variable | Description | Default | Example |
|---|---|---|---|
PG_VERSION |
Target PostgreSQL version (14-17) | 17 |
16 |
PGDATA |
PostgreSQL data directory | /var/lib/postgresql/data |
Custom path |
POSTGRES_DB |
Default database name | postgres |
myapp |
POSTGRES_USER |
Database superuser username | postgres |
admin |
PGPASSWORD |
Database password (direct) | - | mySecurePassword |
PGPASSWORD_FILE |
Path to file containing password | - | /run/secrets/db_password |
| Variable | Description | Default | Values |
|---|---|---|---|
PG_TUNE |
Alias for PGCONFIG_AUTO_TUNE | true |
true, false |
POSTGRES_CLI_ARGS |
Custom postgres-cli arguments | See below | --dry-run --pg-tune |
UPGRADE_ONLY |
Exit after upgrade (no start) | false |
true, false |
| Variable | Description | Default | Example |
|---|---|---|---|
PG_TUNE_MAX_CONNECTIONS |
Override max connections | Auto-calculated | 200 |
PG_TUNE_MEMORY |
Override memory in MB | Auto-detected | 8192 |
PG_TUNE_CPUS |
Override CPU count | Auto-detected | 4 |
PG_AUTH_METHOD |
Authentication method | scram-sha-256 |
md5, trust |
The postgres-cli tool provides comprehensive PostgreSQL management. It's included in the Docker image and can be installed standalone.
# Install CLI tool
go install github.com/flanksource/postgres/cmd@17
# Or use Docker image
docker run --rm ghcr.io/flanksource/postgres:17 postgres-cli --helpAvailable for all commands:
| Flag | Short | Description | Default | Example |
|---|---|---|---|---|
--username |
-U |
PostgreSQL username | postgres or $PG_USER |
-U admin |
--password |
-W |
PostgreSQL password | $PGPASSWORD or $PGPASSWORD_FILE |
-W mypass |
--database |
-d |
Database name | postgres or $PG_DATABASE |
-d myapp |
--host |
PostgreSQL host | localhost or $PG_HOST |
--host db.example.com |
|
--port |
-p |
PostgreSQL port | 5432 or $PG_PORT |
-p 5433 |
--data-dir |
Data directory path | $PGDATA or auto-detected |
--data-dir /pgdata |
|
--bin-dir |
PostgreSQL binary directory | Auto-detected | --bin-dir /usr/lib/postgresql/17/bin |
|
--config |
-c |
Configuration file path | - | -c /etc/postgresql.conf |
--locale |
Database locale | C |
--locale en_US.UTF-8 |
|
--encoding |
Database encoding | UTF8 |
--encoding UTF8 |
|
--dry-run |
Simulate without changes | false |
--dry-run |
Automatically start PostgreSQL with optional pre-start tasks.
postgres-cli auto-start [flags]Flags:
| Flag | Description | Default |
|---|---|---|
--auto-init |
Initialize database if not exists | true |
--auto-upgrade |
Upgrade PostgreSQL if version mismatch | true |
--auto-reset-password |
Reset password on startup | true |
--pg-tune |
Run pg_tune optimization | true |
--upgrade-to <N> |
Target PostgreSQL version | 0 (auto-detect 17) |
--max-connections |
Max connections for pg_tune | 0 (auto-calculate) |
--memory |
Override memory in MB | 0 (auto-detect) |
--cpus |
Override CPU count | 0 (auto-detect) |
--type |
Database type for pg_tune | web |
--auth-method |
pg_hba.conf auth method | scram-sha-256 |
Examples:
# Start with all auto features
postgres-cli auto-start
# Initialize new database and start
postgres-cli auto-start --auto-init
# Upgrade to version 17 without starting
postgres-cli auto-start --upgrade-to=17 --dry-run
# Start with custom tuning
postgres-cli auto-start --pg-tune --max-connections=200 --memory=8192Manage PostgreSQL server instances:
postgres-cli server <subcommand> [flags]Subcommands:
| Command | Description |
|---|---|
status |
Show comprehensive PostgreSQL status |
health |
Perform health check |
start |
Start PostgreSQL server |
stop |
Stop PostgreSQL server gracefully |
restart |
Restart PostgreSQL server |
initdb |
Initialize PostgreSQL data directory |
reset-password |
Reset PostgreSQL superuser password |
upgrade |
Upgrade PostgreSQL to target version |
backup |
Create PostgreSQL backup using pg_dump |
sql |
Execute SQL query |
Examples:
# Check PostgreSQL status
postgres-cli server status
# Initialize new cluster
postgres-cli server initdb --data-dir=/pgdata
# Reset password
postgres-cli server reset-password --password=newpass
# Upgrade to version 17
postgres-cli server upgrade --target-version=17
# Execute SQL query
postgres-cli server sql --query="SELECT version();"
# Execute SQL from file
postgres-cli server sql --file=/path/to/script.sqlShow version information:
postgres-cli versionThe docker-entrypoint.sh automatically calls postgres-cli auto-start:
# Default behavior (all features enabled)
docker run ghcr.io/flanksource/postgres:17
# Custom postgres-cli arguments
docker run -e POSTGRES_CLI_ARGS="--pg-tune --dry-run" \
ghcr.io/flanksource/postgres:17
# Upgrade only (no start)
docker run -e UPGRADE_ONLY=true \
-v pgdata:/var/lib/postgresql/data \
ghcr.io/flanksource/postgres:17# Check status of local PostgreSQL
postgres-cli server status --data-dir=/var/lib/postgresql/data
# Upgrade local PostgreSQL installation
postgres-cli server upgrade \
--target-version=17 \
--data-dir=/var/lib/postgresql/data
# Generate optimized configuration
postgres-cli auto-start \
--pg-tune \
--memory=8192 \
--max-connections=200 \
--dry-run
# Connect to remote PostgreSQL
postgres-cli server sql \
--host=db.example.com \
--port=5432 \
--username=admin \
--query="SELECT count(*) FROM users;"# Reset password using environment variable
export PGPASSWORD=oldpass
export POSTGRES_PASSWORD=newpass
postgres-cli server reset-password
# Reset password using file
echo "newpassword" > /tmp/password
postgres-cli server reset-password \
--password=$(cat /tmp/password)
rm /tmp/password
# Reset via auto-start
postgres-cli auto-start --auto-reset-password- Contributing Guide - Development setup and guidelines
- Helm Chart Documentation - Chart configuration details
- Extension Examples - Extension usage
- Migration Guide - Migrating from other distributions
- GitHub Issues
- GitHub Discussions
- Security: [email protected]
Apache License 2.0. See LICENSE for details.