Server Management

Server Environment Variables Setup Guide

Written by Jack Williams Reviewed by George Brown Updated on 23 February 2026

Introduction and Purpose

Environment variables are simple pieces of data your programs read at runtime. They hold configuration like database URLs, API keys, or feature flags. Using environment variables keeps code portable and makes it easier to change settings without editing source files.

This article explains what environment variables are, how to set them on Linux, macOS, and Windows, and how to manage them securely in containers and CI/CD pipelines. You’ll also find practical troubleshooting tips and best practices to avoid common mistakes.

Overview of Environment Variables

An environment variable is a name/value pair available to a process and its child processes. The shell or operating system stores them. Common uses include:

  • Paths and configuration (PATH, HOME)
  • Credentials or tokens (DATABASE_URL, API_KEY)
  • Feature toggles and environment names (NODE_ENV, RAILS_ENV)

Scope matters:

  • Process-level variables exist only for a running program.
  • User-level variables persist for a user session or profile.
  • System-level variables apply to all users and services.

The “12-factor app” pattern recommends storing config in environment variables so the same code runs across environments with different settings.

Prerequisites and Security Considerations

Before changing environment variables:

  • Have the right access (user shell, sudo for system-wide changes, admin on Windows).
  • Know the shell you use (bash, zsh, PowerShell) because files and commands differ.
  • Treat secrets (passwords, API keys) as sensitive: do not commit them to source control or share them in logs.
  • Use secure storage and proper permissions for files with secrets.
  • Prefer short-lived credentials or dynamic secrets where possible.

Environment Variable Formats and Conventions

Naming and formatting rules:

  • Use uppercase letters, digits, and underscores. Start with a letter or underscore.
  • Avoid spaces and special characters. Example: DATABASE_URL or API_KEY.
  • Keep names short and descriptive.

Common value formats:

  • Plain text: simple values like “true” or “production”.
  • URLs: postgres://user:pass@host:port/db
  • JSON or base64 for complex data, but prefer files or secret managers for large payloads.

.env file conventions:

  • Lines like KEY=value
  • Comments start with #
  • Do not store secrets in a public repo. Add .env to .gitignore.
  • Example:
    DB_HOST=localhost
    DB_USER=app
    DB_PASSWORD=secret
    

Quoting rules:

  • Unquoted values end at newline; use quotes if value contains spaces or #.
  • Be careful with shell expansion (e.g., $ and backticks) — quote when needed.

Setting Environment Variables on Linux Servers

Temporary (process-level):

  • For a single command:
    DB_HOST=localhost DB_USER=app ./start-app
    
  • For current shell session:
    export DB_PASSWORD=secret
    echo $DB_PASSWORD
    

Persistent for one user:

  • Add exports to your shell profile:
    • Bash: ~/.bashrc or ~/.bash_profile
    • Zsh: ~/.zshrc
      Example:
      export DATABASE_URL="postgres://user:pass@localhost:5432/db"
      

      Then reload with source ~/.bashrc or open a new shell.

System-wide (all users):

  • /etc/environment is a basic option (key=value pairs without export).
    DB_HOST=0.0.0.0
    

    Log out and back in or reboot for changes to take effect.

  • Use /etc/profile.d/*.sh for scripts that export variables:
    sudo tee /etc/profile.d/myapp.sh <<'EOF'
    export MYAPP_ENV=production
    EOF
    

    Then chmod +x /etc/profile.d/myapp.sh.

systemd services:

  • For services managed by systemd, set environment variables in the unit file:
    [Service]
    Environment="DB_HOST=localhost" "DB_USER=app"
    

    Or place a file in /etc/systemd/system/my.service.d/env.conf with Environment= lines and reload systemd:

    sudo systemctl daemon-reload
    sudo systemctl restart my.service
    
  • You can use EnvironmentFile=/etc/myapp/env to keep secrets out of unit files.

Tips:

  • Sudo may clear environment variables; use sudo -E if you need to preserve them, but avoid exposing secrets.
  • Use tools like env or printenv to inspect environment variables:
    printenv DATABASE_URL
    env | grep DB
    

Setting Environment Variables on macOS Servers

Shell profiles:

  • macOS uses zsh by default (macOS Catalina and later).
  • Add exports to ~/.zshrc for interactive shells:
    export NODE_ENV=production
    

    Then source ~/.zshrc.

GUI apps and login items:

  • GUI apps launched from Finder or login items don’t inherit your shell profile. Use launchctl or LaunchAgents:
    • To set environment variables for the user GUI environment:
      launchctl setenv MY_VAR "value"
      

      This persists only until logout.

    • For longer-term GUI variables, create a LaunchAgent plist that sets environment variables, but consider using a proper secrets store or agent-specific configs.

system-wide:

  • Use /etc/paths or /etc/profile for system-wide PATH changes; use /etc/profile.d if available.

Homebrew services and systemd alternatives:

  • Services run via launchd; set environment using plist keys or use a wrapper shell script that exports variables before starting the service.

Setting Environment Variables on Windows Servers

Temporary (command prompt or PowerShell):

  • Command Prompt:
    set MY_VAR=value
    echo %MY_VAR%
    
  • PowerShell:
    $env:MY_VAR = "value"
    echo $env:MY_VAR
    

Persistent (user or system):

  • GUI: Settings → System → About → Advanced system settings → Environment Variables. Add user or system variables there.
  • Command line (PowerShell):
    [Environment]::SetEnvironmentVariable("MY_VAR", "value", "User")
    [Environment]::SetEnvironmentVariable("MY_VAR", "value", "Machine") # needs admin
    
  • setx (creates or modifies environment variables permanently, but not available to current session):
    setx MY_VAR "value"
    

    Open a new shell to see setx changes.

Path changes:

  • Modify PATH carefully and avoid duplicates. Use the Environment Variables GUI or PowerShell to append:
    [Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:mybin", "User")
    

Registry (advanced):

  • Environment variables are stored in the registry under:
    • HKEY_CURRENT_USEREnvironment
    • HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerEnvironment
  • Only use the registry if you have a specific need.

Permissions:

  • Setting system-level variables requires admin rights; some changes need a logout or reboot.

Using Environment Variables with Containers and Docker

Dockerfile:

  • Set defaults and build-time values:
    ARG APP_ENV=production
    ENV APP_ENV=${APP_ENV}
    

    Use ARG for build-time only and ENV for runtime defaults.

docker run:

  • Pass variables at runtime:
    docker run -e DB_HOST=host -e API_KEY=abc myimage
    
  • Use an env file:
    docker run --env-file ./prod.env myimage
    

docker-compose:

  • Define environment in compose:
    services:
      app:
        environment:
          - DB_HOST=postgres
        env_file:
          - .env
    

Secrets in containers:

  • Do not bake secrets into images or commit env files. Use:
    • Docker secrets (Swarm): secure and mounted as files in /run/secrets.
    • Bind mounts or sidecars to provide secrets from the host.
    • Secret managers injected at runtime by your orchestrator (Kubernetes secrets, Vault injector).

Kubernetes:

  • Use Secrets and ConfigMaps. Inject secrets as environment variables or mounted files. For high-security needs prefer file mounts and restricted RBAC.

Tip:

  • Environment variables are visible in process lists or container metadata in some cases. Prefer file-based secrets or secret stores for high-value secrets.

Managing Environment Variables in CI/CD Pipelines

Principles:

  • Store secrets in the pipeline’s secret store (not in repo).
  • Limit who can read or modify secrets.
  • Mask values in logs.

GitHub Actions:

  • Use repository secrets or organization secrets and reference them in workflows:
    - name: Build
      env:
        API_KEY: ${{ secrets.API_KEY }}
    

    Secrets are masked in logs.

GitLab CI:

  • Use CI/CD variables (masked/protected) in Settings → CI/CD and use them in .gitlab-ci.yml:
    script:
      - echo $API_KEY
    

Jenkins:

  • Use Credentials plugin and inject with “Credentials Binding” or with environment variables via withCredentials block. Avoid storing raw secrets in pipeline scripts.

CircleCI, Travis, etc.:

  • All provide secure env var storage; use those rather than committing values.

Best practices:

  • Use separate credentials per environment (dev/staging/prod).
  • Rotate secrets regularly.
  • Restrict variable scope to required branches or jobs.
  • Avoid echoing secrets in logs; use secret-masking features.

Secrets Management and Secure Storage

Why use a secrets manager:

  • Centralized access control and auditing.
  • Automatic rotation and short-lived credentials.
  • Encrypted storage and controlled distribution.

Common options:

  • HashiCorp Vault: dynamic secrets, leasing, and strong access control.
  • Cloud providers: AWS Secrets Manager / Parameter Store, Azure Key Vault, GCP Secret Manager.
  • Third-party SaaS: Doppler, 1Password Secrets Automation.
  • SOPS + Git (encrypted secrets in repo) for infrastructure-as-code workflows.

Patterns:

  • Use environment variables to pass non-sensitive config and a short-lived token to fetch secrets at runtime.
  • Use service identities (IAM roles, instance profiles) instead of embedding long-lived secrets.
  • Grant least privilege and enable audit logging.

Storing secrets safely:

  • Encrypt secrets at rest and in transit.
  • Limit who can read or modify secrets.
  • Use fine-grained policies and short TTLs where possible.
  • Automate rotation and test rotation procedures.

Troubleshooting and Best Practices

Common issues and fixes:

  • Variable not found: Check export vs set, shell file used (.bashrc vs .bash_profile vs .zshrc), and whether you opened a new shell.
  • Systemd service not seeing env vars: Use Environment= or EnvironmentFile= in the service unit and reload systemd.
  • Windows setx changes not in current session: Open a new shell.
  • Quoting problems: Wrap values with spaces or special characters in quotes.
  • Newlines in secrets: Use base64 encoding or files for multiline secrets.

Best practices checklist:

  • Never commit secrets to version control. Add .env to .gitignore.
  • Use .env.example for public sample configuration without values.
  • Favor secret managers for sensitive data and use environment variables for pointers or short-lived tokens.
  • Keep variable names consistent and documented.
  • Use least privilege and separate credentials by environment.
  • Mask variables in CI logs and audit access to secrets.
  • Prefer file-mounted secrets for high-security use cases where environment exposure is risky.
  • Document where each variable is defined and how to rotate it.

Monitoring and auditing:

  • Track access to secret management systems.
  • Alert on unusual requests or failed access attempts.
  • Periodically review and rotate keys and tokens.

Quick commands for debugging:

  • Linux/macOS:
    env | grep MYAPP
    printenv DATABASE_URL
    
  • Windows (PowerShell):
    Get-ChildItem Env: | Where-Object { $_.Name -like "DB_*" }
    echo $env:MY_VAR
    

Final tip:

  • Treat environment variables as part of your configuration interface. Make them easy to manage, document them, and protect the sensitive ones.

Troubleshooting and Best Practices (Summary)

Environment variables are powerful and simple but can introduce security and operational risks if handled poorly. Use the right files and commands for your platform, prefer secret managers for sensitive data, and apply consistent naming and access controls. When things go wrong, check the shell or service context, confirm persistence and scope, and inspect logs carefully without exposing secrets.

Following these patterns will keep configuration clear, portable, and safer across development, CI/CD, containers, and production systems.

About Jack Williams

Jack Williams is a WordPress and server management specialist at Moss.sh, where he helps developers automate their WordPress deployments and streamline server administration for crypto platforms and traditional web projects. With a focus on practical DevOps solutions, he writes guides on zero-downtime deployments, security automation, WordPress performance optimization, and cryptocurrency platform reviews for freelancers, agencies, and startups in the blockchain and fintech space.