docker-compose-variables-entorno-env

Environment Variables in Docker Compose

  • 5 min

Environment variables in Docker Compose are external values that parameterize the YAML file without having to edit it every time.

In the previous article, we wrote a docker-compose.yml that worked, but it had a security and flexibility problem.

We had things like:

environment:
  MYSQL_ROOT_PASSWORD: password123  # ❌ Visible password!
ports:
  - "8080:80"                       # ❌ Fixed port
Copied!

If you share this project:

  1. Security: Everyone knows your admin password.
  2. Rigidity: If your colleague is already using port 8080 for something else, they have to edit your docker-compose.yml file to be able to work.

To solve this, Docker Compose integrates natively with .env files.

The code (YAML) defines the structure, but the configuration (Variables) defines the environment.

What is an environment variable?

It’s a dynamic value that can affect the behavior of processes on a computer. In Docker Compose, we use these variables to replace values within the YAML file before executing it.

The syntax to use a variable in Compose is the “Shell” style: ${VARIABLE_NAME}.

Transforming our YAML

Let’s fix our security disaster. Instead of hardcoding the values, we put “placeholders”:

services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}  # ✅ Variable
      MYSQL_DATABASE: ${DB_NAME}           # ✅ Variable

  admin:
    image: phpmyadmin/phpmyadmin
    ports:
      - "${HOST_PORT}:80"                  # ✅ Variable
    depends_on:
      - db
Copied!

If you try to run docker compose up now, Docker will warn you (or use empty strings) because those variables are not defined. We need to give them values.

In older examples, you’ll often see version: "3.8" at the beginning of the file. In current Docker Compose, this field is kept for compatibility, but it’s no longer necessary to include it.

The .env File

Docker Compose automatically looks in the same folder for a file named .env (hidden, starts with a dot).

It’s a simple text file with a KEY=VALUE format. Create an .env file next to your docker-compose.yml:

# Database Configuration
DB_PASSWORD=SuperSecret123!
DB_NAME=my_application

# Port Configuration
HOST_PORT=8080
Copied!

That’s it! You don’t need to configure anything else. When you run docker compose up, Docker reads the .env, replaces ${DB_PASSWORD} with SuperSecret123!, and starts the containers.

Security and Git

The .env file should NEVER be uploaded to the code repository (Git). It contains your actual secrets. Your production password, your API keys, etc.

So, how does my team know which variables they need to create? The standard practice is to create a template file called .env.example (or .env.template).

Steps to do it right:

Create your .env with your real passwords.

Add .env to your .gitignore file. (This prevents you from accidentally uploading it).

Create an .env.example with fake or empty values:

# .env.example
DB_PASSWORD=change_this
DB_NAME=app_db
HOST_PORT=8080
Copied!

Upload the .env.example to GitHub.

When a new developer downloads your project, they will see the .example, make a copy, rename it to .env, and put their own values.

The .env file prevents you from writing secrets inside the YAML, but it’s not a safe. If someone has access to the server or the local project, they can read it. For sensitive secrets, use a secret manager or Docker Secrets when appropriate.

Secrets in Docker Compose

If you have a password, token, or private key, it’s best not to treat it as a normal variable.

For these cases, Docker Compose allows declaring secrets. The idea is simple: instead of passing the value as an environment variable, Docker mounts a file inside the container, and the application reads it from there.

For example, for a PostgreSQL database:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt
Copied!

In this case, the actual password is in a local file:

./secrets/db_password.txt
Copied!

And inside the container, it appears mounted as:

/run/secrets/db_password
Copied!

The official PostgreSQL image understands the POSTGRES_PASSWORD_FILE variable, reads the password from that file, and avoids having to write it directly in the YAML.

This doesn’t automatically turn your laptop into Fort Knox. In local Docker Compose, the ./secrets/db_password.txt file still exists on your disk, and you must protect it with appropriate permissions and exclude it from Git.

But it’s much better than leaving the password written in docker-compose.yml or embedding it inside the image.

The difference between .env and env_file

There is a common confusion in Docker Compose.

  1. Substitution in YAML (what we just saw): Docker reads the .env and replaces ${VAR} inside the docker-compose.yml file itself. It’s used to configure ports, replicas, image versions, etc.
  2. Passing variables to the container (env_file): Sometimes you have an application (e.g., Django or Laravel) that needs 50 environment variables to function internally. Writing them all in the environment: section of the YAML is very messy. You can tell Docker: “Take this entire file and inject all its variables INSIDE the container”.
services:
  my-app:
    image: my-image
    env_file:
      - ./config/app.env  # Load everything from here inside the container
Copied!