Skip to main content
The following guide deploys the Minimus Postgres image together with custom certificates generated with OpenSSL to help you get started. Run the code to try it for yourself.

Components

  • Postgres image: Postgres container configured with the secure configuration for client authentication.
  • certgen.sh script: Generates a custom CA, server, and client certificates using OpenSSL.
  • minidebug image: A Minimus dev toolkit that provides a shell, OpenSSL, and other utilities used to generate the certificates.
  • psql installed

What this guide demonstrates

  • TLS handshake validation
  • Server/client certificate trust
  • Basic auth and Postgres operations
  • Image compatibility

Directory Structure

.
├── certgen.sh             # Certificate generation script
├── create-certs.yml       # Compose file to run certgen container
├── entrypoint.sh          # Custom PostgreSQL entrypoint to set permissions
├── pg_hba.conf            # Custom pg_hba config to require SSL and client certs
└── docker-compose.yml     # Compose file to run PostgresDB

Deploy Postgres with TLS certificates

1

Save certgen.sh

Save the following script to a file named certgen.sh. The script is used to generate the TLS certificates and store them in a certs folder on the host.
certgen.sh
#!/bin/sh
# Company: Minimus
# Author: Alexander Haytovich

set -e
cd /certs

echo "[INFO] Creating OpenSSL config for server..."
cat > openssl.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_server
prompt = no

[req_distinguished_name]
CN = postgres

[v3_server]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = postgres
DNS.2 = localhost
IP.1 = 127.0.0.1
IP.2 = 192.168.30.0
IP.3 = 192.168.30.2
IP.4 = 192.168.30.3



[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, keyCertSign, cRLSign
EOF

echo "[INFO] Creating OpenSSL config for client..."
cat > client_openssl.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_client
prompt = no

[req_distinguished_name]
CN = testuser

[v3_client]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF

echo "[INFO] Generating CA certificate..."
openssl genrsa -out ca-key.pem 2048
openssl req -x509 -new -nodes -key ca-key.pem \
  -sha256 -days 365 -out ca.pem \
  -subj "/CN=Test CA" \
  -extensions v3_ca -config openssl.cnf

echo "[INFO] Generating server key and CSR..."
openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out server.csr -config openssl.cnf -extensions v3_server

echo "[INFO] Signing server certificate..."
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial \
  -out server-cert.pem -days 365 -sha256 \
  -extfile openssl.cnf -extensions v3_server

echo "[INFO] Generating client certificate..."
openssl genrsa -out client-key.pem 2048
openssl req -new -key client-key.pem -out client.csr \
  -config client_openssl.cnf -extensions v3_client
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial \
  -out client-cert.pem -days 365 -sha256 \
  -extfile client_openssl.cnf -extensions v3_client

# Ensure private key is root-owned and protected (for PostgreSQL)
echo "[INFO] Fixing permissions for client-key.pem..."

#python client not running as root, changing client permissions to user 1000 that is running the python client
chown 1000:1000 /certs/client-key.pem
chmod 600 /certs/client-key.pem


echo "[SUCCESS] Certificates created:"
2

Save create-certs.yml

Save the following YAML file to run with Docker Compose. It uses the Minimus minidebug image to generate the certificates with the certgen.shshell script. Minidebug is a Minimus dev toolkit that provides a shell, OpenSSL, and other utilities.The certificates will be persisted in the certs volume on the host.
create-certs.yml
services:
  create_certs:
    image: reg.mini.dev/minidebug:latest
    container_name: create_certs
    volumes:
    - ./certs:/certs
    - ./certgen.sh:/certgen.sh:ro
    entrypoint:
    - /bin/sh
    - /certgen.sh
    network_mode: none
3

Generate certificates

Run the following to generate the certificates:
docker compose -f create-certs.yml up

TLS certificates

The script certgen.sh generates the following self-signed certificates:
  • Self-signed CA certificate (ca.pem)
  • Server certificates (server-cert.pem, server-key.pem) with SANs: postgres, localhost, 127.0.0.1, and 192.168.30.3
  • Client certificates for testuser(client.pem, client-key.pem)
Client private key permissions are set to 0600 and owned by UID 1000 . Certificate permissions are adjusted to support non-root containers. The certificates will be mounted into the Postgres container.
4

Save custom entrypoint

entrypoint.sh
#!/bin/bash
set -e
# Fix ownership so postgres can use the private key
echo "[INFO] Fixing ownership and permissions of /certs..."
chown postgres:postgres /certs/server-key.pem
echo "[INFO] Fixing file permissions..."
# Restrict private key access
chmod 600 /certs/server-key.pem

echo "[INFO] Launching PostgreSQL..."
exec docker-entrypoint.sh "$@"
Make sure the entrypoint script is executable on your host. If necessary, give it execute permissions:
chmod +x ./entrypoint.sh
5

Save custom HBA

PostgreSQL’s default access rules are defined in the file pg_hba.conf. Save the following configuration to mount it and customize the Host-Based Authentication rules.
pg_hba.conf
# Allow local socket connections without SSL (for internal psql commands)
local   all             all                                     trust

# Allow readonly_user to connect using password over SSL
hostssl all readonly_user 0.0.0.0/0 scram-sha-256

# Allow remote SSL connections with client cert validation
hostssl all             all             0.0.0.0/0               cert clientcert=verify-full
6

Save Docker Compose script

Save the following Docker Compose script to a file nameddocker-compose.yml. This script sets up the Postgres service with a healthcheck, mounts a volume with the certificates, the custom entrypoint and the custom HBA config, and maps port 5432.
docker-compose.yml
services:
  postgres:
    image: reg.mini.dev/postgres
    container_name: pg_ssl
    environment:
      POSTGRES_USER: testuser
      POSTGRES_PASSWORD: testpass
      POSTGRES_DB: testdb
    volumes:
    - pgdata:/var/lib/postgresql/data
    - ./certs:/certs
    - ./entrypoint.sh:/entrypoint.sh:ro
    - ./pg_hba.conf:/etc/postgresql/pg_hba.conf
    ports:
    - 5432:5432
    healthcheck:
      test:
      - CMD
      - pg_isready
      - -U
      - testuser
      - -d
      - testdb
      interval: 5s
      retries: 10
    entrypoint:
    - /entrypoint.sh
    command: "postgres\n  -c ssl=on\n  -c ssl_cert_file=/certs/server-cert.pem\n \
      \ -c ssl_key_file=/certs/server-key.pem\n  -c ssl_ca_file=/certs/ca.pem\n  -c\
      \ hba_file=/etc/postgresql/pg_hba.conf\n"
volumes:
  pgdata: null
7

Run Postgres

Start the Postgres container:
docker compose -f docker-compose.yml up
8

Run tests over psql

Use psql to connect over TLS and run tests. For example, here are a few commands you can try out:
  1. Connect to the db:
    psql "host=127.0.0.1 \
    port=5432 \
    dbname=testdb \
    user=testuser \
    sslmode=verify-ca \
    sslrootcert=./certs/ca.pem \
    sslcert=./certs/client-cert.pem \
    sslkey=./certs/client-key.pem"
    
  2. Show all schemas in current database:
    \dn
    
  3. Show server version:
    SELECT version();
    
  4. Check that TLS is active:
    SHOW ssl;
    SHOW ssl_cert_file;
    SHOW ssl_key_file;