Managing configurations is crucial for running secure and efficient applications within Docker containers. Environment variables provide a flexible and powerful way to configure your containers without modifying the underlying image.
This comprehensive guide explains how to pass environment variables to Docker containers using various methods, covering everything from basic command-line usage to advanced security practices with Docker Secrets and HashiCorp Vault.
Why Use Environment Variables in Docker?
Environment variables offer several key advantages for containerized applications:
- Portability: Containers can be run in different environments (development, staging, production) without changing the image itself. You simply adjust the environment variables.
- Security: Sensitive information like API keys, database passwords, and other credentials should not be hardcoded into your Dockerfile or application code. Environment variables provide a more secure way to manage this data.
- Configuration Management: Environment variables centralize configuration, making it easier to manage and update settings across multiple containers and services.
- Flexibility: You can easily override default settings defined in a Dockerfile at runtime.
Methods for Passing Environment Variables
There are several ways to pass environment variables to Docker containers, each with its own advantages and use cases.
1. Using the Command-Line (-e
Flag)
The simplest method is to use the -e
(or --env
) flag with the docker run
command. This allows you to set environment variables directly when starting a container:
docker run -e MY_VAR=my_value -e ANOTHER_VAR=another_value my-image
This method is suitable for quick tests and development. However, it’s important to note that environment variables set this way might be visible in system process listings (e.g., using ps
), making it unsuitable for sensitive data in production environments. Ensure that all -e
flags and options appear before the image name in your docker run
command.
Read: How to install and setup Docker on Ubuntu 22.04
2. Using an Environment File (--env-file
)
Use an environment file for more organized and secure handling of multiple environment variables, especially in production. Create a plain text file (often named .env
, but the name is arbitrary) containing key-value pairs:
# .env file example
DATABASE_URL=mysql://user:password@host:3306/database
SITE_MODE=staging
API_KEY=your_secret_api_key
Then, use the --env-file
flag with docker run
:
docker run --env-file .env my-image
This approach keeps sensitive information out of your command history and makes it easier to manage different configurations for different environments. Avoid using extra quotes around values in your .env
file, as these quotes might be included in the variable’s value within the container. Write VAR=value
, not VAR="value"
.
3. Dockerfile ENV
Instruction
You can set default environment variables directly within your Dockerfile
using the ENV
instruction:
ENV DB_HOST=database
ENV DB_NAME=appdb
ENV DB_USER=dbadmin
ENV APP_VERSION=1.0
These variables will be available within the container at runtime. You can also set variables dynamically during the build process using shell commands:
ENV APP_VERSION=$(cat version.txt)
This example reads the contents of a version.txt
file and sets the APP_VERSION
environment variable accordingly. Variables set with ENV
can be overridden at runtime using the -e
or --env-file
options.
Read: How to create new users in a Docker container?
4. Docker Compose (docker-compose.yml
)
Docker Compose simplifies the management of multi-container applications. It provides several ways to handle environment variables.
a. Using a .env
File with Docker Compose
Place a .env
file in the same directory as your docker-compose.yml
file. For example:
# .env file
APP_ENV=production
API_KEY=abcdef123456
Then, in your docker-compose.yml
, you can reference these variables using variable substitution:
version: "3.8"
services:
web:
image: my-web-app
environment:
- APP_ENV=${APP_ENV}
- API_KEY=${API_KEY}
This approach promotes consistency and security across different environments. Docker Compose automatically loads the .env
file.
b. Inheriting Host Environment Variables
You can pass environment variables from your host machine directly to your containers. First, export the variable on your host:
export DEBUG_MODE=true
Then, in your docker-compose.yml
, list the variable without a value:
version: "3.8"
services:
app:
image: my-app
environment:
- DEBUG_MODE
Docker Compose will use the value of DEBUG_MODE
from your host environment. This simplifies configuration, especially when dealing with development-specific settings.
Read: Mastering Python Virtual Environments: A Comprehensive Guide to venv, pipenv, poetry, and More
c. Defining Environment Variables Directly in docker-compose.yml
You can also define environment variables directly within the environment
section of your docker-compose.yml
file:
version: '3.8'
services:
mycontainer:
image: myimage
environment:
DB_PASSWORD: mysecretpassword
This is similar to using the -e flag, Use this method cautiously with sensitve data.
5. Shell Process Substitution
For advanced use cases, you can use shell process substitution to dynamically pass a subset of host environment variables. For example:
docker run --env-file <(env | grep '^AWS_') my-image
This command passes only environment variables that start with AWS_
to the container. Use this technique with caution to avoid accidentally exposing sensitive variables.
Read: How to copy files from host to Docker container
Advanced Security: Docker Secrets and HashiCorp Vault
For highly sensitive data, such as passwords and API keys, relying solely on environment variables is not recommended, even with --env-file
. Environment variables can still be exposed through logs, debugging tools, or accidental misconfigurations. Docker Secrets and HashiCorp Vault provide more robust security.
Docker Secrets
Docker Secrets are designed to securely manage sensitive information within a Docker Swarm cluster. While they can be used with standalone containers, their primary benefit is in orchestrated environments.
To use Docker Secrets:
- Create a Secret: You can create a secret from a file or from standard input. For example:
echo "my_secret_password" | docker secret create db_password -
- Reference the Secret in Your
docker-compose.yml
:version: "3.8" services: database: image: mysql:latest environment: - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_password secrets: - db_password secrets: db_password: external: true # Use if the secret already exists # Or, to create it from a file: # file: ./db_password.txt
The MYSQL_ROOT_PASSWORD_FILE
environment variable tells MySQL to read the password from the specified file, which is mounted as a read-only file inside the container. This ensures that the sensitive data never appears in logs or the process list.
HashiCorp Vault
For enterprise-grade secret management, especially in complex, multi-cloud environments, consider using HashiCorp Vault. Vault is a dedicated secrets management solution that provides:
- Centralized Secret Storage: Secrets are stored securely and encrypted at rest.
- Access Control: Fine-grained access control policies determine who can access which secrets.
- Dynamic Secrets: Vault can generate temporary credentials for various services (databases, cloud providers, etc.), reducing the risk of long-lived credentials being compromised.
- Auditing: Vault provides detailed audit logs of all secret access and management activities.
Integrating Vault with Docker typically involves using the Vault Agent, which can automatically authenticate and retrieve secrets for your containers. While the setup is more complex than Docker Secrets, it offers significantly enhanced security and scalability.
Accessing Environment Variables Inside Containers
Once environment variables are passed to your container, you can access them within your application code using standard library functions provided by your programming language. Examples:
- Python:
import os db_host = os.environ.get("DB_HOST") # Use .get() to provide a default value if the variable is not set
- Node.js:
const dbHost = process.env.DB_HOST;
- Java:
String dbHost = System.getenv("DB_HOST");
- PHP
$dbHost = getenv('DB_HOST');
Using os.environ.get()
in Python (or similar methods in other languages) is generally preferred over directly accessing os.environ["DB_HOST"]
because it allows you to provide a default value if the environment variable is not set, preventing errors.
Best Practices and Common Pitfalls
- Prioritize Security: Never hardcode sensitive information. Use environment variables for configuration, and use Docker Secrets or HashiCorp Vault for highly sensitive data.
- Use
.env
Files for Local Development:.env
files simplify local development and testing. However, never commit.env
files containing secrets to version control. - Be Mindful of Variable Scope: Understand the difference between variables set in the
Dockerfile
(build-time) and those set at runtime (using-e
,--env-file
, or Docker Compose). Runtime variables overrideDockerfile
variables. - Use Consistent Naming Conventions: Adopt a consistent naming convention for your environment variables (e.g.,
UPPER_SNAKE_CASE
). - Avoid Unnecessary Quotes: As mentioned earlier, avoid unnecessary quotes in your .env files.
- Order of parameters: Ensure parameters and flags appear before the image name in your
docker run
command. - Document Your Environment Variables: Clearly document all required and optional environment variables for your application.
Conclusion
Environment variables are a fundamental aspect of Docker container configuration. By understanding the different methods for passing and managing them, and by prioritizing security best practices, you can create portable, secure, and easily configurable containerized applications. This guide has provided a comprehensive overview, from basic usage to advanced techniques.
For further exploration, consult the official Docker documentation:
- Docker Run Environment Variables
- Docker Compose Environment Variables
- How to Use Secrets in Docker Compose
- Docker Security Best Practices
To learn even more about Docker and containerization, explore other articles on this site!
If you like the content, we would appreciate your support by buying us a coffee. Thank you so much for your visit and support.