Mastering `docker run -e`: Environment Variables Guide
Introduction: The Unseen Power of Environment Variables in Docker
In the dynamic world of containerization, Docker stands as a foundational technology, enabling developers to package applications and their dependencies into portable, self-sufficient units. At the heart of making these containers adaptable and versatile lies a seemingly simple yet incredibly powerful mechanism: environment variables. While Docker images provide a static blueprint, environment variables, primarily injected via the docker run -e command, are the conduits through which we infuse runtime dynamism, configuration flexibility, and operational context into our containerized applications.
This comprehensive guide delves deep into the nuances of docker run -e, exploring its syntax, mechanics, best practices, security implications, and its indispensable role in modern microservices architectures. We'll uncover how environment variables empower developers to build robust, scalable, and environment-agnostic applications, from simple web servers to complex API gateway systems managing a multitude of API interactions. By the end of this journey, you'll possess a master-level understanding of how to leverage environment variables effectively, transforming your Docker deployments from rigid constructs into highly adaptable and intelligent components of your infrastructure.
The ability to externalize configuration via environment variables is not merely a convenience; it's a fundamental principle of cloud-native development, promoting the "Twelve-Factor App" methodology's emphasis on configuration as a critical aspect of application design. Instead of baking sensitive or environment-specific details directly into the image, environment variables allow us to defer these decisions until runtime, fostering greater security, flexibility, and operational agility.
The Genesis of docker run -e: Why Environment Variables Matter
Before we dissect the docker run -e command itself, it's crucial to grasp the underlying philosophy behind environment variables and their significance in the container ecosystem. Traditionally, applications might have relied on configuration files (e.g., config.ini, application.properties, settings.json) or command-line arguments to dictate their behavior. While these methods have their place, environment variables offer distinct advantages in containerized environments:
- Immutability and Portability: Docker images are designed to be immutable. Baking environment-specific configurations into an image would necessitate building a new image for every environment (development, staging, production), undermining the "build once, run anywhere" promise of containers. Environment variables decouple configuration from the image, allowing the same image to run in different environments with varying settings. This dramatically simplifies CI/CD pipelines and reduces deployment friction.
- Security and Secrecy (with caveats): While not a perfect solution for secrets management, environment variables provide a layer of separation for sensitive data like database credentials, API keys, and access tokens, preventing them from being hardcoded into source code or committed to version control. They are generally more secure than plain text files mounted into containers, as they are not persisted on the host filesystem in the same way. However, their limitations for true secret management will be thoroughly explored later.
- Standardization and Interoperability: Environment variables are a universally recognized mechanism across operating systems and programming languages. This standardization simplifies integration with various tooling, scripting, and orchestration platforms like Kubernetes, Docker Compose, and CI/CD systems, all of which natively support injecting environment variables. It creates a common language for configuring applications, irrespective of their internal implementation details.
- Runtime Flexibility: The true power of environment variables lies in their ability to inject configuration at the moment a container is launched. This means an application can adapt its behavior based on the host environment, available resources, or specific deployment requirements without requiring any changes to its codebase or recompilation. For instance, a single web application image can be configured to connect to different databases, switch API endpoints, or enable/disable features simply by changing the environment variables passed during
docker run. - Integration with Orchestration: Modern container orchestration platforms heavily rely on environment variables for configuration. Docker Compose uses an
environmentsection, Kubernetes usesenvarrays in Pod definitions, and cloud platforms often provide mechanisms to inject environment variables into running containers. This seamless integration makes environment variables the de facto standard for dynamic configuration in distributed systems.
Understanding these foundational principles is key to appreciating why docker run -e is not just a command, but a critical component of a robust containerization strategy. It’s the bridge between a static application image and its dynamic operational context.
Deconstructing docker run -e: Syntax and Basic Usage
The docker run -e flag is the primary mechanism to pass environment variables to a Docker container. Its syntax is straightforward, yet capable of expressing complex configurations. Let's break it down.
Basic Syntax
The most common way to use -e is to specify a KEY=VALUE pair:
docker run -e MY_VARIABLE="my_value" my_image
Here: * docker run: The command to create and start a new container. * -e or --env: The flag indicating that an environment variable is being passed. * MY_VARIABLE="my_value": The specific environment variable, consisting of a key (MY_VARIABLE) and its corresponding value (my_value). It's good practice to quote values, especially if they contain spaces or special characters, to ensure they are interpreted correctly by the shell.
You can specify multiple environment variables by using the -e flag multiple times:
docker run -e DB_HOST="database.example.com" -e DB_PORT=5432 -e API_KEY="your_secret_api_key_here" my_application_image
Each -e flag introduces a new environment variable into the container's execution environment. These variables become accessible to the main process running inside the container, as well as any child processes it spawns.
Accessing Environment Variables Inside the Container
Once inside the container, applications can access these environment variables using standard methods provided by their respective programming languages or shell commands.
Example (Bash/Shell):
# Inside the container
echo $MY_VARIABLE
# Output: my_value
Example (Python):
import os
my_variable = os.environ.get("MY_VARIABLE")
print(my_variable)
# Output: my_value
Example (Node.js/JavaScript):
const myVariable = process.env.MY_VARIABLE;
console.log(myVariable);
// Output: my_value
Example (Java):
String myVariable = System.getenv("MY_VARIABLE");
System.out.println(myVariable);
// Output: my_value
This universal accessibility makes environment variables an incredibly flexible configuration mechanism across diverse tech stacks.
Passing Environment Variables from the Host
A powerful feature is the ability to pass environment variables that are already defined in your host shell directly into the container, without explicitly writing KEY=VALUE. If you omit the =VALUE part, Docker will attempt to resolve the variable from the host's environment:
# On your host machine
export MY_HOST_VAR="This comes from the host"
# Then, run the container
docker run -e MY_HOST_VAR my_image env | grep MY_HOST_VAR
# Output: MY_HOST_VAR=This comes from the host
This capability is particularly useful in scripting or CI/CD pipelines where certain variables (like build numbers, commit SHAs, or temporary credentials) are already present in the execution environment and need to be propagated to containers.
Important Considerations for KEY=VALUE Pairs:
- Quoting: Always quote values, especially if they contain spaces, special characters (
&,|,<,>,(,),;,\,$,*,?,[,],#,~,=,%), or if you want to ensure the shell doesn't perform expansion prematurely.docker run -e 'MESSAGE=Hello World'is safer thandocker run -e MESSAGE=Hello World. - Variable Names: Environment variable names typically consist of uppercase letters, numbers, and underscores (
_). They should not start with a number. This is a convention, not a strict Docker rule, but adheres to POSIX standards for shell variables. - Empty Values: You can pass an environment variable with an empty value:
docker run -e MY_EMPTY_VAR="" my_image. This is distinct from not passing the variable at all. - No Value (Host Passthrough): As discussed,
docker run -e MY_HOST_VARwill pull the value from the host's environment ifMY_HOST_VARis set on the host. IfMY_HOST_VARis not set on the host, Docker will passMY_HOST_VARwith an empty string value to the container.
Mastering these basic syntaxes and behaviors forms the bedrock for more advanced and secure environment variable management strategies within Docker.
Deep Dive into docker run -e Mechanics and Precedence
Understanding how Docker handles environment variables is crucial, especially when dealing with complex multi-stage builds, inherited images, and various configuration sources. There's a clear hierarchy that dictates which value takes precedence when the same environment variable is defined in multiple places.
Sources of Environment Variables in Docker
Environment variables can originate from several places:
- Dockerfile
ENVInstructions: These variables are set during the image build process and are baked into the image. They serve as default values or configuration points for the application within the image itself.dockerfile # Dockerfile FROM alpine ENV APP_NAME="MyDefaultApp" ENV DB_HOST="localhost" CMD ["sh", "-c", "echo App: $APP_NAME, DB: $DB_HOST"]When an image built from this Dockerfile is run without any-eflags,APP_NAMEandDB_HOSTwill have their default values. docker run -eFlags: These are variables passed at container runtime, directly overriding anyENVinstructions defined in the Dockerfile.docker run --env-fileOption: This option allows you to load environment variables from one or more files. This is often preferred for managing many variables or separating sensitive ones.- Base Image Inheritance: If your Dockerfile uses a
FROMinstruction, environment variables set in the base image will be inherited by your derived image unless explicitly overridden.
The Precedence Order
Docker follows a specific order of precedence when resolving environment variables. When a conflict arises (the same variable name is defined in multiple places), the value from the higher-precedence source wins. The order, from lowest to highest precedence, is:
- Values inherited from the base image.
ENVinstructions in the Dockerfile. These overwrite values from the base image if a conflict exists.- Variables loaded from
--env-file(in order of declaration if multiple files are used). Variables in later files overwrite those in earlier files. docker run -eflags. These have the highest precedence, overriding any values set in the Dockerfile or from--env-file.
Let's illustrate with an example:
Dockerfile:
FROM alpine
ENV APP_MODE="production"
ENV DEBUG_LOGGING="false"
ENV DB_HOST="default-db"
env_vars.env file:
DB_HOST=file-db
DEBUG_LOGGING=true
NEW_VAR=hello
Running the container:
docker run \
--env-file ./env_vars.env \
-e APP_MODE="development" \
my_image \
env
Expected Output (env command inside container would show): * APP_MODE=development (from -e flag, highest precedence) * DEBUG_LOGGING=true (from --env-file, overrides Dockerfile) * DB_HOST=file-db (from --env-file, overrides Dockerfile) * NEW_VAR=hello (from --env-file, new variable)
This precedence model is critical for effective configuration management. It allows developers to define sensible defaults in the image (via ENV), provide environment-specific overrides via files (--env-file), and then make last-minute, command-line adjustments (-e) for testing or debugging without rebuilding the image.
Implications of Precedence
- Defaults and Overrides:
ENVin Dockerfile is perfect for setting application defaults.docker run -eis perfect for overriding those defaults for specific deployments. - Debugging: When troubleshooting, you can temporarily override variables with
-ewithout modifying any files, helping isolate issues. - Multi-Environment Deployments: A single Docker image can serve multiple environments (dev, staging, prod) by simply swapping out the
--env-fileor passing different-eflags. This is fundamental to CI/CD pipelines.
By grasping this intricate dance of precedence, you gain the power to precisely control your container's environment, ensuring your applications behave exactly as intended across various operational contexts.
Common Use Cases and Practical Examples of docker run -e
The versatility of docker run -e shines through in a myriad of practical scenarios. Let's explore some of the most common and impactful use cases, complete with illustrative examples.
1. Database Connection Configuration
Perhaps the most ubiquitous use case is configuring database connections. Instead of hardcoding credentials or hostnames into your application, environment variables provide a flexible and secure way to connect to different databases in various environments.
Scenario: A Node.js web application needs to connect to a PostgreSQL database.
Dockerfile (snippet):
# ... other build steps ...
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# Set default values for local development if needed, but often omitted for critical vars
ENV DB_HOST="localhost"
ENV DB_PORT="5432"
ENV DB_USER="app_user"
ENV DB_NAME="my_app_db"
# Do NOT set DB_PASSWORD here in production images
CMD ["npm", "start"]
Running the container for development (local PostgreSQL):
docker run \
-p 3000:3000 \
-e DB_HOST="host.docker.internal" \ # For Docker Desktop on Mac/Windows to access host DB
-e DB_PORT="5432" \
-e DB_USER="dev_user" \
-e DB_PASS="dev_password" \
-e DB_NAME="my_app_dev_db" \
my_nodejs_app
Running the container for production (remote PostgreSQL):
docker run \
-p 80:3000 \
-e DB_HOST="prod-db-instance.us-east-1.rds.amazonaws.com" \
-e DB_PORT="5432" \
-e DB_USER="prod_user" \
-e DB_PASS="very_secure_prod_password_from_secret_manager" \
-e DB_NAME="my_app_prod_db" \
my_nodejs_app
Note: Directly passing DB_PASS via -e on the command line is generally discouraged for production. We'll cover better secret management later.
2. API Keys and External Service Integration
Applications frequently interact with external APIs (e.g., payment gateways, mapping services, weather data). These interactions often require API keys or tokens, which are prime candidates for environment variables.
Scenario: A Python application uses an external weather API.
Dockerfile (snippet):
# ...
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Running the container:
docker run \
-p 5000:5000 \
-e WEATHER_API_KEY="your_actual_weather_api_key_12345" \
-e GEOLOCATION_API_ENDPOINT="https://api.geolocation.com/v1" \
my_python_app
This allows the same image to be deployed with different API keys for various environments or different clients, without modifying the code.
3. Application-Specific Configuration and Feature Flags
Environment variables are excellent for controlling application behavior, enabling/disabling features, or adjusting internal parameters without a redeploy.
Scenario: A web application with a "dark mode" feature that can be toggled, and logging verbosity.
Running the container:
# Enable dark mode, set logging to debug
docker run \
-p 8080:80 \
-e ENABLE_DARK_MODE="true" \
-e LOG_LEVEL="DEBUG" \
my_web_app
# Disable dark mode, set logging to info
docker run \
-p 8080:80 \
-e ENABLE_DARK_MODE="false" \
-e LOG_LEVEL="INFO" \
my_web_app
This dynamic control is invaluable for A/B testing, gradual rollouts, or quick fixes.
4. Specifying Proxy Settings
In enterprise networks, containers often need to route traffic through an HTTP proxy. Environment variables are the standard way to configure this.
Running the container with proxy settings:
docker run \
-e HTTP_PROXY="http://proxy.mycompany.com:8080" \
-e HTTPS_PROXY="http://proxy.mycompany.com:8080" \
-e NO_PROXY="localhost,127.0.0.1,myinternal.domain" \
my_application_that_needs_proxy
Most network-aware applications and libraries (like curl, wget, Python's requests, Node.js axios, etc.) automatically pick up these standard proxy environment variables.
5. Passing Configuration for an API Gateway or Microservices Ecosystem
In a microservices architecture, individual services might need to know about the upstream API gateway or other critical infrastructure components. Environment variables are perfect for this.
Scenario: A microservice needs to register itself with a service discovery mechanism or know the URL of the API gateway that exposes it.
Running a microservice:
docker run \
-p 8081:8080 \
-e GATEWAY_BASE_URL="https://api.example.com" \
-e SERVICE_REGISTER_ENDPOINT="http://service-discovery:8500/v1/register" \
-e SERVICE_NAME="user-management-service" \
user_management_service
This allows the user-management-service to dynamically configure its interaction with the broader ecosystem. For example, it might use SERVICE_REGISTER_ENDPOINT to announce its presence to a service mesh or register its exposed APIs. An API gateway like APIPark thrives in such environments, streamlining the management, integration, and deployment of these microservices and their associated APIs. By centralizing the definition and routing of API calls, APIPark can reduce the complexity of individual services needing to know intricate network topology, allowing their environment variables to focus on core business logic rather than elaborate access configurations. It acts as the intelligent layer, making the ecosystem of containerized services more manageable and secure.
These examples demonstrate the fundamental role docker run -e plays in making containers adaptable, configurable, and ready for diverse operational contexts. The ability to inject runtime configuration via environment variables is a cornerstone of modern, cloud-native application development.
Advanced Techniques: Beyond Basic docker run -e
While basic docker run -e KEY=VALUE is sufficient for many scenarios, Docker offers more sophisticated ways to manage environment variables, particularly for larger projects or sensitive data.
1. Using --env-file for Bulk Configuration
Manually typing many -e flags on the command line can be tedious and error-prone. The --env-file option allows you to load environment variables from one or more files. Each line in the file represents a KEY=VALUE pair.
config.env file:
DB_HOST=my-prod-db.example.com
DB_PORT=5432
DB_USER=produser
SERVICE_ENDPOINT=https://api.external.com/v2
Running with --env-file:
docker run \
--env-file ./config.env \
-e ADDITIONAL_VAR="runtime_override" \ # Can combine with -e
my_application
Advantages of --env-file: * Readability: Keeps your docker run command clean and short. * Organization: Group related variables into logical files (e.g., dev.env, prod.env, secrets.env). * Version Control: config.env files can be version-controlled (though not for secrets, as discussed next). * Reusability: Easily share configuration files across multiple docker run commands or Docker Compose files.
Important Note on Order: If you specify multiple --env-file flags, variables defined in later files will override those in earlier files.
# vars_default.env: DB_HOST=default
# vars_prod.env: DB_HOST=production
docker run --env-file vars_default.env --env-file vars_prod.env my_image
# DB_HOST will be 'production'
2. Managing Sensitive Data: The Limitations of -e and Better Alternatives
While docker run -e is good for general configuration, passing truly sensitive information (like production database passwords, root API keys, private cryptographic keys) directly as plain text on the command line or in .env files (especially if committed to VCS) presents significant security risks:
- Command History: Values can be stored in your shell's history file (
.bash_history,.zsh_history). - Process List: On Linux, environment variables are visible in
/proc/<pid>/environfor processes owned by the user runningdocker. Others with root access can see them. - Build Logs: In CI/CD pipelines, if
docker run -ecommands are logged, secrets can leak into logs. docker inspect: The output ofdocker inspect <container_id>will include all environment variables, making them easily retrievable by anyone with access to the Docker daemon.
For production environments, never rely solely on -e for secrets. Instead, utilize dedicated secret management solutions:
a. Docker Secrets (for Docker Swarm)
Docker Secrets are designed for Docker Swarm mode. They allow you to store sensitive data encrypted at rest and transmit it securely to specific services when they need it. Secrets are mounted as files in a tmpfs directory within the container, making them more secure than environment variables.
# Create a secret (e.g., from a file or directly)
echo "my_secure_db_password" | docker secret create db_password_secret -
# Run a service using the secret
docker service create \
--name my-app \
--secret db_password_secret \
my_application
Inside the container, the secret will be available as a file, typically at /run/secrets/db_password_secret. The application reads this file to get the password.
b. Kubernetes Secrets
Similar to Docker Secrets, Kubernetes Secrets are designed for storing sensitive information. They can be mounted as data volumes or exposed as environment variables within a Pod. While exposing as environment variables has some of the same risks as docker run -e (e.g., kubectl describe pod can show them), Kubernetes provides more robust RBAC and encryption at rest capabilities.
apiVersion: v1
kind: Secret
metadata:
name: my-app-secret
type: Opaque
data:
DB_PASSWORD: <base64_encoded_password> # e.g., echo "password123" | base64
---
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app
image: my_application
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-secret
key: DB_PASSWORD
c. External Secret Management Tools (Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager)
For highly secure and scalable secret management, integrating with dedicated external tools is the gold standard. These services centralize secret storage, provide auditing, rotation, and fine-grained access control. Applications typically authenticate with these services at startup to fetch the necessary secrets.
Process: 1. Store secrets in Vault/AWS Secrets Manager. 2. Provide the container with minimal credentials (e.g., an IAM role, a Vault token) via an environment variable or volume mount. 3. The application inside the container uses these minimal credentials to authenticate with the secret manager and retrieve the actual sensitive values.
This approach ensures secrets are never passed directly to the container through insecure channels and are dynamically fetched at runtime, offering the highest level of security.
3. Dynamic Environment Variables and Entrypoint Scripts
Sometimes, environment variables need to be generated or processed dynamically at container startup, based on other variables or runtime conditions. This is where custom entrypoint scripts excel.
Scenario: A configuration file needs to be dynamically generated using environment variables before the main application starts.
entrypoint.sh:
#!/bin/sh
set -e
# Generate config file using environment variables
echo "database_url = ${DB_HOST}:${DB_PORT}/${DB_NAME}" > /app/config.ini
echo "api_key = ${EXTERNAL_API_KEY}" >> /app/config.ini
# Execute the main application command
exec "$@"
Dockerfile (snippet):
# ...
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/techblog/en/usr/local/bin/entrypoint.sh"]
CMD ["python", "app.py"] # This becomes "$@" in entrypoint.sh
Running the container:
docker run \
-e DB_HOST="prod-db" \
-e DB_PORT="5432" \
-e DB_NAME="prod_app" \
-e EXTERNAL_API_KEY="super_secret_key" \
my_application
The entrypoint.sh script will run first, generate config.ini, and then launch python app.py. This pattern is incredibly powerful for complex initialization logic.
4. --env-add (Experimental/Less Common)
Docker also has a --env-add flag, primarily used with docker commit and docker diff in experimental contexts, but less commonly with docker run. Its purpose is more about managing environments of images rather than directly influencing a running container. For docker run, -e and --env-file are the standard mechanisms.
By mastering these advanced techniques, you can move beyond basic container configuration to building truly robust, secure, and dynamically configurable containerized applications ready for enterprise-grade deployments and complex microservices ecosystems.
APIPark is a high-performance AI gateway that allows you to securely access the most comprehensive LLM APIs globally on the APIPark platform, including OpenAI, Anthropic, Mistral, Llama2, Google Gemini, and more.Try APIPark now! 👇👇👇
docker run -e in Orchestration Contexts
While docker run -e is fundamental for single-container execution, its principles extend seamlessly into container orchestration platforms. These platforms provide their own syntax for defining environment variables, abstracting the raw docker run command.
1. Docker Compose
Docker Compose is a tool for defining and running multi-container Docker applications. It uses a YAML file (typically docker-compose.yml) to configure application services. Environment variables are a first-class citizen in Compose.
docker-compose.yml Example:
version: '3.8'
services:
web:
image: my_nodejs_app
ports:
- "3000:3000"
environment:
DB_HOST: "db" # 'db' is the service name, Compose's internal DNS handles resolution
DB_PORT: "5432"
API_ENDPOINT: "https://external-api.com"
# DB_PASS: "my_secret_password" # Not recommended for secrets in YAML
env_file:
- .env # Load variables from a .env file relative to the docker-compose.yml
depends_on:
- db
db:
image: postgres:13
environment:
POSTGRES_DB: my_app_db
POSTGRES_USER: app_user
POSTGRES_PASSWORD: my_db_password # Not recommended for production
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Key takeaways for Docker Compose: * environment key: Directly define KEY: VALUE pairs within a service definition. This is equivalent to docker run -e. * env_file key: Specify one or more .env files to load variables, similar to docker run --env-file. This is often preferred for managing multiple variables or environment-specific configurations (e.g., env_file: - .env.dev, env_file: - .env.prod). * .env file (implicit): Compose also automatically looks for a .env file in the directory where docker-compose.yml is located. Variables defined here are available for substitution in the docker-compose.yml itself (e.g., image: ${APP_IMAGE}) and can be passed to services. * Precedence: Environment variables from the host where docker compose up is run override env_file variables, which in turn override environment variables defined directly in the docker-compose.yml.
2. Kubernetes
Kubernetes, the dominant container orchestration platform, offers flexible ways to inject environment variables into Pods. These methods integrate with Kubernetes's robust configuration and secret management capabilities.
a. Direct env in Pod Definition:
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app
image: my_application
env:
- name: DB_HOST
value: "kubernetes-db-service"
- name: API_KEY
value: "my_public_api_key" # Not for secrets
- name: APP_MODE
value: "production"
b. valueFrom for Dynamic Values: This allows environment variables to be sourced from other Kubernetes objects, enhancing flexibility and security.
fieldRef: From the Pod's own metadata (e.g., Pod name, namespace, IP address).yaml apiVersion: v1 kind: Pod metadata: name: my-app-pod spec: containers: - name: my-app image: my_application env: - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespaceresourceFieldRef: From the container's resource limits/requests.
secretKeyRef: From a Secret (the recommended way for sensitive data).```yaml
secret.yaml
apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: DB_PASSWORD:# e.g., echo "mysecret" | base64
pod.yaml
apiVersion: v1 kind: Pod metadata: name: my-app-pod spec: containers: - name: my-app image: my_application env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-secret key: DB_PASSWORD ```
configMapKeyRef: From a ConfigMap (for non-sensitive configuration).```yaml
configmap.yaml
apiVersion: v1 kind: ConfigMap metadata: name: app-config data: APP_MODE: "production" LOG_LEVEL: "INFO" API_ENDPOINT: "https://prod.api.example.com"
pod.yaml
apiVersion: v1 kind: Pod metadata: name: my-app-pod spec: containers: - name: my-app image: my_application env: - name: APP_MODE valueFrom: configMapKeyRef: name: app-config key: APP_MODE - name: LOG_LEVEL valueFrom: configMapKeyRef: name: app-config key: LOG_LEVEL ```
c. envFrom for Bulk Loading: This allows you to inject all key-value pairs from a ConfigMap or Secret as environment variables, rather than specifying each one individually.
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
spec:
containers:
- name: my-app
image: my_application
envFrom:
- configMapRef:
name: app-config # All keys from 'app-config' ConfigMap become env vars
- secretRef:
name: db-secret # All keys from 'db-secret' Secret become env vars
Key takeaways for Kubernetes: * Kubernetes provides sophisticated mechanisms (ConfigMaps, Secrets, valueFrom, envFrom) to manage environment variables, moving beyond simple inline declarations. * Secrets are the preferred method for sensitive data, ideally mounted as files rather than environment variables, due to security considerations. * The tight integration with the Kubernetes API allows for highly dynamic and declarative configuration management.
In essence, while docker run -e is the elementary building block, orchestration tools provide higher-level abstractions and enhanced features to manage environment variables at scale, ensuring consistency, security, and operational efficiency across complex distributed applications.
Security Considerations: The Double-Edged Sword of Environment Variables
Environment variables are incredibly powerful, but their use, especially for sensitive data, demands a rigorous understanding of security implications. As the saying goes, "with great power comes great responsibility."
Risks of Using docker run -e for Secrets
As briefly mentioned before, passing sensitive data directly via docker run -e has several vulnerabilities:
- Shell History Exposure: When you type
docker run -e DB_PASS="secret"in your terminal, this command is typically saved in your shell's history file (.bash_history,.zsh_history). Anyone gaining access to your user account on that machine can easily retrieve these secrets. - Process List (ps) Exposure: On a Linux host, environment variables for a running process can be inspected by examining
/proc/<pid>/environ. Whiledocker runitself might be short-lived, the main process inside the container will inherit these variables. Any user with root privileges on the host (or even non-root users with appropriate permissions) cancat /proc/<container_pid>/environand discover your secrets. - Docker Inspect Exposure: The output of
docker inspect <container_id_or_name>includes all environment variables passed to the container. If an attacker gains access to the Docker daemon (e.g., via a compromised user with Docker group membership), they can easily extract all secrets from running containers. - Logging and CI/CD Pipeline Leaks: In automated environments,
docker runcommands are often logged. If secrets are part of these commands, they can inadvertently end up in build logs, deployment logs, or other diagnostic output, which might be less secure than your source code repository. - Ephemeral Secrets vs. Persistent Logs: Even if a container is short-lived, the secrets persist in logs or inspect output.
Mitigation: The golden rule is: Do not put production secrets directly into docker run -e or version-controlled .env files.
Best Practices for Secure Environment Variable Management
- Use Dedicated Secret Management Solutions:
- Docker Secrets (for Swarm): As described previously, this mounts secrets as files in
tmpfs, making them invisible topsanddocker inspect(they are not env vars). Your application reads from these files. - Kubernetes Secrets: Similarly, prefer mounting secrets as volumes (files) over injecting them as environment variables. If you must use them as environment variables, rely on Kubernetes's RBAC and encryption features, but be aware of the
kubectl describe podvisibility. - External Vaults (HashiCorp Vault, cloud secret managers like AWS Secrets Manager, Azure Key Vault, GCP Secret Manager): These are the most robust solutions. They provide centralized management, auditing, rotation, and fine-grained access control. Containers authenticate with the vault to retrieve secrets at runtime.
- Docker Secrets (for Swarm): As described previously, this mounts secrets as files in
- Minimize Scope and Lifetime:
- Only provide the secrets a container absolutely needs. Avoid giving a container access to credentials for other services it doesn't interact with.
- For temporary operations, generate short-lived credentials or tokens if possible, reducing the window of exposure.
- Read Secrets as Files (Preferred):
- Whenever possible, have your application read secrets from files mounted into the container (e.g.,
/run/secrets/db_password). This is how Docker Secrets and Kubernetes Secrets (when mounted as volumes) work. This avoids thepsanddocker inspectexposure of environment variables.
- Whenever possible, have your application read secrets from files mounted into the container (e.g.,
- Least Privilege Principle:
- Ensure that the user running the Docker daemon and the user executing
docker run(and thus potentially having access to environment variables) has the minimum necessary privileges. - Within the container, run processes as a non-root user (
USERinstruction in Dockerfile) to limit potential damage if the application is compromised.
- Ensure that the user running the Docker daemon and the user executing
- Audit and Monitor:
- Regularly audit your Docker deployments and CI/CD pipelines to ensure secrets are not inadvertently leaked into logs or configuration files.
- Implement monitoring and alerting for access to your secret management systems.
A Note on Public APIs and Non-Sensitive Keys
Not all keys are secrets. If an API key is for a public API that has rate limiting but no financial implications or access to sensitive user data, then passing it via docker run -e might be acceptable, particularly if the key is meant to be public anyway (e.g., a map API key for client-side use that you just want to track server-side). However, even in these cases, it's a good practice to consider secret management for consistency and to prepare for future changes in the API's sensitivity.
In the context of API gateways, like APIPark, security considerations around API keys and service credentials are paramount. APIPark helps centralize API key management, authentication, and authorization for numerous backend APIs and microservices. While individual containerized services (configured by docker run -e) might still need credentials to talk to APIPark or other internal services, APIPark itself helps abstract away and secure the much broader set of APIs it manages, reducing the surface area for direct secret exposure within application containers. It provides an additional layer of security and management, complementing best practices for individual container configuration.
Ultimately, secure environment variable management is about understanding the threat model and choosing the appropriate tool for the job. For anything truly sensitive, move beyond simple docker run -e and embrace dedicated secret management solutions.
Troubleshooting Common docker run -e Issues
Even with a clear understanding, issues can arise when working with environment variables. Here are some common problems and their solutions:
1. Variable Not Found Inside Container
Symptom: Your application reports that an expected environment variable is missing or empty.
Possible Causes & Solutions: * Typo in variable name: Double-check the spelling and case of the variable name (environment variables are case-sensitive). MY_VAR is different from my_var. * Incorrect docker run -e syntax: Ensure you're using KEY=VALUE format correctly. * Precedence issues: A variable might be defined, but a higher-precedence source (e.g., ENV in Dockerfile) is overriding it. Use docker inspect <container_id> and look at the Config.Env section to see what variables Docker thinks are set. Run env inside the container to see what the application process actually sees. * Application isn't reading variables correctly: Verify your application code uses the correct method to read environment variables (e.g., os.environ.get("VAR_NAME") in Python). * Shell expansion on host: If your VALUE contains special characters (like $), your host shell might expand them before passing them to Docker. Always quote values (e.g., docker run -e 'VAR="value with spaces"'). * Entrypoint/CMD issue: If your container uses a custom ENTRYPOINT script, ensure it's not inadvertently clearing or modifying the environment before launching your main application. Use set -e in your script to catch errors early.
2. Variable Value is Incorrect or Unexpected
Symptom: The application starts, but behaves unexpectedly, suggesting an incorrect configuration value.
Possible Causes & Solutions: * Trailing spaces or special characters: Ensure values are trimmed and correctly escaped or quoted. VAR="value " (with a space) is different from VAR="value". * Misleading defaults: If your Dockerfile has ENV instructions, ensure they are appropriate. If you are overriding them with -e, confirm the -e value is correct. * Incorrect --env-file content: Check the .env file for typos, empty lines, or invalid formats. Remember that lines starting with # are comments. * Order of --env-file: If using multiple --env-file flags, remember that later files override earlier ones. * Shell expansion again: If a variable value itself contains shell variables that you want the container's shell to expand, ensure the host shell doesn't expand them first. For example, docker run -e 'VAR="$ANOTHER_VAR"' would pass "$ANOTHER_VAR" literally, while docker run -e VAR=\$ANOTHER_VAR might pass $ANOTHER_VAR for the container to expand. Be precise with quoting.
3. Debugging Environment Variables
When troubleshooting, these commands are invaluable:
docker inspect <container_id>: This command provides a wealth of information about a running or stopped container, including all environment variables Docker is aware of for that container in theConfig.EnvandConfig.Entrypointsections. This is your first stop for checking what Docker sees.bash docker inspect my_container | grep -A 5 -B 5 "Env"docker exec <container_id> env: This allows you to execute theenvcommand inside a running container, showing the exact environment variables visible to new processes started within that container. This is crucial for verifying what your application process actually receives.bash docker exec my_container envdocker logs <container_id>: If your application logs its startup configuration (which is good practice for debugging), inspect the logs for clues about how it's interpreting environment variables.
Temporary ENTRYPOINT/CMD override: For difficult cases, you can temporarily override the container's ENTRYPOINT or CMD to a simple shell and then interactively check the environment.```bash docker run -it --entrypoint sh my_image
Inside the container:
$ env $ echo $MY_VARIABLE $ exit ``` This allows you to explore the container's environment before your actual application code runs, helping to isolate if the issue is with variable injection or application interpretation.
By systematically applying these diagnostic steps, you can quickly pinpoint and resolve issues related to environment variables, ensuring your containerized applications are configured precisely as intended.
Comparison with Other Configuration Methods
While environment variables are a cornerstone of Docker configuration, they are not the only method. Understanding their strengths and weaknesses relative to other approaches helps in choosing the right tool for each configuration need.
1. Configuration Files Mounted via Volumes
Mechanism: External configuration files (e.g., application.properties, nginx.conf, settings.py) are stored on the host or in a Docker volume, and then mounted into the container at a specific path.
docker run -v /path/to/host/config/nginx.conf:/etc/nginx/nginx.conf:ro my_nginx_image
Pros: * Richness: Configuration files can support complex data structures (JSON, YAML, XML), comments, and multi-line values, which are difficult or impossible with simple environment variables. * Application Familiarity: Many legacy applications are designed to read configuration from files, making this a natural integration point. * Security (for non-secrets): If files are managed securely on the host (e.g., using proper file permissions), they can be reasonably safe for non-sensitive data. * Hot Reloading: Some applications can detect changes in mounted configuration files and reload their settings without a restart.
Cons: * Immutability Challenge: If you modify a mounted file on the host, the container's configuration changes. While flexible, this can complicate ensuring consistency across deployments or debugging. * Portability: Requires managing the file on the host, which can be less portable than pure environment variable injection, especially across different host types or cloud environments. * Sensitive Data: Storing secrets directly in plain text files (even if mounted) still carries risks similar to .env files, though less susceptible to ps or docker inspect for the environment variables themselves. Docker Secrets and Kubernetes Secrets often implement this pattern by mounting secrets as files.
When to Use: * For complex, hierarchical configurations. * When integrating with legacy applications expecting file-based configuration. * For applications that can hot-reload configurations from files. * For nginx.conf, httpd.conf, application-specific JSON/YAML config.
2. Build-Time Arguments (ARG in Dockerfile, --build-arg with docker build)
Mechanism: Variables passed to the docker build command. They are available only during the build process and are not persisted in the final image's runtime environment (unless explicitly copied to an ENV variable or used to configure a file).
# Dockerfile
ARG BUILD_VERSION
RUN echo "Building version: $BUILD_VERSION" > /app/version.txt
ENV APP_VERSION=$BUILD_VERSION # Persist if needed
docker build --build-arg BUILD_VERSION=1.0.0 -t my_app .
Pros: * Image Customization: Allows minor variations in image building without separate Dockerfiles (e.g., different dependency versions, build flags). * Reduced Image Layer Size: Unlike ENV, ARG values are not automatically persisted in the final image, potentially leading to slightly smaller images if not captured by ENV.
Cons: * Not for Runtime: Cannot be changed after the image is built. Not suitable for runtime configuration. * Security Risk: Like -e, --build-arg values can appear in build logs, making them unsafe for secrets. Docker has a DOCKER_BUILDKIT=1 docker build --secret feature for this, but it's more advanced.
When to Use: * To customize build-time parameters (e.g., base image tag, proxy settings for apt or npm during build, version numbers baked into metadata). * Never for runtime configuration or secrets.
3. Baked-in Configuration (Hardcoding or Defaulting in Image)
Mechanism: Configuration values are directly hardcoded into the application code or the Dockerfile (ENV instructions) without any runtime override mechanism.
# Dockerfile
ENV DEFAULT_LOG_LEVEL="INFO"
# app.py
LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO") # Using ENV as a default
Pros: * Simplicity: Easiest to implement for truly static, non-changing configuration. * Reliability: Guarantees a default behavior if no external configuration is provided.
Cons: * Lack of Flexibility: Requires rebuilding the image to change any configuration. * Immutability Violation: Contradicts the "build once, run anywhere" principle if environment-specific details are hardcoded. * Security Risk: Hardcoding sensitive data is a major anti-pattern.
When to Use: * For truly immutable application defaults that should rarely change and are not environment-specific (e.g., default port, internal service names within a tightly coupled system if those names are stable). * As fallback defaults when environment variables might not be present.
Summary Table: Configuration Methods Comparison
| Feature/Method | docker run -e (Env Vars) |
Mounted Config Files | Build-Time Args | Baked-in Configuration |
|---|---|---|---|---|
| Runtime Config | Excellent | Excellent | No | No |
| Build-Time Config | No | No | Yes | Yes |
| Security (Secrets) | Poor (use alternatives) | Poor (use alternatives) | Poor | Very Poor |
| Complexity | Low | Medium | Low | Very Low |
| Flexibility | High | High | Low | Very Low |
| Data Structure | Simple KEY=VALUE strings |
Complex (JSON, YAML) | Simple KEY=VALUE strings |
Depends on implementation |
| Orchestration Tool Support | Excellent (environment, env) |
Good (volumes) |
N/A | N/A |
| Portability | High | Medium | N/A | High |
This table highlights that while docker run -e offers fantastic runtime flexibility and simplicity for string-based configurations, it's part of a broader toolkit. For complex data structures, files are better; for build-time variations, ARG is appropriate; and for true secrets, dedicated secret management solutions are paramount. A robust container strategy often involves a combination of these methods, carefully chosen for each specific configuration need.
The Role of Environment Variables in Microservices Architectures
In the era of microservices, applications are decomposed into smaller, independently deployable services that communicate over a network, often via APIs. Environment variables play an absolutely critical role in making these distributed systems function effectively, providing the glue for dynamic configuration.
1. Service Discovery and Communication
Microservices rarely hardcode the network locations of other services they depend on. Instead, they discover them dynamically. Environment variables are a common mechanism to inject these discovery parameters:
- Service Registrar Endpoints: A service might be configured with
SERVICE_REGISTRY_URL="http://consul:8500"to register itself. - Dependent Service Endpoints: Instead of relying solely on service discovery, a service might be configured with
USER_SERVICE_ENDPOINT="http://user-service:8080"for direct communication within a well-defined network. - Message Broker URIs:
KAFKA_BROKER_LIST="kafka1:9092,kafka2:9092"allows a service to connect to its message queue.
This decoupling means that the user-management-service doesn't need to be rebuilt if the user-service changes its network location; only the environment variable needs to be updated.
2. Configuration for API Gateways
An API gateway is a central entry point for clients accessing microservices. It handles routing, authentication, rate limiting, and other cross-cutting concerns. Both the API gateway itself and the backend services it exposes rely heavily on environment variables:
- Gateway Configuration: The API gateway (e.g., Nginx, Kong, Zuul, or a specialized solution like APIPark) uses environment variables to configure:
- Which backend services to route to.
- Authentication providers.
- SSL certificate paths.
- Caching settings.
- Monitoring endpoints. For instance,
API_TARGET_URL_USERS="http://user-service:8080".
- Backend Service Configuration: Microservices behind the API gateway might use environment variables to:
- Know their external-facing API gateway URL (e.g., for generating public URLs in responses).
- Configure any specific authentication tokens or headers required to communicate with the API gateway if the gateway itself acts as an upstream dependency.
- Set their internal API routes and versions.
This dynamic configuration via environment variables is crucial for managing the complex network topology and security requirements inherent in a microservices ecosystem.
3. Environment-Specific Customization
Microservices environments (development, staging, production) often have distinct requirements:
- Logging Levels:
LOG_LEVEL="DEBUG"in dev,LOG_LEVEL="INFO"in prod. - Database Connections: Different database instances for each environment.
- Feature Toggles: Enable experimental features in a staging environment before rolling out to production.
- Resource Limits/Requests: While often configured by orchestration, application services can use environment variables to fine-tune their internal resource usage based on the allocated limits.
Environment variables allow a single microservice container image to be promoted through various stages of a CI/CD pipeline, adapting its behavior at each stage without modification.
4. Injecting Credentials and Tokens (Securely)
Each microservice might need various credentials: database passwords, API keys for external services, OAuth tokens, internal service-to-service authentication keys. While direct -e for secrets is discouraged, the orchestration layers (like Kubernetes Secrets or Docker Secrets) that often underpin microservices architectures use environment variables (or mounted files derived from them) to inject these sensitive values securely.
For example, a product-service might need: * DB_PASSWORD (from Kubernetes Secret mounted as file) * INVENTORY_SERVICE_API_KEY (from an external secret manager) * STRIPE_PUBLIC_KEY (non-sensitive, from ConfigMap or direct env var)
This externalization is key to managing the security posture of dozens or hundreds of independent services without hardcoding.
5. Centralized API Management with APIPark
In a landscape teeming with microservices and diverse APIs, an API gateway like APIPark becomes an indispensable tool. APIPark, an open-source AI gateway and API management platform, excels at standardizing, securing, and orchestrating the interactions between these services.
Individual microservices, launched with docker run -e for their internal configurations, can seamlessly integrate with APIPark. For example, a containerized service might use an environment variable to define its base path, which APIPark then uses for routing: SERVICE_BASE_PATH="/techblog/en/v1/users". APIPark then handles the external exposure, rate limiting, and authentication for https://your-api-gateway.com/v1/users, abstracting the backend service's actual network location.
APIPark’s ability to unify API formats, encapsulate prompts into REST APIs, and provide end-to-end API lifecycle management directly benefits from and simplifies the environment variable configuration within individual microservices. By handling the complex aspects of API governance at the gateway level, it allows individual containerized applications to focus on their core business logic, configured efficiently via a streamlined set of environment variables. This creates a powerful synergy: docker run -e handles granular container configuration, while APIPark manages the macroscopic API landscape, enhancing efficiency, security, and scalability for the entire microservices ecosystem.
In summary, environment variables are not just a convenient feature; they are a foundational pillar of microservices architecture, enabling the dynamic configuration, flexible deployment, and secure operation of highly distributed applications.
Future Trends and Evolution of Container Configuration
The landscape of containerization is constantly evolving, and with it, the methods and best practices for configuration. While docker run -e will remain a fundamental tool, we can observe several trends influencing its future and complementary technologies.
1. Shift Towards Declarative Configuration
Modern orchestration platforms like Kubernetes heavily emphasize declarative configuration. Instead of imperative commands like docker run -e, you define the desired state of your application (including its environment variables) in YAML files. The orchestrator then works to achieve and maintain that state. This trend will continue to gain traction, abstracting away the raw docker run command for most production deployments.
2. Enhanced Secret Management Integration
The awareness of the security risks associated with environment variables for secrets has led to a push for tighter integration with dedicated secret management solutions. We can expect: * More native integrations: Orchestrators and CI/CD tools will offer more seamless, out-of-the-box integrations with cloud secret managers and HashiCorp Vault. * Runtime fetching: A move away from static secret injection towards applications dynamically fetching secrets from a vault at startup, using short-lived tokens. * Confidential Computing: Emerging technologies like confidential computing aim to protect data in use, which could further secure environment variables and other sensitive data even within memory.
3. Operator Pattern for Application Configuration
In Kubernetes, the "Operator" pattern allows custom controllers to manage complex applications. An Operator can dynamically adjust application configurations (including environment variables) based on external conditions or custom resource definitions (CRDs). This enables highly sophisticated, application-specific configuration logic that goes beyond simple key-value pairs.
4. Dynamic Configuration Management Systems
Systems like HashiCorp Consul (with Consul Template), etcd, and various cloud-native service meshes offer ways to dynamically update configurations without restarting containers. While environment variables are often the initial configuration, applications might then subscribe to these dynamic systems for live updates. This reduces the reliance on re-deploying containers just to change a configuration.
5. Standardized Configuration Schemas
As applications become more complex, simply passing opaque KEY=VALUE pairs can lead to configuration sprawl and errors. There's a growing need for standardized configuration schemas (e.g., using JSON Schema) to validate and document application configurations, making them more robust and maintainable.
6. Environmental Context Injection (Non-Variables)
Beyond simple environment variables, containers are increasingly receiving more contextual information through other means: * Service Mesh Sidecars: Injecting proxies (like Envoy in Istio) as sidecar containers, which handle all network traffic, effectively externalizing networking configuration from the application's environment variables. * Ephemeral Containers: For debugging, ephemeral containers can be injected into a Pod to inspect a running container's environment without restarting it. * Workload Identity: Cloud providers offer workload identity solutions (e.g., AWS IAM Roles for Service Accounts, Azure AD Workload Identity) that provide fine-grained permissions to containers without needing to inject static credentials via environment variables.
Conclusion on Evolution
While the docker run -e command serves as a foundational primitive for injecting configuration, its role will increasingly be encapsulated within higher-level abstractions provided by orchestration platforms and specialized tools. The focus will shift towards more secure, dynamic, and declarative configuration management, where environment variables remain a critical low-level mechanism but are managed through more sophisticated patterns. Understanding docker run -e is and will remain essential for debugging, local development, and grasping the underlying mechanics, even as enterprise deployments move towards more advanced solutions.
Conclusion: The Enduring Importance of docker run -e
Our journey through the world of docker run -e has revealed it to be far more than a simple command-line flag. It is a cornerstone of modern containerization, a powerful lever that injects dynamism, flexibility, and operational context into static Docker images. From its basic KEY=VALUE syntax to its intricate precedence rules, and from its indispensable role in database connections and API configurations to its crucial function in microservices ecosystems and API gateways, environment variables are the lifeblood of adaptable containerized applications.
We've explored how docker run -e enables the "build once, run anywhere" philosophy, allowing a single image to seamlessly traverse development, staging, and production environments. We delved into practical examples, demonstrating its utility for everything from configuring logging levels to managing external API keys. We also highlighted advanced techniques, such as using --env-file for bulk configuration and the critical need for robust secret management solutions—Docker Secrets, Kubernetes Secrets, and external vaults—to mitigate the inherent security risks of passing sensitive data via plain text environment variables.
The integration of docker run -e with orchestration platforms like Docker Compose and Kubernetes underscores its fundamental nature. These platforms provide higher-level abstractions, translating declarative configurations into the underlying environment variable injections that containers ultimately consume. In complex microservices landscapes, environment variables serve as the conduits through which services discover each other, configure their interactions with API gateways (like APIPark), and adapt to environment-specific demands. APIPark, as an open-source AI gateway and API management platform, further enhances this ecosystem by providing a unified layer for managing, securing, and deploying AI and REST services, complementing the granular configuration offered by docker run -e at the container level.
While the future promises even more sophisticated configuration patterns and enhanced security mechanisms, the fundamental concept of environment variables as a runtime configuration injection method will remain central. Mastering docker run -e is not just about memorizing a command; it's about internalizing a core principle of cloud-native development: externalizing configuration for flexibility, security, and scalability. With this comprehensive understanding, you are now equipped to leverage the full potential of environment variables, building more resilient, adaptable, and intelligent containerized applications ready for any challenge the modern infrastructure landscape may present.
Frequently Asked Questions (FAQs)
1. What is the primary difference between ENV in a Dockerfile and docker run -e? The primary difference lies in their timing and mutability. ENV instructions in a Dockerfile set environment variables during the image build process, baking them into the image. These act as default values. docker run -e, on the other hand, injects environment variables at container runtime, allowing you to override any ENV values defined in the Dockerfile. This makes docker run -e ideal for environment-specific configurations without rebuilding the image.
2. Is it safe to pass sensitive data like API keys or database passwords using docker run -e? Generally, no, it is not safe for production environments. Passing sensitive data directly via docker run -e can expose secrets in shell histories, process lists (/proc/<pid>/environ), and docker inspect output. For production and highly sensitive data, it's strongly recommended to use dedicated secret management solutions like Docker Secrets (for Swarm), Kubernetes Secrets (preferably mounted as files), or external secret vaults (e.g., HashiCorp Vault, AWS Secrets Manager).
3. How can I pass multiple environment variables to a Docker container efficiently? You have two main methods: a) Use multiple -e flags: docker run -e VAR1=VAL1 -e VAR2=VAL2 my_image. This is suitable for a few variables. b) Use the --env-file option: Create a file (e.g., config.env) with KEY=VALUE pairs on separate lines, then run docker run --env-file ./config.env my_image. This is ideal for managing many variables and keeping your docker run command clean.
4. What is the order of precedence for environment variables in Docker? Docker follows a specific order of precedence, where later declarations override earlier ones: 1. Variables inherited from the base image (lowest precedence). 2. ENV instructions in the Dockerfile. 3. Variables loaded from --env-file (if multiple files, later files override earlier ones). 4. docker run -e flags (highest precedence).
5. How do orchestration tools like Docker Compose and Kubernetes handle environment variables, and how does it relate to docker run -e? Orchestration tools provide their own declarative ways to define environment variables, abstracting the raw docker run -e command. * Docker Compose: Uses an environment key in docker-compose.yml for direct KEY: VALUE pairs and an env_file key to load variables from files. * Kubernetes: Uses an env array within a Pod definition, allowing direct name: value pairs, or valueFrom references to ConfigMaps or Secrets. It also has envFrom to load all variables from a ConfigMap or Secret. These tools essentially translate your high-level configuration into the underlying environment variable mechanisms understood by the Docker engine, akin to what docker run -e does for a single container.
🚀You can securely and efficiently call the OpenAI API on APIPark in just two steps:
Step 1: Deploy the APIPark AI gateway in 5 minutes.
APIPark is developed based on Golang, offering strong product performance and low development and maintenance costs. You can deploy APIPark with a single command line.
curl -sSO https://download.apipark.com/install/quick-start.sh; bash quick-start.sh

In my experience, you can see the successful deployment interface within 5 to 10 minutes. Then, you can log in to APIPark using your account.

Step 2: Call the OpenAI API.
