Skip to content
Community

EXPLORE TOPICS

Editorial approach

New to CoreTrailKit?

Observe your services. Keep the evidence close.

CoreTrailKit
Back to articles
GUIDE/ Docker

Postgres in Docker: rotate an existing password

You changed POSTGRES_PASSWORD and recreated the database container, but the application rejects the new password. The old one still works. If the data volume already contains a database, that behavior can be expected.

By CoreTrailKit4 min read

01

Separate the container environment from database state

POSTGRES_USER, POSTGRES_DB, and POSTGRES_PASSWORD supply initial setup values for the official image. They do not rewrite users and passwords in a database that is already initialized. A persistent volume lets those database records survive container recreation.

Look for an initialized data directory and startup logs showing that initialization was skipped. Seeing a new password in container configuration only proves which environment value was supplied; it does not prove the stored role password changed.

02

Create the example with an explicit persistent volume

Use a new project directory and unique Compose project name if password-proof already exists. Save this Compose file there. Start it with DEMO_DB_PASSWORD=old-demo-only docker compose up --detach, then wait until PostgreSQL is accepting connections.

The port is not published to the host. A separate client container on the project network will be used for authentication checks. This distinguishes an actual password check from a local socket session that may be trusted by the image configuration.

yaml · EXAMPLE
name: password-proof
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: demo
      POSTGRES_DB: demo
      POSTGRES_PASSWORD: ${DEMO_DB_PASSWORD:?Set DEMO_DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata: {}

03

Keep a marker and recreate only the container

First use the local administrative session in this isolated example to create a marker table. Then recreate db with a different initialization password while retaining pgdata. Do not run down --volumes: that would delete the evidence and the database rather than rotate a credential.

Use these commands only in the disposable example database. CREATE TABLE is intentional example data, not a diagnostic to run in an unrelated production schema.

sh · EXAMPLE
DEMO_DB_PASSWORD=old-demo-only docker compose exec db   psql -U demo -d demo -c   "CREATE TABLE marker(value text); INSERT INTO marker VALUES ('retained');"
DEMO_DB_PASSWORD=new-demo-only docker compose up --detach --force-recreate db

04

Test authentication from another container

Wait until the recreated database is ready, then connect from a separate client on password-proof_default. The new-demo-only password should be rejected while old-demo-only can still read retained. A connection to a Unix socket inside db may not ask for a password and therefore is not a credential-rotation test.

Our lab observed that exact result with PostgreSQL 16: the new environment password failed authentication, the old role password still worked, and the marker row survived. The stored data was unchanged by container recreation.

sh · EXAMPLE
# Expected authentication failure in this disposable example.
docker run --rm --network password-proof_default   -e PGPASSWORD=new-demo-only postgres:16   psql -h db -U demo -d demo -Atc 'SELECT value FROM marker'
# Expected output after the service is ready: retained.
docker run --rm --network password-proof_default   -e PGPASSWORD=old-demo-only postgres:16   psql -h db -U demo -d demo -Atc 'SELECT value FROM marker'

05

Rotate the role through an authorized session

In this isolated image setup, open psql as the demo administrator and use the interactive password command below. It prompts for the new value instead of putting a real password in shell history or a SQL command argument. On a real deployment, use an existing authorized administrative path; do not assume local trust is available or should be enabled.

After changing the role password, update the application secret and recreate clients that receive credentials through their container environment. Test a new network connection with the rotated credential and confirm an existing row remains. Review application availability and pooled connections as part of the rotation.

text · EXAMPLE
# Shell: enter the local example's authorized administrative session.
DEMO_DB_PASSWORD=new-demo-only docker compose exec db psql -U demo -d demo
# psql prompt: follow the password prompts, then leave psql.
\password demo
\q
# Back in the shell: verify from a new network client; -W prompts.
docker run --rm -it --network password-proof_default postgres:16 psql -h db -U demo -d demo -W -c 'SELECT value FROM marker'

06

Treat init scripts and upgrades as separate work

Files in /docker-entrypoint-initdb.d also belong to first initialization. Adding an init script to an image is not a migration plan for a volume that already contains data. Use an explicit, reviewed migration process for an existing database.

If an authorized password change is not possible, follow your access-recovery procedure and backup policy. Deleting the volume to make a new password work is data replacement, not recovery. In our corrected test the rotated role authenticated and the same marker row was still present.

Know the limits

The controlled example uses the Docker Official Image for PostgreSQL 16 with /var/lib/postgresql/data. PostgreSQL 18+ image storage defaults differ; inspect the exact image documentation before adapting mount paths. Example passwords below are disposable demonstration values, not production credentials. Reproduction checked on 26 October 2026 using Linux containers on Docker Engine 29.6.2 and Compose 5.3.1; this is the validation environment, not a minimum-version claim.

Sources & further reading

A technical publication by CoreTrailKit.

Browse all articles

CONTINUE EXPLORING