How to Configure Systemd User Environment Variables in Ubuntu Using environment.d

In modern Linux desktop and server environments running systemd, configuring persistent environment variables across disparate application contexts has historically been fragmented. System administrators often defined variables inside ~/.bashrc, ~/.profile, /etc/environment, or desktop-specific display manager wrappers. However, shell configuration files only apply to interactive shell sessions launched via bash or zsh; they are completely ignored by graphical applications launched from application menus (Wayland/X11), background systemd user services (systemctl --user), cron tasks, and desktop notification daemons.

To eliminate this fragmentation and establish a unified, shell-agnostic environment variable standard, systemd provides environment.d. Through the systemd-environment-d-generator subsystem, systemd parses simple key-value configuration files placed in standard directory drop-ins upon user session login. Variables declared via environment.d are automatically injected into the systemd user manager instance ([email protected]) and inherited by all desktop applications, user services, and terminal shells. This guide explains how environment.d operates, how to define system-wide and user-specific configurations in Ubuntu, and how to verify variable propagation.

Understanding the environment.d Specification and Evaluation Precedence

The environment.d specification processes configuration files ending with the .conf extension across several standard system and user directory roots. When a user logs into Ubuntu, the systemd-environment-d-generator binary reads and merges files according to strict lexical sorting and path precedence:

  • ~/.config/environment.d/*.conf: User-specific declarations. This directory holds the highest precedence, allowing individual developers or desktop users to set or override system-wide defaults.
  • /etc/environment.d/*.conf: Administrator-defined system-wide declarations. Applied to all user sessions on the system (ideal for corporate proxy settings, developer paths, and default language locales).
  • /run/environment.d/*.conf: Transient runtime environment files dynamically generated by early boot scripts or container init routines.
  • /usr/lib/environment.d/*.conf: Vendor-supplied defaults shipped by upstream Debian/Ubuntu distribution packages. Should not be edited directly by administrators.

Files are evaluated in alphabetical ASCII order (e.g. 10-proxy.conf is processed before 50-development.conf). If the same variable is defined in multiple files, the file processed last or the file residing in a higher-priority directory wins.

Configuration File Syntax and Supported Directives

Configuration files in environment.d use a clean, shell-agnostic key-value format. They do not support complex shell scripts, conditional logic (such as if/else statements), or unquoted parameter expansion loops:

# Basic syntax
KEY=value
KEY="quoted value with spaces"

# Variable reference expansion
PATH="${HOME}/.local/bin:${PATH}"
EDITOR="/usr/bin/nano"
XDG_DATA_HOME="${HOME}/.local/share"

Key syntax rules include:

  • Lines starting with # or ; are treated as comments and ignored.
  • Values containing spaces must be enclosed in double quotes (") or single quotes (').
  • Variable expansion uses the syntax ${VARIABLE}. You can reference variables declared earlier in the file or existing system variables like ${HOME} and ${PATH}.
  • Command substitution (e.g. $(uname -m) or backticks) is not supported.

Configuring System-Wide Environment Variables via /etc/environment.d/

To configure environment variables that must be inherited by every user account on the Ubuntu system—such as enterprise HTTP proxy configurations or shared Java/Go toolchains—create a drop-in file inside /etc/environment.d/.

Create the directory if it does not already exist:

sudo mkdir -p /etc/environment.d

Create a configuration file for corporate network proxy settings:

sudo nano /etc/environment.d/20-proxy.conf

Add the proxy declarations:

HTTP_PROXY="http://proxy.internal.corp:8080"
HTTPS_PROXY="http://proxy.internal.corp:8080"
NO_PROXY="localhost,127.0.0.1,.internal.corp"

Save and close the file. To establish a system-wide Go and Rust toolchain path configuration, create an additional file:

sudo nano /etc/environment.d/50-toolchains.conf
GOPATH="${HOME}/go"
CARGO_HOME="${HOME}/.cargo"
PATH="${HOME}/.cargo/bin:${HOME}/go/bin:${PATH}"

Configuring User-Specific Environment Variables via ~/.config/environment.d/

To configure variables for your personal user account without requiring root privileges, use the user configuration root.

Create the personal configuration directory:

mkdir -p ~/.config/environment.d

Create a personal development environment file:

nano ~/.config/environment.d/10-development.conf

Define custom developer preferences:

EDITOR="nvim"
VISUAL="nvim"
PAGER="less -R"
DOTNET_CLI_TELEMETRY_OPTOUT="1"
HOMEBREW_NO_ANALYTICS="1"

Save and exit. These variables will now be available to user services (such as background syncthing or podman units) as well as graphical IDEs launched from GNOME Shell.

Verifying and Testing Environment Variable Generation

Before logging out or rebooting, verify that systemd’s generator parses your configuration files cleanly and generates the expected key-value pairs.

Execute the systemd generator manually in dry-run mode using systemd-environment-d-generator:

/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator

The command reads all valid .conf files in precedence order and outputs the fully resolved environment block to stdout:

CARGO_HOME=/home/ubuntu/.cargo
DOTNET_CLI_TELEMETRY_OPTOUT=1
EDITOR=nvim
GOPATH=/home/ubuntu/go
HOMEBREW_NO_ANALYTICS=1
HTTP_PROXY=http://proxy.internal.corp:8080
HTTPS_PROXY=http://proxy.internal.corp:8080
NO_PROXY=localhost,127.0.0.1,.internal.corp
PAGER=less -R
PATH=/home/ubuntu/.cargo/bin:/home/ubuntu/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
VISUAL=nvim

If any syntax errors or invalid quotation marks exist, the generator will output a descriptive error identifying the offending line and filename.

Applying Changes to Running User Sessions

Because the environment generator executes during initial user manager instantiation (at login), updating configuration files does not automatically inject new variables into currently active processes.

To update the active systemd user manager without logging out:

  1. Import the generated environment into the active systemd user session using systemctl --user import-environment:
    systemctl --user import-environment $(/usr/lib/systemd/user-environment-generators/30-systemd-environment-d-generator | cut -d= -f1)
  2. Verify that the user manager holds the updated variables:
    systemctl --user show-environment | grep -E "(EDITOR|GOPATH|HTTP_PROXY)"
  3. Restart any user services that require the updated variables:
    systemctl --user daemon-reload

For graphical desktop applications (e.g. VS Code, Chrome, Terminal emulators), log out of the Ubuntu GNOME/KDE desktop session and log back in. The display manager will read the updated systemd user environment, propagating your variables seamlessly across all desktop software and child processes.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.