Why this matters
Real services need a little work around the main program: check the config file exists before starting, clean up a leftover file, tell the program to re-read its settings. They also need settings - which database, which port, and sometimes a password. How you hand over that password decides who else on the box can read it.
What you need to know already: 2.3 (drop-ins, Environment=), 2.10 (signals, kill), 2.5 (User=).
The exec family
Besides ExecStart=, a service can run commands before, after and around it:
[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.yaml
ExecStartPre=-/usr/bin/rm -f /run/app.lock
ExecStart=/opt/app/bin/orders-server
ExecStartPost=/usr/local/bin/notify-deploy.sh
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/usr/local/bin/drain.sh
(test -f FILE succeeds only if FILE exists; rm -f deletes without complaining if the file is not there.)
ExecStartPre=runs before the main process, and a failure aborts the whole start. Several are allowed, run in order.- The
-prefix means "ignore the exit status of this one". Without it, thermabove failing (because the lock file was not there) would stop the service from starting at all. ExecReload=is whatsystemctl reloadruns: "re-read your settings without restarting". Here it sends SIGHUP (the "hang up" signal, which many daemons treat as "reload your config").$MAINPIDis one of the few variables systemd fills in for you. No ExecReload meansreloaderrors out.ExecStop=runs before SIGTERM is sent - the place to finish in-progress work first (called draining).
Other prefixes exist (+ runs that one command as root, ignoring User= and the sandbox of 2.26); you rarely need them.
Environment
An environment variable is a named value (KEY=VALUE) handed to a program when it starts; the program reads it like a setting. Your shell has them too: echo $HOME prints one.
Environment=LOG_LEVEL=info
Environment="APP_NAME=orders api" # quote if it contains spaces
EnvironmentFile=/etc/default/orders # a file, one KEY=VALUE per line
EnvironmentFile=-/etc/default/orders.local # the - means "fine if missing"
The - prefix on EnvironmentFile is the difference between "optional local override" and "service fails to start on a machine that does not have it".
Environment files are not shell scripts. No export, no $OTHER to refer to another variable, no $(command). systemd reads plain KEY=VALUE lines.
Environment is fixed when the process starts: after changing it you must restart the service; daemon-reload alone only updates systemd's copy.
Environment variables are not a secret store
A secret is a value only the service should know: a password, an API key. Anything in Environment= is visible to:
systemctl show -p Environment orders # any user, no privileges
sudo cat /proc/<PID>/environ | tr '\0' '\n' # root, or the service's own user
/proc is a folder the kernel fills with live information about every process; /proc/<PID>/environ holds that process's environment, separated by invisible zero bytes - tr '\0' '\n' swaps each one for a new line so you can read it. (Chapter 4 tours /proc and /sys.)
systemctl show needs no privileges at all. A password in Environment= is readable by every user on the box. EnvironmentFile= is better only because you can lock the file so only root can read it (chmod 600, chapter 4) - the value is still in /proc/PID/environ.
The systemd-native answer:
LoadCredential=db-password:/etc/creds/db-password
systemd (as root) reads the file and gives the service its own copy at $CREDENTIALS_DIRECTORY/db-password - a private folder kept in memory, readable only by that service's user, not in the environment (so not in systemctl show or /proc/PID/environ), gone when the unit stops. The program reads the password from that file.
Bigger setups use a dedicated secrets service, but the principle is the same: a file only the service can read, never an environment variable.
What you can now do
- add pre/post/reload/stop commands to a service
- pass settings with
Environment=andEnvironmentFile= - explain why a password in the environment is readable by everyone, and what to use instead