Linux has five ways to view env vars: printenv (for one or all), env (for all), echo $VAR (for one), set (for shell vars and functions, in bash), and /proc/<pid>/environ (for a running process). The right answer is printenv for a specific var (printenv DATABASE_URL), env for all (with a filter for secrets), echo $VAR for a quick check. The mistake every team makes: the team puts the secret in the command line (myapp --password=***), and ps auxe shows the command line to every user on the box.
Table of contents
- The five commands — printenv, env, echo, set, /proc
- The .bashrc and .profile — the persistent env
- The systemd unit — the env in the service definition
- The docker run -e — the container env
- The one mistake that leaks the secret to ps
- The ProcessEnvironment — the right answer for production
- How this fits the rest of the stack
- FAQ
The five commands — printenv, env, echo, set, /proc
printenv prints one or all env vars. printenv with no args prints all, printenv DATABASE_URL prints the one. The right answer is printenv for a specific var.
env prints all env vars. The right answer is env | grep ^DATABASE to filter, the wrong answer is env to dump everything to the terminal (the team’s screen is full of unrelated vars, the team misses the one they need).
echo $VAR prints the value of a specific var. The right answer is echo $DATABASE_URL for a quick check. The gotcha: the $VAR is expanded by the shell before echo runs, so the command line shows the value (echo $DATABASE_URL is the same as echo postgres://user:***@host/db in the shell history).
set (in bash) prints all shell vars AND functions. The output is long, the team’s actual env vars are mixed in with the shell internals. The right answer is set | grep ^VAR= to filter, the wrong answer is set | grep SECRET to find a secret by name (the team has a secret in the env, the team’s grep finds it, the team displays it on the screen).
/proc/<pid>/environ is the env of a running process. The right answer is cat /proc/1234/environ | tr '\0' '\n' to read a specific process’s env. The wrong answer is to dump the whole thing to a public log file.
The .bashrc and .profile — the persistent env
The .bashrc (or .zshrc) is the shell init file. The team’s env vars are set with export VAR=value in the file, the vars are available in every new shell. The right answer for a dev-time env (a personal API key, a local path) is the .bashrc. The wrong answer is the .bashrc for a secret — the file is readable by the user’s account, the file is in the user’s home, the secret is in the user’s backup.
The .profile (or .bash_profile) is the login shell init file. The right answer is to use the .profile for env vars that should be available in login shells (ssh sessions, cron jobs), the .bashrc for env vars that should be available in interactive shells. The gotcha: the .profile is sourced once, the .bashrc is sourced in every new shell. The team’s PATH and env vars in the .bashrc are not available to the cron job.
The systemd unit — the env in the service definition
The systemd unit file is the right place for the env vars of a service. The team sets Environment="DATABASE_URL=postgres://..." in the unit, the env vars are available to the service. The right answer is the systemd unit for a long-running service, the wrong answer is the .bashrc for a service env (the .bashrc is for the user’s shell, the systemd unit is for the service).
The right answer for a secret is EnvironmentFile=/etc/myapp/secrets.env — the env file is readable by the service user only, the unit references the file, the env vars are available to the service.
The docker run -e — the container env
docker run -e VAR=value sets the env var in the container. The right answer is the -e flag for a single var, --env-file for a file of vars. The wrong answer is the -e flag with a secret — the command line is in the shell history, the command line is in the docker logs.
The right answer for a secret in a container is --env-file, where the file is in a secret store (Docker Swarm secrets, Kubernetes secrets, AWS Secrets Manager), not a local file with chmod 644.
The one mistake that leaks the secret to ps
The mistake: the team runs myapp --password=***, the password is on the command line, ps auxe shows the command line to every user on the box. The attacker (a low-privilege user) runs ps auxe | grep myapp, the password is in the output. The right answer is to use the env var (myapp reads $MYAPP_PASSWORD), the env var is not on the command line, the env var is not visible to ps.
The ProcessEnvironment — the right answer for production
The right answer for a production env var is the process environment, not the command line. The process reads the env var from its environment (set by systemd, by the container runtime, by the deploy platform), the env var is in the process’s memory, the env var is not visible to ps. The right answer for a secret is the platform’s secret store, injected as an env var at process start.
How this fits the rest of the stack
The infrastructure question is a small piece of a larger pattern: the team’s runtime, storage, database, secret store, logs, and deployment platform are all parts of the same platform. The right answer is to model the full stack before the project ships, not after. The RunxBuild hosting calculator is the right place to do that exercise — pick the runtime, the memory tier, the storage, the secret store, and the egress, and the calculator shows what the deploy actually costs at the team’s actual usage.
Useful related references:
FAQ
How do I view environment variables in Linux?
printenv (one or all), env (all), echo $VAR (one), set (shell vars and functions in bash), /proc/<pid>/environ (for a running process).
What is the difference between printenv and env?
printenv is the same as env for printing, but printenv takes args (printenv VAR) while env does not. Both are part of coreutils.
How do I set an environment variable in Linux?
export VAR=value in the shell, or export VAR=value in the .bashrc for persistence. The right answer is the .bashrc for dev, the systemd unit for a service.
How do I see the env vars of a running process?
cat /proc/<pid>/environ | tr '\0' '\n'. The right answer is to read the specific var with cat /proc/<pid>/environ | tr '\0' '\n' | grep VAR.
How do I hide a secret from ps?
Use the env var, not the command line. The env var is in the process’s memory, not on the command line, not visible to ps.
Where should I put env vars for a systemd service?
In the unit file (Environment=VAR=value) or in an EnvironmentFile (referenced with EnvironmentFile=/path/to/file). The right answer is the EnvironmentFile for a secret.
How do I set env vars in Docker?
docker run -e VAR=value for a single var, --env-file for a file of vars. The wrong answer is -e with a secret in the command line.
Why does echo $VAR show nothing in cron?
Cron has a different env than the interactive shell. The right answer is to set the env vars in the crontab line (VAR=value; * * * * * /path/to/script) or in a wrapper script that sources the .env file.