How to Set Environment Variables in Docker

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:

  1. 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 -
    
  2. 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 override Dockerfile 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:

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.

 

Leave a Reply