Environment Variables and Shell Configuration¶
Overview¶
You will see export API_URL=... in tutorials and wonder why it “disappears” tomorrow. Environment variables are how Linux passes configuration into programs for a session — and into services for their whole lifetime.
Plain problem: A script works in your SSH session but fails in cron with “command not found”. Same script — different environment. Cron did not load your .bashrc; PATH was shorter.
An environment variable is a named string every child process inherits: PATH (where to find commands), HOME, LANG, app settings. They live in three common places: interactive shell, startup files, and systemd units.
This is a Command Line tutorial in the REBASH Academy Linux for Cloud & DevOps Engineers series — practical Linux for Cloud and DevOps work.
Prerequisites¶
- Ubuntu practice VM or WSL2
- Essential Linux Commands
- Basic comfort typing commands in bash
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Explain environment variables in plain language
- Inspect and set variables with
export,env, andprintenv - Add a lab-safe snippet under
/etc/profile.d/ - Understand why cron jobs miss SSH environment variables
- Set
Environment=in a systemd unit - Answer fresher interview questions on environment and shell config
Architecture¶
Login shell reads profile files → sets environment → starts processes that inherit copies. Non-interactive cron/systemd paths skip most of your personal .bashrc unless you configure them.
Theory¶
The problem (before any jargon)¶
You add a tool to ~/bin and it works after export PATH=$PATH:~/bin in SSH. Night cron job still says mytool: not found. You did not persist PATH where cron looks.
What is an environment variable? (simple words)¶
Analogy: Environment variables are sticky notes on your desk every helper (process) reads when they start — “find tools in these folders”, “use this language”, “API lives here”.
| Variable | Typical role |
|---|---|
| PATH | Directories searched for command names |
| HOME | Your home directory |
| USER | Username |
| SHELL | Default shell program |
| LANG | Locale / language |
Interview line: “Processes inherit environment at fork time; cron and systemd need explicit PATH or env files.”
View and set (session vs persistent)¶
Session-only export dies when you log out unless written to startup files.
Startup files (Ubuntu bash — simplified)¶
| File | When |
|---|---|
/etc/profile | Login shells |
/etc/profile.d/*.sh | Modular login snippets (good for admins) |
~/.bashrc | Interactive non-login bash |
~/.profile | User login |
Do not edit system files recklessly on production — use drop-ins under profile.d with clear names.
systemd Environment=¶
Services do not read your .bashrc. Set env in the unit:
Cron vs SSH environment¶
Cron runs with minimal env. Fix: absolute paths in scripts, or PATH= line at top of crontab, or wrap in systemd timer (prior scheduling tutorial).
Common pitfalls¶
- Putting secrets in world-readable env files
- Expecting
~expansion in systemdEnvironment=(use full paths) - Mixing login vs non-login shell behaviour
exportin script withoutexportkeyword — child processes miss it
Hands-on Lab¶
Objective¶
Set lab variables, add a profile.d snippet, demonstrate cron missing PATH, fix with absolute path, add systemd Environment=, prove — under ~/rebash-linux/lab-env.
Prerequisites¶
| Item | Notes |
|---|---|
| Ubuntu VM | bash default |
sudo | For profile.d and systemd drop-in |
Lab environment¶
Real-world scenario¶
App team adds ~/bin/deploy-helper. Works interactively; cron deploy fails. You document environment inheritance and fix cron path — ticket style.
Step-by-step tasks¶
Task 1 – Custom tool and PATH¶
Create deploy-helper:
cd ~/rebash-linux/lab-env
chmod +x bin/deploy-helper
export PATH="$HOME/rebash-linux/lab-env/bin:$PATH"
deploy-helper | tee path-session-proof.txt
grep -q 'deploy-helper ran OK' path-session-proof.txt
Expected output
Helper runs when ~/rebash-linux/lab-env/bin is on PATH.
Task 2 – profile.d persistence¶
Create rebash-lab-env.sh:
# REBASH lab-env — append lab bin to PATH for login shells
export PATH="$HOME/rebash-linux/lab-env/bin:$PATH"
export REBASH_APP_ENV=lab
cd ~/rebash-linux/lab-env
sudo cp rebash-lab-env.sh /etc/profile.d/rebash-lab-env.sh
sudo chmod 644 /etc/profile.d/rebash-lab-env.sh
bash -lc 'echo PATH=$PATH; echo REBASH_APP_ENV=$REBASH_APP_ENV' | tee login-shell-env.txt
grep -q 'lab-env/bin' login-shell-env.txt
Expected output
Login shell simulation shows lab bin on PATH and REBASH_APP_ENV=lab.
Task 3 – Break cron env, fix, systemd Environment=¶
cd ~/rebash-linux/lab-env
( crontab -l 2>/dev/null; echo '* * * * * deploy-helper >> '"$HOME"'/rebash-linux/lab-env/logs/cron-broken.log 2>&1' ) | crontab -
mkdir -p logs
sleep 65
tail -3 logs/cron-broken.log 2>/dev/null | tee cron-broken-tail.txt || true
grep -q 'not found' cron-broken-tail.txt && echo "cron missed PATH — expected break" | tee break-notes.txt
crontab -r
( crontab -l 2>/dev/null; echo '* * * * * '"$HOME"'/rebash-linux/lab-env/bin/deploy-helper >> '"$HOME"'/rebash-linux/lab-env/logs/cron-fixed.log 2>&1' ) | crontab -
sleep 65
grep -q 'deploy-helper ran OK' logs/cron-fixed.log
echo "lab-env OK" | tee evidence.txt
Create rebash-env-demo.service:
[Unit]
Description=REBASH lab env demo oneshot
[Service]
Type=oneshot
Environment="REBASH_APP_ENV=systemd-demo"
ExecStart=/usr/bin/env bash -c 'echo REBASH_APP_ENV=$REBASH_APP_ENV >> /tmp/rebash-systemd-env.log'
cd ~/rebash-linux/lab-env
sed "s|/tmp/rebash-systemd-env.log|$HOME/rebash-linux/lab-env/logs/systemd-env.log|" rebash-env-demo.service | sudo tee /etc/systemd/system/rebash-env-demo.service >/dev/null
sudo systemctl daemon-reload
sudo systemctl start rebash-env-demo.service
grep systemd-demo logs/systemd-env.log
crontab -r 2>/dev/null || true
Expected output
Cron fails without full path; succeeds with absolute path. systemd log shows systemd-demo.
Validation steps¶
- Session PATH demo works
- profile.d affects login shell simulation
- Cron break/fix documented
- systemd Environment= proven in log file
Common errors and fixes¶
| Error | Cause | Fix |
|---|---|---|
| command not found in cron | PATH | Absolute path or PATH= in crontab |
| Variable empty in systemd | Not in unit | Environment= or EnvironmentFile= |
| profile.d not loaded | Non-login shell | Use bash -l or correct file |
| Permission denied profile.d | Bad perms | 644, root-owned |
Challenge exercise¶
Add printenv > ~/rebash-linux/lab-env/env-snapshot.txt from SSH and from a one-line cron job — compare line counts in your notes.
Learning outcomes¶
- You understand inheritance vs cron/systemd config
- You fixed a classic “works in SSH only” bug
- You can explain PATH to an interviewer
Cleanup¶
crontab -r 2>/dev/null || true
sudo rm -f /etc/profile.d/rebash-lab-env.sh /etc/systemd/system/rebash-env-demo.service
sudo systemctl daemon-reload
Validation¶
- Evidence under
~/rebash-linux/lab-env - Can explain three places env vars are set
- Ready for filesystem paths tutorial next
Code Walkthrough¶
export PATH=...— session only until profile.d or unit file persists./etc/profile.d/— modular admin-friendly login snippets.- Cron without PATH — deliberate break; mirrors production incidents.
- Absolute path fix — simplest reliable cron fix.
- systemd Environment= — services ignore
.bashrcby design.
Security Considerations¶
- Never put passwords or API keys in world-readable profile.d or unit files in Git.
- Use secret managers or restricted
EnvironmentFile(0600, root-only). - Audit
/etc/environmentand profile.d for stale exports. - Limit who can edit systemd unit drop-ins.
- Scrub env snapshots before sharing in tickets.
Common Mistakes¶
❌ Secrets in export lines.
✅ Environment leaks via ps, logs, and core dumps — use proper secret storage.
❌ Assuming cron loads .bashrc.
✅ It does not — configure env explicitly.
❌ Tilde in systemd paths.
✅ Use full paths like /home/user/bin/app.