Ctrl + K
Docker16 min read

Environment Variables in Docker

A practical guide to environment variables in Docker, including ENV, ARG, .env files, Docker Compose, runtime configuration and secret handling.

Published: 2026-10-05

Environment variables are one of the standard ways to configure applications running inside Docker containers. Instead of hardcoding configuration values into application code or creating a different image for every environment, you can provide values when a container starts.

Docker supports several mechanisms related to environment variables, including ENV in a Dockerfile, --env and --env-file when starting containers, and environment configuration in Docker Compose. Docker also supports build arguments with ARG, but ARG and environment variables serve different purposes.

Understanding these differences is important for building predictable containers and avoiding one of the most common Docker mistakes: treating environment variables as a secure storage mechanism for secrets.

What Are Environment Variables in Docker?

An environment variable is a named value made available to a process. Applications can read these values at runtime and use them for configuration such as ports, database connection settings, feature flags and external service URLs.

NODE_ENV=production
PORT=3000
API_URL=https://api.example.com

When an application runs inside a Docker container, these variables exist in the container's process environment. The application can read them in the same general way it would read environment variables outside Docker.

Why Use Environment Variables with Docker?

Containers are commonly designed to be reusable across environments. The same image can be deployed to development, staging and production while receiving different configuration values at runtime.

  • Keep environment-specific configuration outside application code.
  • Reuse the same Docker image across environments.
  • Change runtime configuration without rebuilding the image.
  • Configure database and API endpoints.
  • Control application modes such as development or production.
  • Configure ports and feature flags.
  • Integrate containers with deployment platforms.
💡 A useful rule is to build the application image once and provide environment-specific configuration when the container is deployed.

ENV in a Dockerfile

The ENV instruction defines environment variables in the image. These values become available to subsequent Dockerfile instructions and to processes started from containers created from that image.

FROM node:22

WORKDIR /app

ENV NODE_ENV=production
ENV PORT=3000

COPY . .

CMD ["node", "server.js"]

In this example, NODE_ENV and PORT are part of the image configuration. Any container created from the image receives these default values unless they are overridden when the container starts.

Setting Multiple ENV Values

Docker supports both separate ENV instructions and multiple key-value pairs in a single instruction.

ENV NODE_ENV=production
ENV PORT=3000

# Also valid
ENV NODE_ENV=production PORT=3000

Using one variable per line can make a Dockerfile easier to read when an application has several configuration values.

Overriding ENV at Container Startup

An environment variable defined in a Dockerfile can be overridden when the container is started.

docker run --env PORT=8080 my-app

The container will receive PORT=8080 instead of the Dockerfile's default value. This allows one image to be configured differently for different environments.

Using docker run --env

The --env or -e option allows you to provide environment variables directly when creating a container.

docker run --env NODE_ENV=production --env PORT=3000 my-app

You can provide multiple --env options when several variables are required.

docker run \
  --env NODE_ENV=production \
  --env PORT=3000 \
  --env API_URL=https://api.example.com \
  my-app

Using --env-file

When an application has many environment variables, placing them directly in the docker run command quickly becomes inconvenient. Docker can load variables from a file using --env-file.

NODE_ENV=production
PORT=3000
API_URL=https://api.example.com
docker run --env-file .env my-app

The variables from the file are made available to the container. This approach is convenient for local development and controlled deployment environments.

What Is a .env File?

A .env file is commonly used to store environment configuration in a simple KEY=VALUE format. Docker Compose supports .env files for variable substitution, and Docker can also use environment files as input when starting containers.

APP_NAME=my-app
PORT=3000
NODE_ENV=development
API_URL=http://localhost:4000

The exact behavior of a .env file depends on how it is being used. A .env file used by Docker Compose for interpolation is not exactly the same concept as passing an environment file directly into a container with docker run --env-file.

.env Files and Git

Local .env files often contain credentials or environment-specific configuration and should generally not be committed to a public repository when they contain sensitive values.

.env
.env.local
.env.production
.env.*.local

A safer pattern is to commit a template containing variable names but not real credentials.

# .env.example
NODE_ENV=
PORT=
API_URL=
DATABASE_URL=
💡 Use .env.example to document which variables an application expects while keeping actual credentials and environment-specific values outside version control.

Docker Compose Environment Variables

Docker Compose provides several ways to define environment variables. A common approach is the environment section in compose.yaml.

services:
  app:
    image: my-app
    environment:
      NODE_ENV: production
      PORT: 3000

Compose passes these values into the container when the service starts.

Using List Syntax in Docker Compose

The environment section can also use list syntax.

services:
  app:
    image: my-app
    environment:
      - NODE_ENV=production
      - PORT=3000

Both mapping and list forms are supported. Mapping syntax is often easier to read when there are many variables.

Using env_file in Docker Compose

Compose can load container environment variables from an external environment file using env_file.

services:
  app:
    image: my-app
    env_file:
      - .env

This keeps a large collection of configuration values outside the Compose file itself and can be convenient for local development.

Environment Variable Substitution in Compose

Compose can substitute environment variables into the Compose configuration. For example, a port mapping can use a variable defined in the environment.

services:
  app:
    image: my-app
    ports:
      - "${APP_PORT}:3000"

If APP_PORT is set to 8080, Compose can resolve the expression to a mapping from host port 8080 to container port 3000.

Default Values in Compose

Compose variable substitution supports default-value syntax. This can be useful when a value has a reasonable local default.

services:
  app:
    ports:
      - "${APP_PORT:-3000}:3000"

If APP_PORT is not set, the expression uses 3000 as the default host port.

ARG vs ENV in Docker

ARG and ENV are frequently confused because both can contain key-value configuration. Their purposes are different.

FeatureARGENV
Primary purposeBuild-time configurationRuntime environment configuration
Available during image buildYesYes
Available to container process by defaultNoYes
Can be overridden at container startupNoYes
Suitable for secretsNoNo

For example, ARG can be used to select a build-time version.

ARG NODE_VERSION=22

FROM node:${NODE_VERSION}

ENV, on the other hand, defines configuration that is intended to exist in the resulting image or container environment.

ENV NODE_ENV=production

ARG Is Not a Secret Mechanism

Passing a password, API key or other sensitive value through ARG does not make it secure. Build arguments can become visible through build metadata or image history depending on how they are used.

# Do not use ARG for credentials
ARG API_KEY=secret-value
⚠️ Neither ARG nor ENV should be treated as a secure secret store. Use dedicated secret mechanisms for sensitive credentials.

Environment Variables and Docker Image Builds

It is important to distinguish values required to build an application from values required to run it. A frontend application may need certain variables during its build process, while a backend application may primarily read configuration when it starts.

Build-time values can become embedded into generated application files. Once a value has been compiled into static JavaScript or another artifact, changing the container's runtime environment may not change that value.

⚠️ For frontend applications, never assume that an environment variable is private just because it came from a .env file. Variables included in browser-delivered bundles can be visible to users.

Environment Variables in Node.js Containers

Node.js applications can access environment variables through process.env.

const port = process.env.PORT || 3000;
const nodeEnv = process.env.NODE_ENV || "development";

console.log({
  port,
  nodeEnv,
});

When the container starts, Docker provides the configured environment variables to the Node.js process.

docker run --env PORT=8080 --env NODE_ENV=production my-app

Validate Required Environment Variables

Applications should validate important environment variables during startup instead of silently continuing with missing configuration.

const required = ["DATABASE_URL", "API_URL"];

for (const name of required) {
  if (!process.env[name]) {
    throw new Error(`Missing required environment variable: ${name}`);
  }
}

Failing early makes configuration problems easier to identify and prevents an application from starting in a partially configured state.

Do Not Hardcode Secrets in Dockerfiles

Credentials should not be placed directly in ENV instructions.

# Bad practice
ENV DATABASE_PASSWORD=my-password
ENV API_SECRET=super-secret-value

Even if a secret is later overridden, putting the original value into the Dockerfile can expose it through source control, build logs, image history or other build metadata.

Environment Variables vs Docker Secrets

Environment variables are convenient for ordinary configuration, but secrets require additional consideration. Docker and container orchestration platforms provide mechanisms specifically intended for sensitive values.

For example, a production deployment may provide a database password through a secret-management system instead of putting it directly into the Dockerfile or Compose configuration.

The exact secret mechanism depends on the deployment environment. The important principle is to keep credentials separate from the image and avoid treating ordinary environment variables as encrypted storage.

Do Environment Variables Become Part of the Image?

Environment variables defined with ENV in a Dockerfile become part of the image configuration. They are therefore not equivalent to values injected only when a container starts.

This distinction is another reason to avoid putting sensitive information into ENV. If a value is environment-specific, it is usually better to provide it at runtime.

Passing Variables Without Rebuilding the Image

One of the main advantages of runtime environment configuration is that you can change configuration without rebuilding the image.

docker run --env API_URL=https://staging.example.com my-app

docker run --env API_URL=https://api.example.com my-app

Both containers can use the same image while communicating with different API endpoints.

Environment Variable Precedence

When multiple configuration sources define the same variable, the effective value depends on the Docker command or Compose configuration being used. Understanding the precedence rules is important when debugging configuration issues.

For Docker Compose in particular, distinguish between variables used to substitute values into the Compose file and variables that are actually passed into the container. A variable can exist in the Compose process environment without automatically becoming a container environment variable in every configuration.

💡 When debugging a variable, first determine where it is defined, then check whether it is being used for Compose interpolation or explicitly passed into the container.

Inspecting Container Environment Variables

Docker provides commands that can help inspect the environment associated with a container.

docker inspect my-container

You can also inspect the environment from inside a running container.

docker exec my-container env

Be careful when sharing command output. Environment variables can contain credentials or other sensitive information.

Common Environment Variable Mistakes

MistakeWhy It Is a ProblemBetter Approach
Hardcoding secrets in ENVSecrets can become part of the imageInject secrets through an appropriate secret mechanism
Using ARG for passwordsBuild arguments are not a secure secret storeUse build secret mechanisms when a build needs credentials
Committing .env with credentialsSecrets can enter version controlIgnore sensitive env files and use templates
Copying .env into an imageConfiguration and secrets become image contentsProvide runtime configuration externally
Assuming frontend variables are privateBrowser code can expose themKeep private values server-side
Changing runtime ENV expecting a rebuilt valueBuild-time values may already be embeddedUnderstand whether the variable is build-time or runtime
Not validating required variablesApplication may start with invalid configurationValidate configuration during startup

Recommended Environment Variable Structure

A project benefits from having a consistent naming and configuration strategy. Variable names should clearly communicate their purpose and should be documented for developers and deployment systems.

# Application
NODE_ENV=production
PORT=3000

# External services
API_URL=https://api.example.com

# Database
DATABASE_URL=...

Sensitive values can be documented in an example file without including the actual credentials.

# .env.example
NODE_ENV=production
PORT=3000
API_URL=
DATABASE_URL=

Environment Variables in Development and Production

Development and production usually require different configuration values. For example, development may connect to a local database while production connects to a managed database service.

# Development
NODE_ENV=development
API_URL=http://localhost:4000

# Production
NODE_ENV=production
API_URL=https://api.example.com

The Docker image does not necessarily need to change just because these values change. Runtime configuration can provide the appropriate settings for each environment.

Environment Variables with Docker Compose for Multiple Services

Compose becomes particularly useful when several services need different configuration values. For example, an application and database can be configured independently while sharing a Compose network.

services:
  app:
    image: my-app
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://db:5432/app

  db:
    image: postgres:18
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app

In real production environments, database credentials should be handled using appropriate secret-management mechanisms rather than storing real passwords directly in a Compose file.

Use Descriptive Variable Names

Environment variables should be easy to identify and consistent across the project.

  • Use uppercase names for conventional environment configuration.
  • Use underscores to separate words.
  • Choose names that describe the value clearly.
  • Use consistent prefixes for related services when useful.
  • Document required variables.
  • Avoid ambiguous names such as VALUE or CONFIG.

Environment Variables and .dockerignore

If local environment files are not supposed to be copied into an image, they should usually be excluded from the Docker build context with .dockerignore.

.env
.env.*
!.env.example

The exact patterns depend on the project's requirements. The important goal is to prevent local credentials and environment-specific files from accidentally becoming build inputs.

A Practical Dockerfile Pattern

A production-oriented Dockerfile commonly provides only safe defaults through ENV and leaves environment-specific configuration to the runtime.

FROM node:22-slim

WORKDIR /app

ENV NODE_ENV=production
ENV PORT=3000

COPY package.json package-lock.json ./
RUN npm ci --omit=dev

COPY dist ./dist

USER node

EXPOSE 3000

CMD ["node", "dist/server.js"]

The image contains a reasonable default runtime configuration, while deployment-specific values such as API URLs and database connections can be injected when the container starts.

Environment Variable Workflow

A simple workflow helps keep Docker configuration predictable.

  • Identify which configuration values the application requires.
  • Separate ordinary configuration from sensitive credentials.
  • Document expected variables in an example file.
  • Keep sensitive local files out of version control.
  • Use ENV only for appropriate image-level defaults.
  • Use runtime injection for environment-specific values.
  • Use secret-management mechanisms for sensitive values.
  • Validate required variables when the application starts.
  • Verify the final container environment before deployment.

Frequently Asked Questions

How do I pass an environment variable to a Docker container?

You can use docker run with the --env or -e option, for example docker run --env PORT=3000 my-app. For multiple variables, you can also use an environment file with --env-file.

What is the difference between Docker ENV and ARG?

ARG is primarily intended for build-time configuration, while ENV defines environment variables that are available to processes in containers created from the image. ARG values do not automatically become runtime environment variables.

Should passwords be stored in Docker ENV?

No. ENV is not a secure secret-storage mechanism. Sensitive credentials should be supplied through an appropriate secret-management mechanism or secure runtime configuration.

Can I use a .env file with Docker?

Yes. Docker can use environment files with commands such as docker run --env-file, and Docker Compose can use .env files for variable substitution and container configuration. The exact behavior depends on how the file is referenced.

Do environment variables require rebuilding a Docker image?

Not when they are provided at container startup. Runtime environment variables can change without rebuilding the image. However, variables used during an application's build process may become embedded into generated artifacts and can therefore require a new build when changed.

Are Docker environment variables secure?

Ordinary environment variables should not be considered a secure secret store. Depending on how they are configured, they can be inspected by Docker commands or processes with sufficient access. Use dedicated secret-management mechanisms for sensitive credentials.

How can I see environment variables inside a Docker container?

You can inspect container configuration with docker inspect or execute a command such as env inside a running container with docker exec. Be careful not to expose sensitive values when sharing the output.

Helpful Docker Tools

Working with Docker configuration often involves several related tasks. Environment variable generators can help create configuration templates, dotenv parsers can inspect and analyze .env files, Docker Compose generators can create service configurations, Dockerfile generators can help build container definitions, and secret generators can create random values for appropriate secret-management workflows.

Conclusion

Environment variables provide a flexible way to configure Docker containers without creating a separate image for every environment. Dockerfile ENV can provide safe defaults, docker run can inject values at startup, --env-file can load larger configurations, and Docker Compose provides additional mechanisms for multi-container applications.

The most important distinction is between build-time and runtime configuration. ARG is intended for build-time values, while ENV is used for runtime environment configuration. Neither should be treated as a secure place to store credentials. Keeping secrets outside images, validating required configuration and using runtime injection makes Docker deployments easier to maintain and safer to operate.

Found an issue?

Found an error, outdated information, or something missing from this article? Let me know through the Contact page.

Your feedback helps improve our articles and keep them accurate and useful.