Skip to Content

Compose Secrets

Every compose.yaml in this section has had POSTGRES_PASSWORD=secret written in plain text. Fine for practice — risky the moment this file is shared or committed to Git. This page fixes that.

The Problem

Anything under environment: in a Compose file is visible to anyone who can read that file, and to anyone who runs docker inspect on the running container. If compose.yaml ever gets committed to a public repository with a real password inside it, that password is now public.

Compose Secrets

A secret in Compose is a value stored in its own file, not written directly in compose.yaml. Docker mounts that file into the container. Your app reads the value from the file, instead of from a plain environment variable. docker inspect does not show it directly.

Create the secret file

Terminal
mkdir secrets echo "secret" > secrets/db_password.txt

Use a real, strong password here in an actual project — secret is only used to match the earlier examples.

Reference it in compose.yaml

compose.yaml
services: db: image: postgres:16-alpine environment: - POSTGRES_USER=appuser - POSTGRES_PASSWORD_FILE=/run/secrets/db_password - POSTGRES_DB=appdb secrets: - db_password volumes: - db-data:/var/lib/postgresql/data volumes: db-data: secrets: db_password: file: ./secrets/db_password.txt

Never commit the secret file

.gitignore
secrets/

Add this line to your project’s .gitignore so the actual password file never reaches version control.

What Changed

Notice POSTGRES_PASSWORD_FILE, not POSTGRES_PASSWORD. The official Postgres image supports a second version of each variable, ending in _FILE. Instead of reading the password directly, it reads the path to a file. Then it reads the real password from inside that file, at startup.

Compose mounts your secrets/db_password.txt file at /run/secrets/db_password inside the container, automatically. That path comes from the secrets: section at the bottom of the file — you do not set it yourself.

Many official images (Postgres, MySQL, MongoDB) support this same _FILE pattern for their sensitive variables. Check an image’s Docker Hub page for a “Docker Secrets” section before assuming it is supported.

Why This Is Better

  • The real password lives in one file, outside your Compose file and outside Git.
  • Inside the container, the password is read from a file at startup — it does not sit as plainly in docker inspect output the way a normal environment variable does.
  • Teammates can each keep their own local secrets/db_password.txt, never shared through version control at all.

Compose secrets are a real improvement over plain environment variables. But the password is still just a file on your disk — not a service like AWS Secrets Manager, built to store secrets safely in the cloud. For a small team or a personal project, this is still a solid, practical step up. Real production secrets management is covered in the Production & Deploy section.

Hands-on Task

  1. Create a secrets/db_password.txt file with a real password, and add secrets/ to .gitignore.
  2. Update your db service to use POSTGRES_PASSWORD_FILE and the secrets: block above.
  3. Run docker compose up and confirm the stack still starts correctly with the password read from the file.
  4. Run docker compose down -v and start again — confirm PostgreSQL still creates the same user and password from the secret file.

Quick Check

  • Can you explain why a plain environment: password is risky?
  • Do you understand what POSTGRES_PASSWORD_FILE does differently from POSTGRES_PASSWORD?
  • Can you explain why secrets/ must be in .gitignore?

docker compose secrets, postgres password file docker, docker secrets vs environment variables, docker compose gitignore

Last updated on