You exported a variable in .bashrc and your systemd user service still cannot see it, which is exactly what environment.d is for. You will set a variable for services under your systemd user instance, extend an existing value safely, and verify the result with systemctl --user show-environment. The examples use systemd 255.4-1ubuntu8.17. Allow about fifteen minutes, including a reload and a small test.
You need a normal user shell and a running systemd user manager. The file belongs to your account, so the main workflow does not need sudo. This mechanism changes the environment passed to user services. It does not rewrite the environment of the already-running user manager itself, and it does not change unrelated login shells.
Start with read-only checks. They show whether the user instance is reachable and what it exports before you change anything:
$ systemctl --user is-system-running
running
$ systemctl --user show-environment | grep '^PATH='
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Your status may be degraded while the manager is still usable. If the command cannot connect to the user bus, fix that session problem first. An ordinary login shell's printenv output is not proof of what the user manager exports.
Checkpoint: write down the variable you want to add or change, and decide whether it is for every user service or one unit. A unit-specific Environment= setting in a service override may be a better boundary when the value is sensitive or only one service needs it.
Create the per-user directory and a file with a two-digit prefix. Both are ordinary user-owned state:
$ install -d -m 0755 "$HOME/.config/environment.d"
$ nano "$HOME/.config/environment.d/60-project.conf"
Use whichever editor you like. Put one assignment on each line. This example adds a tool directory in front of the existing PATH:
PROJECT_HOME=/home/alice/projects/example
PATH=${PROJECT_HOME}/bin:$PATH
Use your real home directory and project path instead of copying /home/alice blindly. The file format has a few rules:
$NAME or ${NAME}.#.For a value with a fallback, use the documented forms:
CACHE_ROOT=${CACHE_ROOT:-/var/cache/example}
OPTIONAL_FLAG=${OPTIONAL_FLAG:+enabled}
The first form uses the fallback when the earlier value is empty. The second produces enabled only when the earlier value would be non-empty.
Warning: keep secrets out of a broadly inherited environment where you can. Environment variables can be exposed through service diagnostics and process inspection.
systemd reads .conf files from the user directory, then the administrator and runtime locations, then the local and vendor locations. In precedence order, the directories are:
~/.config/environment.d
/etc/environment.d
/run/environment.d
/usr/local/lib/environment.d
/usr/lib/environment.d
Files are sorted together by filename, not separately within each directory. A file in a higher-priority directory replaces a same-named file from a lower-priority one. For different names, the lexicographically later assignment wins. So 60-project.conf can extend a vendor value, while 90-project-local.conf can override an earlier assignment.
A file in ~/.config/environment.d does not automatically win every conflict. A vendor file with a later name can still win for an individual variable. When the result surprises you, list the installed files:
$ find /usr/lib/environment.d /usr/local/lib/environment.d /run/environment.d /etc/environment.d "$HOME/.config/environment.d" \
-maxdepth 1 -type f -name '*.conf' -print 2>/dev/null | sort
Ask systemd to rerun its environment generators after you save the file:
$ systemctl --user daemon-reload
This is an ordinary user-manager operation and normally needs no elevated privileges. The generator runs at manager startup and at configuration reload. Services started afterwards get the refreshed environment. A service that is already running keeps the environment it received when it started.
Warning: do not restart a production service just to make a variable visible until you have checked the value and confirmed a restart is acceptable. If one is required, treat it as a service-disrupting action and use that unit's normal maintenance procedure.
Query the user manager, not just the shell where you edited the file:
$ systemctl --user show-environment | grep -E '^(PROJECT_HOME|PATH)='
PATH=/home/alice/projects/example/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PROJECT_HOME=/home/alice/projects/example
Line order can differ. Check the value itself, and confirm the expected directory appears once, in the intended position. If the variable is missing, check the extension, file path and variable name, then run systemctl --user daemon-reload again.
To see the generator's diagnostic trace without changing configuration, run:
$ SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator
... reading environment.d files ...
The installed generator prints the calculated assignments to standard output. Debug wording and values depend on the machine, so use the final assignment as your evidence.
show-environment confirms what the manager exports. To check that a service inherits it, run a harmless transient user unit, if transient units are available:
$ systemd-run --user --wait --pipe /usr/bin/sh -c 'printf "PROJECT_HOME=%s\n" "$PROJECT_HOME"'
PROJECT_HOME=/home/alice/projects/example
This starts a short-lived process and leaves no persistent unit file. If systemd-run is unavailable or policy blocks transient units, inspect an existing non-critical user service through its normal logs instead.
Tip: do not use a production service as a test target just to prove a path variable.
To remove only your setting, delete the assignment from the file or move the file out of the directory. Moving is reversible:
$ mv "$HOME/.config/environment.d/60-project.conf" \
"$HOME/.config/environment.d/60-project.conf.disabled"
$ systemctl --user daemon-reload
$ systemctl --user show-environment | grep '^PROJECT_HOME=' || echo 'PROJECT_HOME is not exported'
The disabled file no longer has the required .conf extension, so the generator ignores it.
Recovery: keep the disabled file until you have confirmed that dependent user services no longer need the value. Existing services may still hold the old value until they are restarted, so check their lifecycle before you rely on the removal.
*.conf file under ~/.config/environment.d.systemctl --user daemon-reload completed after the change.systemctl --user show-environment shows the intended value, and a newly started test process inherits it.