Systemd provides several robust methods for working with environment variables in service files, offering more flexibility than traditional init scripts. The main approaches include:
The simplest way is to use the Environment
directive in the [Service]
section:
[Service]
Environment=PARAM1=123
Environment=PARAM2=444
ExecStart=/usr/local/bin/daemon1 --param1=${PARAM1} --param2=${PARAM2}
For multiple variables, create an environment file (e.g., /etc/default/daemon1):
# /etc/default/daemon1
PARAM1=123
PARAM2=444
Then reference it in your service file:
[Service]
EnvironmentFile=/etc/default/daemon1
ExecStart=/usr/local/bin/daemon1 ${PARAM1} ${PARAM2}
Systemd supports parameterized services using the @
syntax:
[Unit]
Description=Daemon instance %i
[Service]
Environment=INSTANCE=%i
ExecStart=/usr/local/bin/daemon1 --instance=${INSTANCE}
Start with: systemctl start daemon1@instance1.service
For your specific wget example, here's the correct implementation:
[Service]
Environment=PARAM1=123 PARAM2=444
ExecStartPre=/bin/sh -c '/usr/bin/wget -O - --post-data="key1=${PARAM1}&key2=${PARAM2}" http://192.168.1.2/log.php'
ExecStopPost=/bin/sh -c '/usr/bin/wget -O - --post-data="key1=${PARAM1}" http://192.168.1.2/log.php'
- Variables won't expand directly in Exec* directives - wrap them in
/bin/sh -c
- Use
systemctl show --property=Environment service-name
to verify variables - Remember to
systemctl daemon-reload
after changes
- Store sensitive variables in
/etc/systemd/system/.env
with restricted permissions - Use
EnvironmentFile=-/path/to/file
(with dash) to make the file optional - Prefix variable names with your application name (e.g.,
MYAPP_PARAM1
)
When transitioning from traditional init scripts to systemd services, many developers encounter difficulties with parameterization. The example service file attempts to:
- Declare environment variables (PARAM1, PARAM2)
- Reference these variables in ExecStartPre and ExecStopPost commands
- Maintain script-like flexibility within a declarative configuration
Systemd uses a different syntax for environment variables than traditional init scripts. Here's the corrected version:
[Unit]
Description=my daemon
After=network.target
[Service]
Environment=PARAM1=123
Environment=PARAM2=444
ExecStart=/usr/local/bin/daemon1
PIDFile=/var/run/daemon1.pid
ExecStartPre=-/usr/bin/bash -c '/usr/bin/wget -O - --post-data="key1=${PARAM1}&key2=${PARAM2}" http://192.168.1.2/log.php'
ExecStopPost=-/usr/bin/bash -c '/usr/bin/wget -O - --post-data="key1=${PARAM1}" http://192.168.1.2/log.php'
Type=simple
[Install]
WantedBy=multi-user.target
Three critical techniques make this work:
# 1. Proper environment declaration
Environment=VARNAME=value
# 2. Shell invocation for variable expansion
ExecStartPre=-/usr/bin/bash -c 'command ${VARNAME}'
# 3. Escaping special characters
--post-data="key1=${PARAM1}&key2=${PARAM2}"
For more complex scenarios, consider these approaches:
# Using environment files
EnvironmentFile=/etc/default/my-daemon
# Multiple variables in one line
Environment="PARAM1=123" "PARAM2=444"
# Dynamic values with shell commands
ExecStartPre=-/usr/bin/bash -c 'export TIMESTAMP=$(date +%s); wget --post-data="ts=${TIMESTAMP}" example.com'
When variables don't expand as expected:
- Check service status:
systemctl status my-daemon
- Inspect environment:
systemctl show my-daemon --property=Environment
- Test command manually:
sudo -E bash -c 'echo ${PARAM1}'
[Unit]
Description=Python Web Service
After=network.target
[Service]
Environment=PORT=8080
Environment=HOST=0.0.0.0
ExecStart=/usr/bin/python3 -m http.server ${PORT} --bind ${HOST}
ExecStartPre=/usr/bin/bash -c 'echo "Starting on ${HOST}:${PORT}" > /var/log/web-start.log'
[Install]
WantedBy=multi-user.target