Environment Variables & Configuration
Environment Variables & Configuration
Every process on Linux inherits a set of environment variables — named key-value pairs that configure its runtime behaviour without hard-coding values into the program itself. Environment variables are how you tell a database client which host to connect to, how you inject secrets into containers without committing them to source control, and how the shell knows where to find every command you type. Understanding them deeply is non-negotiable for production DevOps work.
What Environment Variables Are and How They Work
When the kernel forks a new process, it passes two things to that process: its arguments (argv) and its environment (envp) — a null-terminated array of KEY=VALUE strings. The child process can read any of these strings, and it inherits a full copy of the parent's environment. Critically, a child process can only see its own copy; changes a child makes do NOT propagate back to the parent. This is a fundamental Unix design principle: environment is inherited downward, never upward.
Read and manipulate environment variables with these essential commands:
VAR=value command (no export) sets the variable only for that one command invocation. It is the cleanest way to test with a different config without polluting your shell session. Production scripts use this pattern extensively.
The PATH Variable — The Most Important Variable on Any System
PATH is a colon-separated list of directories the shell searches, in order, when you type a command name without a path. When you type docker, the shell walks each directory in PATH and executes the first match it finds. If no match exists, you get command not found.
PATH means your custom binary shadows a system binary of the same name. This is intentional when you need a newer version, but dangerous if done carelessly. A common incident: a developer installs a local curl that has debug flags enabled and prepends its directory, then a systemd service that calls curl picks up the debug build and fails silently. Always know which binary wins with which <command>.
Shell Profiles and Configuration Precedence
The shell reads different configuration files depending on how it is invoked. Getting this wrong is the source of many "works on my machine, breaks in cron / CI / SSH" bugs. There are two axes:
- Login shell vs. non-login shell: A login shell is the first shell session that starts when you authenticate — e.g., SSH login,
su - username, or a terminal emulator set to login mode. A non-login shell is any subsequent shell — e.g., opening a new terminal tab, runningbashfrom within another script, or a subshell. - Interactive vs. non-interactive shell: An interactive shell has a terminal attached and prompts the user. A non-interactive shell runs a script and exits — this is what cron jobs and CI runners use.
The practical rules for where to put things:
/etc/profileand/etc/profile.d/*.sh: System-wide settings applied to every user at login. In production, configuration management tools (Ansible, Puppet) write here to configure PATH additions or proxy settings fleet-wide. Never put secrets here.~/.bash_profile(or~/.profile): Per-user, login shell only. The canonical place to set andexportenvironment variables that tools need everywhere (e.g.,GOPATH,JAVA_HOME,NVM_DIR). By convention, this file sources~/.bashrcso interactive shells also benefit.~/.bashrc: Per-user, non-login interactive shell. Aliases, shell functions, prompt customisation, and completions go here. Do NOT put long-running commands or heavy computation here — it runs on every new terminal tab./etc/environment: PAM-read file. Key-value pairs, no shell syntax. Variables set here are available to ALL processes started by PAM (login, SSH, graphical sessions) regardless of which shell they use. Ideal forLANG,TZ, proxy settings, or feature flags that every process on the host must see.
Applying Changes Without Restarting
After editing a profile file, the current shell session does not automatically see the changes. You have two options:
source ~/.bashrc(or the dot shorthand:. ~/.bashrc) — re-executes the file in the current shell. Changes take effect immediately without opening a new terminal.- Open a new shell — a fresh login shell will read all profiles from scratch.
In production automation (e.g., a CI job that installs a tool and then uses it in the same script), you must source the profile or explicitly set the variable — a new subprocess does not inherit changes the parent script made to a file it hasn't sourced.
Environment Variables in Production Systems
In containerised and cloud-native environments, environment variables are the primary mechanism for injecting runtime configuration. The Twelve-Factor App methodology (the design philosophy behind most modern cloud services) mandates that all configuration live in the environment, not in config files baked into the image.
echo $DB_PASSWORD or set -x (which prints every command and its expanded variables). Secrets leaking into CI logs is one of the most common security incidents in engineering organisations. Use printenv DB_PASSWORD | wc -c to verify a secret is set without revealing its value.
Debugging Environment Problems
When a service or script cannot find a binary, fails to connect to a database, or behaves differently in CI than on your laptop, environment variables are almost always the cause. A systematic debug approach:
PATH beyond /usr/bin:/bin, no user profile sourced, no HOME in some versions. A script that calls docker or terraform without a full PATH will fail silently. Fix it by either sourcing the profile at the top (source /etc/profile) or using absolute paths. Similarly, systemd unit files do not inherit the interactive user's environment; set Environment= or EnvironmentFile= directives in the [Service] block explicitly.
Sensitive Variables and Security Hygiene
Environment variables are visible to anyone who can read /proc/<pid>/environ, which by default is restricted to the process owner and root. However, they can leak through:
- Verbose logging (e.g.,
set -xin shell scripts, debug middleware in web frameworks) - Error pages that dump the environment
ps auxoutput if the secret was passed as a command-line argument rather than as an env var- Docker inspect output and Kubernetes pod descriptions (for env vars, not secrets mounted as files)
For anything truly sensitive (passwords, API tokens, private keys), prefer secrets management systems — HashiCorp Vault, AWS Secrets Manager, or Kubernetes Secrets backed by an encrypted secrets store — and inject them as files into /run/secrets/, not as environment variables, where possible. At a minimum, never commit .env files to version control.