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
mkdir secrets
echo "secret" > secrets/db_password.txtUse a real, strong password here in an actual project â secret is only used to match the earlier examples.
Reference it in 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.txtNever commit the secret file
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 inspectoutput 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
- Create a
secrets/db_password.txtfile with a real password, and addsecrets/to.gitignore. - Update your
dbservice to usePOSTGRES_PASSWORD_FILEand thesecrets:block above. - Run
docker compose upand confirm the stack still starts correctly with the password read from the file. - Run
docker compose down -vand 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_FILEdoes differently fromPOSTGRES_PASSWORD? - Can you explain why
secrets/must be in.gitignore?