Containers Advanced

Docker Compose .env Variables Empty on Alpine Linux: A Troubleshooting Guide

Resolve `docker-compose` `.env` file variables not loading correctly on Alpine Linux hosts, leading to empty values in containers. Understand shell compatibility and common pitfalls.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve `docker-compose` `.env` file variables not loading correctly on Alpine Linux hosts, leading to empty values in containers. Understand shell compatibility and common pitfalls.

When deploying Docker Compose applications on an Alpine Linux host, a common and frustrating issue is the .env file variables failing to load into your containers, resulting in empty or undefined values where you expect configuration. This can lead to application failures, misconfigurations, or unexpected behavior. This guide delves into the specific intricacies of Alpine Linux that often cause this problem and provides a robust, step-by-step resolution.

Symptom & Error Signature

The primary symptom is your application or service within a Docker container behaving as if certain environment variables are unset or empty, despite a seemingly correct .env file being present alongside your docker-compose.yml.

You might observe:

  • Application Logs: Error messages indicating missing configuration values, null references, or fallbacks to default settings.

    2026-10-22 10:30:05 [ERROR] Application failed to start: Database host is not defined. (Expected value for DB_HOST)
    2026-10-22 10:30:05 [INFO] Using default port 80 for service, variable APP_PORT was empty.
    
  • docker inspect Output: When inspecting the running container, the Config.Env array does not contain the expected variables from your .env file.

    # Example: Check environment variables for a service named 'webapp'
    docker ps -aqf "name=webapp" | xargs docker inspect --format '{{json .Config.Env}}' | jq .
    

    You might see:

    [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      "HOSTNAME=a1b2c3d4e5f6",
      "DB_HOST=",  # Empty! Expected a value from .env
      "APP_PORT="  # Empty!
    ]
    
  • docker-compose config Output: While docker-compose config typically shows how env_file is referenced, it won't always show the resolved values. However, it can confirm if the env_file directive itself is being processed.

    docker-compose config
    

    If your docker-compose.yml uses env_file: .env, the output will reflect that, but it won't explicitly show the loaded values unless those are also explicitly defined in the environment section or through shell expansion.

Root Cause Analysis

The "Docker compose env file variables not loaded values empty on Alpine Linux" issue often stems from a combination of factors specific to Alpine's minimal environment, particularly its default shell and how docker-compose interacts with it.

  1. BusyBox ash vs. bash Incompatibility:

    • Alpine Linux uses BusyBox ash as its default shell, not bash. While ash is highly efficient and lightweight, it lacks many features and syntax sugar found in bash.
    • Many developers, accustomed to bash, write .env files that include bash-specific constructs such as export VAR=VALUE, complex variable expansions (VAR=${ANOTHER_VAR:-default}), comments on the same line as variables (VAR=VALUE # comment), or arithmetic expansions.
    • The internal mechanisms docker-compose uses to parse .env files can sometimes involve invoking a shell, or its Python-based parser might itself be sensitive to non-standard .env formats common in bash environments. ash will either ignore these constructs, misinterpret them, or throw errors, leading to variables being unset.
  2. docker-compose Installation Method on Alpine:

    • On Alpine, docker-compose is often installed via apk add py3-docker-compose (or similar for Python 2). This installs the Python package.
    • While generally functional, differences can arise in how this Python package interacts with the underlying system shell for certain operations, potentially influencing .env parsing, especially if the file contains shell-like syntax.
    • The official docker-compose standalone binary is often a more robust choice, but its installation on Alpine might require more manual steps to ensure Python dependencies are met.
  3. Strict .env File Parsing:

    • The docker-compose .env parser expects a very specific format: KEY=VALUE on each line, with no export, no internal comments, and values usually unquoted unless they contain spaces or special characters. Deviations can cause parsing failures.
  4. Incorrect File Path or Permissions:

    • A classic issue: the .env file isn't located in the same directory as docker-compose.yml, or it has incorrect permissions preventing docker-compose from reading it.
  5. Conflicting Environment Variables:

    • If a variable is already set in the shell where docker-compose is executed (e.g., via ~/.profile or /etc/profile.d/), it will often take precedence over the variable defined in .env.
  6. docker-compose.yml Configuration:

    • The docker-compose.yml file might be missing the env_file: ./.env directive or referencing an incorrect path. Alternatively, it might be explicitly overriding .env values in the environment: section.

Step-by-Step Resolution

Follow these steps to diagnose and resolve .env variable loading issues on Alpine Linux.

1. Standardize and Simplify Your .env File Syntax

The most common culprit on Alpine is an .env file that's not strictly compatible with ash or the docker-compose parser's expectations.

  • Rule 1: One KEY=VALUE per line.
  • Rule 2: No export keyword.
  • Rule 3: No inline comments.
  • Rule 4: Values should generally be unquoted unless they contain spaces or special characters, in which case use single quotes for literal values or double quotes for shell-like expansion (though avoid complex expansions for maximum compatibility).

Incorrect .env (Common Bash-isms):

# Database configuration
export DB_HOST=localhost # The database server
DB_USER=${USER:-appuser} # Default to appuser if USER is not set
DB_PASSWORD="my secure password with spaces"
APP_PORT=8080 # App listening port

Correct .env (Strict and ash-compatible):

# Database configuration
DB_HOST=localhost
DB_USER=appuser
DB_PASSWORD="my secure password with spaces"
APP_PORT=8080

Complex variable expansion (e.g., VAR=${ANOTHER_VAR:-default}) within the .env file should be avoided. While docker-compose can handle some forms of variable interpolation from the shell environment into the docker-compose.yml, direct shell-like expansion within .env itself is best kept minimal.

You can test how ash would interpret your .env file by manually sourcing it:

# From the directory containing your .env file
apk add --no-cache bash # Temporarily install bash for comparison
sh -c 'source ./.env && env' | grep DB_HOST # Test with ash
bash -c 'source ./.env && env' | grep DB_HOST # Test with bash (for comparison)

2. Verify docker-compose.yml Configuration

Ensure your docker-compose.yml correctly references the .env file and that no conflicting environment declarations are overriding it.

  • env_file Directive: Make sure your docker-compose.yml explicitly uses the env_file directive.

    # docker-compose.yml
    version: '3.8'
    
    services:
      webapp:
        build: .
        ports:
          - "8080:8080"
        env_file:
          - ./.env # Correctly reference the .env file
        # environment: # Avoid explicitly redefining variables here if they are in .env
        #   DB_HOST: production-db # This would override DB_HOST from .env
    
  • Check for Overrides: Variables explicitly defined in the environment section of docker-compose.yml take precedence over those in the .env file. Ensure you're not unintentionally overriding values.

    docker-compose processes environment variables in a specific order of precedence:

    1. Variables passed directly via docker-compose run -e or docker-compose up -e.
    2. Variables defined in the shell environment where docker-compose is executed.
    3. Variables from an env_file (if specified).
    4. Variables defined directly in the environment section of docker-compose.yml.
    5. Variables from a .env file automatically loaded (i.e., ./.env in the project root if no env_file is specified).

3. Inspect Loaded Environment Variables

After attempting the above fixes, always verify what docker-compose and your containers actually see.

# 1. Use docker-compose config to check the service configuration
docker-compose config

# 2. Start your services
docker-compose up -d

# 3. Inspect a running container's environment variables
#    Replace 'webapp' with your service name
CONTAINER_ID=$(docker ps -aqf "name=webapp")
docker inspect --format '{{json .Config.Env}}' "${CONTAINER_ID}" | jq .

# 4. Run a temporary debug container to see its environment
docker-compose run --rm webapp env

If variables are still missing, the issue is with docker-compose itself or its interaction with the host environment.

4. Update or Reinstall Docker Compose on Alpine

Ensure you're using a recent, stable version of Docker Compose. Older versions might have bugs related to .env parsing.

# Check current Docker Compose version
docker-compose version

# Update/Reinstall on Alpine
apk update
apk upgrade py3-docker-compose # Or py2-docker-compose if you're on Python 2 (unlikely now)

# If issues persist, consider installing the standalone binary
# First, remove the apk version
apk del py3-docker-compose

# Then install the standalone Docker Compose binary
# Check the latest release at https://github.com/docker/compose/releases
LATEST_VERSION="v2.24.5" # Replace with the actual latest version
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
wget "https://github.com/docker/compose/releases/download/${LATEST_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -O $DOCKER_CONFIG/cli-plugins/docker-compose
chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose

# Create a symlink to make it available globally (optional, but recommended)
ln -s $DOCKER_CONFIG/cli-plugins/docker-compose /usr/local/bin/docker-compose

# Verify installation
docker-compose version

Manually installing the standalone Docker Compose binary gives you more control over the version but means you are responsible for keeping it updated, as apk upgrade will no longer manage it.

5. Check File Permissions and Path

Simple, but often overlooked. Ensure the .env file exists and is readable by the user running docker-compose.

# Go to your project root
cd /path/to/your/docker-compose/project

# Verify .env exists
ls -l .env

# Verify permissions (should be readable by owner, e.g., 644 or 600)
chmod 644 .env # Set sane permissions if needed

6. Diagnose Shell Environment with docker-compose run

You can use docker-compose run to directly inspect the environment variables visible before your actual application starts. This is useful for isolating the problem.

docker-compose run --rm webapp sh -c "env | grep DB_" # Replace webapp with your service

If DB_HOST (or your variable) is still empty here, the issue is definitively with docker-compose's parsing or setup. If it is present here, but not in your application, then the problem lies within your application's startup script or how it accesses environment variables.

7. Temporary Workaround: Explicitly Define in docker-compose.yml

As a last resort or a temporary measure while you troubleshoot, you can explicitly define the critical environment variables directly in your docker-compose.yml. This bypasses the .env file entirely for those specific variables.

# docker-compose.yml
version: '3.8'

services:
  webapp:
    build: .
    ports:
      - "8080:8080"
    # env_file: # Consider commenting this out if using 'environment' for specific vars
    #   - ./.env
    environment:
      DB_HOST: localhost_via_yml # Explicitly set here
      DB_USER: appuser_via_yml
      DB_PASSWORD: "my secure password with spaces_via_yml"
      APP_PORT: 8080

While this works, it reduces the flexibility of your deployments. The primary goal should be to get .env files working correctly. Only use this as a temporary solution or for variables that rarely change.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.