You set a variable and it vanishes after reboot
You run a development script and it fails with Error: JAVA_HOME is not defined. You type export JAVA_HOME=/usr/lib/jvm/java-17-openjdk and the script works. You close the terminal, reboot the laptop, and the error returns. You need the variable to survive the session, the reboot, and the service manager. Environment variables are the hidden wiring between your shell, your applications, and the operating system. Setting them correctly means understanding scope.
What is actually happening
Every process on Fedora inherits a list of key-value pairs from its parent. The shell passes them to the programs you run. A temporary variable lives only in the current terminal window. A user variable loads when you log in. A system variable loads before any user session starts. The difference matters because systemd services, cron jobs, and graphical applications read from different layers. If you put a variable in the wrong place, the program that needs it will never see it.
Think of it like handing a note to a colleague. You can tape it to their monitor for today. You can write it in their personal notebook for next week. You can print it on the company memo board for everyone to read. Each location has a specific audience and a specific lifespan. Fedora splits environment configuration across three distinct subsystems. The shell handles interactive sessions. PAM handles login and authentication. Systemd handles background daemons. They do not share a single configuration file by default.
Run journalctl -xe first when a service fails to start. The explanatory text often points directly to a missing environment variable. Read the actual error before guessing.
The fix and how to apply it
Start with the scope you actually need. Most desktop users only need user-level persistence. System administrators need system-wide configuration. Service managers need their own mechanism.
Here is how to set a temporary variable for the current terminal session.
export MY_VAR="my_value"
# export pushes the variable into the current shell's environment
# child processes will inherit it immediately
echo $MY_VAR
# verify the shell recognizes the assignment before running your tool
Temporary variables vanish when the terminal closes. They are useful for testing a configuration before committing it to a file.
Here is how to make a variable persistent for your user account across reboots.
echo 'export MY_VAR="my_value"' >> ~/.bashrc
# ~/.bashrc loads for every interactive bash session
# appending with >> prevents overwriting your existing configuration
source ~/.bashrc
# reload the current shell to apply the change without logging out
The ~/.bashrc file runs for interactive shells. It does not run for non-interactive scripts or cron jobs. If you need a variable for login shells or graphical sessions, append to ~/.profile instead. Fedora uses ~/.profile to source ~/.bashrc automatically, so placing exports in ~/.bashrc covers most desktop workflows.
Here is how to set a system-wide variable that every user and login session can read.
MY_VAR="my_value"
# /etc/environment uses KEY=VALUE syntax without export
# PAM reads this file during authentication
# do not use quotes around the key, only around the value
Edit /etc/environment with sudo nano /etc/environment. This file applies to all users and loads before the shell starts. It is the standard place for system-wide defaults. Do not add export here. PAM expects plain assignments.
Here is how to configure environment variables for systemd services specifically.
systemctl set-environment MY_VAR="my_value"
# writes to /etc/environment.d/ and updates the runtime manager
# systemd services will see the variable on the next start
systemctl daemon-reload
# forces systemd to re-read unit files and environment directories
Systemd does not read ~/.bashrc or /etc/environment by default. It maintains its own environment block. Use systemctl set-environment for services that run as daemons. This keeps service configuration separate from interactive shell configuration.
Here is how to attach a variable to a specific service unit file.
[Service]
Environment="MY_VAR=my_value"
# Environment= inside a unit file scopes the variable to that service only
# other services and user sessions will not inherit it
ExecStart=/usr/bin/my-daemon
# the daemon will receive MY_VAR when systemd launches it
Edit the override file with sudo systemctl edit <service_name>. This creates a drop-in in /etc/systemd/system/<service_name>.service.d/. Never edit the original unit file in /usr/lib/systemd/system/. Package updates will overwrite vendor files. Trust the package manager. Manual file edits drift, snapshots stay.
Verify it worked
Run printenv MY_VAR to check the current session. Run sudo systemctl show <service_name> --property=Environment to verify systemd sees it. If the output is empty, the variable is not in the correct scope for that process. Check which file the process actually reads before guessing.
Use env | grep MY_VAR to see exactly how the variable is formatted. Whitespace inside quotes often breaks path resolution. Use systemctl status <service_name> to check recent log lines and state in one view. Always check status before restarting.
Common pitfalls and what the error looks like
The most frequent mistake is mixing up shell variables and environment variables. Typing MY_VAR="my_value" without export creates a shell variable. Child processes will not inherit it. You will see errors like Error: variable MY_VAR is not set even though echo $MY_VAR works in your terminal. Always use export in shell config files.
Another common trap is quoting. Single quotes prevent variable expansion. Double quotes allow it. If your value contains spaces or special characters, wrap it in double quotes. If you need a literal dollar sign inside the value, escape it with a backslash. A misquoted path will break application startup silently.
Systemd services often fail with Failed at step EXEC spawning /usr/bin/app: No such file or directory when a PATH variable is missing. The service runs with a minimal environment. It does not inherit your interactive shell settings. Add Environment="PATH=/usr/local/bin:/usr/bin" to the [Service] section of the unit file, or use systemctl set-environment. Never rely on ~/.bashrc for daemon configuration.
Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/. If you modify a vendor file, the next dnf upgrade --refresh will overwrite your changes and break the system. dnf upgrade --refresh is the normal weekly maintenance command. dnf system-upgrade is for crossing major Fedora releases. They are different commands. Don't conflate them.
SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. A missing environment variable will not trigger an SELinux denial, but a wrong file path will. Check the audit log if the service crashes immediately after starting.
When to use this vs alternatives
Use export in the terminal when you need a quick test or a one-off override. Use ~/.bashrc when you are configuring tools for your own interactive sessions. Use ~/.profile when you need variables for login shells or graphical desktop sessions. Use /etc/environment when every user and login session needs the same baseline. Use systemctl set-environment when you are configuring background daemons and systemd services. Use unit file Environment= directives when the variable must be locked to a specific service and never leak to others.