Skip to content

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

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, and printenv
  • 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.

Linux CLI workflow — shell, env, PATH, systemd

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)

Terminal
echo "$PATH"
export REBASH_LAB=hello
env | grep REBASH

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:

[Service]
Environment="APP_MODE=production"
EnvironmentFile=/etc/myapp/env

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 systemd Environment= (use full paths)
  • Mixing login vs non-login shell behaviour
  • export in script without export keyword — 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

Terminal
mkdir -p ~/rebash-linux/lab-env/bin && cd ~/rebash-linux/lab-env

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:

deploy-helper
#!/usr/bin/env bash
echo "deploy-helper ran OK at $(date -Is)"
Terminal
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.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
Terminal
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=

Terminal
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:

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'
Terminal
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

Terminal
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

  1. export PATH=... — session only until profile.d or unit file persists.
  2. /etc/profile.d/ — modular admin-friendly login snippets.
  3. Cron without PATH — deliberate break; mirrors production incidents.
  4. Absolute path fix — simplest reliable cron fix.
  5. systemd Environment= — services ignore .bashrc by 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/environment and 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.