How to Use Environment Variables in systemd Service Files for Parameterized Commands


2 views

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
  1. Store sensitive variables in /etc/systemd/system/.env with restricted permissions
  2. Use EnvironmentFile=-/path/to/file (with dash) to make the file optional
  3. 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:

  1. Check service status: systemctl status my-daemon
  2. Inspect environment: systemctl show my-daemon --property=Environment
  3. 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