PostgreSQL
Docker
superuser
container security
database setup

Specifying superuser PostgreSQL password for a Docker Container

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

The official PostgreSQL Docker image expects you to set the initial superuser password at container startup. If you do not configure it correctly, the container either refuses to start or comes up with an authentication setup that is unsafe for anything beyond temporary local testing.

The Standard Way: POSTGRES_PASSWORD

For the official image, the usual method is the POSTGRES_PASSWORD environment variable. It sets the password for the default postgres superuser during first-time database initialization.

bash
1docker run --name my-postgres \
2  -e POSTGRES_PASSWORD=mysecretpassword \
3  -p 5432:5432 \
4  -d postgres:16

This works only the first time the data directory is initialized. If the container already has a persistent volume with an existing cluster, changing the environment variable later does not automatically reset the database password.

Setting The Database Name And User At The Same Time

You can also initialize a custom database and application user alongside the superuser password.

bash
1docker run --name app-db \
2  -e POSTGRES_DB=appdb \
3  -e POSTGRES_USER=appuser \
4  -e POSTGRES_PASSWORD=appsecret \
5  -p 5432:5432 \
6  -d postgres:16

In this case, POSTGRES_USER becomes the initial superuser-like account created by the image startup logic, and POSTGRES_PASSWORD sets its password. The default postgres role may still exist, but your application can connect as appuser.

Docker Compose Example

For local development, Docker Compose keeps the configuration readable.

yaml
1services:
2  db:
3    image: postgres:16
4    container_name: app-db
5    environment:
6      POSTGRES_DB: appdb
7      POSTGRES_USER: appuser
8      POSTGRES_PASSWORD: appsecret
9    ports:
10      - "5432:5432"
11    volumes:
12      - pgdata:/var/lib/postgresql/data
13
14volumes:
15  pgdata:

Start it with:

bash
docker compose up -d

Once the container is ready, verify the login:

bash
docker exec -it app-db psql -U appuser -d appdb -c 'SELECT current_user;'

Safer Secret Handling

Hard-coding passwords directly in shell history or compose files is acceptable only for disposable local environments. For anything more serious, use a secret file.

The PostgreSQL image supports POSTGRES_PASSWORD_FILE, which points to a file inside the container.

yaml
1services:
2  db:
3    image: postgres:16
4    environment:
5      POSTGRES_USER: appuser
6      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
7    secrets:
8      - postgres_password
9
10secrets:
11  postgres_password:
12    file: ./secrets/postgres_password.txt

This avoids exposing the password in plain environment output. The container entrypoint reads the file and uses its contents during initialization.

Changing The Password After Initialization

If the container already created the database cluster, update the password from inside PostgreSQL rather than by editing the container definition.

bash
docker exec -it my-postgres psql -U postgres -c "ALTER USER postgres WITH PASSWORD 'newsecret';"

This is an important operational detail. Many people restart a container with a new POSTGRES_PASSWORD value and expect it to apply automatically. It does not if the data directory already exists.

Initialization Scripts

If you need additional setup, place SQL or shell scripts in /docker-entrypoint-initdb.d/. They are executed only on first initialization.

sql
CREATE DATABASE reporting;
CREATE USER report_user WITH PASSWORD 'reportsecret';
GRANT ALL PRIVILEGES ON DATABASE reporting TO report_user;

That is useful when you want repeatable local environments without manually logging in after startup.

Common Pitfalls

The biggest mistake is forgetting that initialization variables apply only when PostgreSQL creates a new data directory. With a reused volume, environment changes do not retroactively update roles or passwords.

Another issue is storing secrets directly in source control. A compose file with a real production password is a preventable security failure. Use secret files, environment injection from a secure system, or an orchestrator-level secret mechanism.

It is also easy to confuse POSTGRES_USER with ordinary application-role creation later in a migration. The startup variables define the initial cluster state. They are not a substitute for proper database migration scripts.

Finally, avoid disabling authentication checks just to get a container running faster. If a password is required, set one explicitly and verify it with a real login test.

Summary

  • Use POSTGRES_PASSWORD to set the initial PostgreSQL superuser password in the official Docker image.
  • The environment variables only affect first-time initialization of the data directory.
  • Use Docker Compose for readability and POSTGRES_PASSWORD_FILE for safer secret handling.
  • Change passwords later with ALTER USER, not by expecting container variables to rewrite an existing cluster.
  • Treat database credentials as secrets, even in containerized development setups.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.