A production-ready PostgreSQL 18.1 Docker image with automatic TLS certificate generation, optimized for secure connections from Cloudflare Hyperdrive, PgBouncer, and other clients requiring SSL.
require, verify-ca, and verify-full SSL modesscram-sha-256 authentication, TLS 1.2+ only, SSL-only remote connectionspg_stat_statements for query monitoring# Run with auto-generated TLS certificates
docker run -d \
--name postgres-tls \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=mypassword \
-e POSTGRES_DB=mydb \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/18/main \
your-dockerhub-username/postgres-tls:18.1
| Variable | Required | Default | Description |
|---|---|---|---|
POSTGRES_USER | Yes | - | Database superuser name |
POSTGRES_PASSWORD | Yes | - | Database superuser password |
POSTGRES_DB | No | $POSTGRES_USER | Default database name |
| File | Path | Description |
|---|---|---|
| CA Certificate | /var/lib/postgresql/18/main/certs/ca.crt | Root CA for client verification |
| CA Private Key | /var/lib/postgresql/18/main/certs/ca.key | CA signing key (keep secure) |
| Server Certificate | /var/lib/postgresql/18/main/certs/server.crt | Server identity certificate |
| Server Private Key | /var/lib/postgresql/18/main/certs/server.key | Server private key |
Note: Certificates are stored inside the data directory so they persist with a single volume mount.
ssl = on
ssl_min_protocol_version = TLSv1.2
ssl_max_protocol_version = TLSv1.3
ssl_ciphers = HIGH:MEDIUM:+3DES:!aNULL
ssl_prefer_server_ciphers = on
To connect with verify-ca or verify-full mode, clients need the CA certificate:
# Extract CA certificate from running container
docker cp postgres-tls:/var/lib/postgresql/18/main/certs/ca.crt ./ca.crt
# Or from a volume
docker run --rm -v pgdata:/data alpine cat /data/certs/ca.crt > ca.crt
psql "postgresql://myuser:mypassword@localhost:5432/mydb?sslmode=require"
psql "host=localhost dbname=mydb user=myuser sslmode=verify-ca sslrootcert=./ca.crt"
psql "host=postgres-server dbname=mydb user=myuser sslmode=verify-full sslrootcert=./ca.crt"
Note:
verify-fullrequires the hostname to match the certificate CN or SAN. Default CN ispostgres-server.
import ssl
# Create SSL context with CA verification
ssl_context = ssl.create_default_context(cafile="/path/to/ca.crt")
ssl_context.check_hostname = False # verify-ca mode
ssl_context.verify_mode = ssl.CERT_REQUIRED
engine = create_async_engine(
"postgresql+asyncpg://user:pass@host:5432/db",
connect_args={"ssl": ssl_context}
)
const { Client } = require('pg');
const fs = require('fs');
const client = new Client({
host: 'localhost',
database: 'mydb',
user: 'myuser',
password: 'mypassword',
ssl: {
rejectUnauthorized: true,
ca: fs.readFileSync('/path/to/ca.crt').toString(),
}
});
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
)
caCert, _ := ioutil.ReadFile("/path/to/ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
config, _ := pgx.ParseConfig("postgres://user:pass@host:5432/db")
config.TLSConfig = &tls.Config{
RootCAs: caCertPool,
InsecureSkipVerify: false,
ServerName: "", // empty for verify-ca
}
# Extract CA certificate
docker cp postgres-tls:/var/lib/postgresql/certs/ca.crt ./ca.crt
# Upload to Cloudflare
npx wrangler cert upload certificate-authority \
--ca-cert ./ca.crt \
--name postgres-ca
npx wrangler hyperdrive create my-database \
--connection-string="postgres://user:pass@your-host:5432/dbname" \
--ca-certificate-id <CERT_ID_FROM_STEP_1> \
--sslmode verify-ca
export default {
async fetch(request: Request, env: Env) {
const client = new Client(env.HYPERDRIVE.connectionString);
await client.connect();
const result = await client.query('SELECT NOW()');
await client.end();
return Response.json(result.rows);
}
};
To use PgBouncer as a connection pooler with this PostgreSQL instance:
pgbouncer.ini:
[databases]
mydb = host=postgres-host port=5432 dbname=mydb
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
# Backend TLS to PostgreSQL
server_tls_sslmode = verify-ca
server_tls_ca_file = /etc/pgbouncer/ca.crt
For production, use a volume to persist data and certificates:
docker run -d \
--name postgres-tls \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=mypassword \
-v pgdata:/var/lib/postgresql/18/main \
your-dockerhub-username/postgres-tls:18.1
| Volume Mount | Contains |
|---|---|
/var/lib/postgresql/18/main | Database files + TLS certificates |
Important: Certificates are stored in
/var/lib/postgresql/18/main/certs/inside the data directory. If you delete the volume, new certificates will be generated and you'll need to update clients with the new CA certificate.
Default authentication rules:
| Type | Database | User | Address | Method |
|---|---|---|---|---|
| local | all | all | - | trust |
| hostssl | all | all | 0.0.0.0/0 | scram-sha-256 |
| hostssl | all | all | ::/0 | scram-sha-256 |
ca.crt - CA certificate (needed by clients to verify server)server.crt - Server certificate (sent during TLS handshake)ca.key - CA private key (can sign fake certificates)server.key - Server private key (can impersonate server)scram-sha-256Pre-configured optimizations:
| Setting | Value | Description |
|---|---|---|
shared_buffers | 512MB | Shared memory for caching |
work_mem | 32MB | Per-operation memory |
effective_cache_size | 1.5GB | Planner's cache assumption |
random_page_cost | 1.1 | Optimized for SSD |
effective_io_concurrency | 300 | Parallel I/O for SSD |
max_connections | 100 | Maximum concurrent connections |
pg_stat_statements - Query performance monitoring (pre-loaded)pg_trgm - Trigram text similarity (initialized)Ensure you're using the correct CA certificate extracted from the container.
# Verify certificate chain
openssl verify -CAfile ca.crt server.crt
Config files should be readable:
docker exec postgres-tls ls -la /etc/postgresql.conf
# Should show: -rw-r--r-- postgres postgres
docker exec postgres-tls psql -U myuser -d mydb -c "SHOW ssl;"
# Should return: on
openssl x509 -in ca.crt -text -noout
git clone <repository>
cd postgresql
docker build -f Dockerfile.tls -t postgres-tls:18.1 .
Based on the official PostgreSQL Docker image. PostgreSQL is released under the PostgreSQL License.
Content type
Image
Digest
sha256:fff04ae7e…
Size
155.5 MB
Last updated
3 months ago
docker pull andreidrang/postgreslite-tls:slave-18.4