- How to set environment variable in systemd service?
- 6 Answers 6
- Constant value
- Variable value
- How to set global environment variables to all services?
- 2 Answers 2
- How to load environment variables for the process of a systemd service?
- 1 Answer 1
- pass environment variables to services started with systemctl
- 2 Answers 2
How to set environment variable in systemd service?
I have an Arch Linux system with systemd and I’ve created my own service. The configuration service at /etc/systemd/system/myservice.service looks like this:
[Unit] Description=My Daemon [Service] ExecStart=/bin/myforegroundcmd [Install] WantedBy=multi-user.target
6 Answers 6
Times change and so do best practices.
The current best way to do this is to run systemctl edit myservice , which will create an override file for you or let you edit an existing one.
In normal installations this will create a directory /etc/systemd/system/myservice.service.d , and inside that directory create a file whose name ends in .conf (typically, override.conf ), and in this file you can add to or override any part of the unit shipped by the distribution.
For instance, in a file /etc/systemd/system/myservice.service.d/myenv.conf :
[Service] Environment="SECRET=pGNqduRFkB4K9C2vijOmUDa2kPtUhArN" Environment="ANOTHER_SECRET=JP8YLOc2bsNlrGuD6LVTq7L36obpjzxd"
Also note that if the directory exists and is empty, your service will be disabled! If you don’t intend to put something in the directory, ensure that it does not exist.
For reference, the old way was:
The recommended way to do this is to create a file /etc/sysconfig/myservice which contains your variables, and then load them with EnvironmentFile .
For complete details, see Fedora’s documentation on how to write a systemd script.
I guess the sysconfig path is specific to Fedora but the question is about Arch Linux. The answer by paluh is more interesting I think
/etc/sysconfig is Fedora-specific. AFAIR Arch Linux was pushing for having the config files somewhere package-specific rather in /etc rather than that Fedora-specific location. Like /etc/myservice.conf , though using extra file doesn’t seem the right way here.
No, no, no. /etc/sysconfig is not recomended. It is discouraged, along with /etc/default/* from debian, because they are pointless, and the names are meaningless and make sense only for backwards compatibility reasons (all of /etc is about configuration of the system, not just /etc/sysconfig, and /etc/defaults is for overrides, not the defaults). Just put the definitions directly in the unit file, or if it is not possible, in an enviornment file that has a package specific location (like Michał’s comment suggests).
The answer depends on whether the variable is supposed to be constant (that is, not supposed to be modified by user getting the unit) or variable (supposed to be set by the user).
Since it’s your local unit, the boundary is quite blurry and either way would work. However, if you started to distribute it and it would end up in /usr/lib/systemd/system , this would become important.
Constant value
If the value doesn’t need to change per instance, the preferred way would be to place it as Environment= , directly in the unit file:
[Unit] Description=My Daemon [Service] Environment="FOO=bar baz" ExecStart=/bin/myforegroundcmd [Install] WantedBy=multi-user.target
The advantage of that is that the variable is kept in a single file with the unit. Therefore, the unit file is easier to move between systems.
Variable value
However, the above solution doesn’t work well when sysadmin is supposed to change the value of the environment variable locally. More specifically, the new value would need to be set every time the unit file is updated.
For this case, an extra file is to be used. How — usually depends on the distribution policy.
One particularly interesting solution is to use /etc/systemd/system/myservice.service.d directory. Unlike other solutions, this directory is supported by systemd itself and therefore comes with no distribution-specific paths.
In this case, you place a file like /etc/systemd/system/myservice.service.d/local.conf that adds the missing parts of unit file:
[Service] Environment="FOO=bar baz"
Afterwards, systemd merges the two files when starting the service (remember to systemctl daemon-reload after changing either of them). And since this path is used directly by systemd, you don’t use EnvironmentFile= for this.
If the value is supposed to be changed only on some of the affected systems, you may combine both solutions, providing a default directly in the unit and a local override in the other file.
How to set global environment variables to all services?
I have seen that it should be possible, by using DefaultEnvironment= in /etc/systemd/system.conf . However, I’m not sure whether I can use expression expansions like PATH=$PATH:/whatever/path or not. So, how can I set global default service environments, with expression expansions?
systemd docs say that for Environment= «Variable expansion is not performed inside the strings and the «$» character has no special meaning., and for DefaultEnvironment= it only says that % -specifier expansions (like %H for host name) are applied. But it should be easy to find out what PATH=$PATH:/whatever/path results in?
Thanks for the note, so normal expansion isn’t supported in systemd, good to know! (i saw the % but missed the part about the $)
2 Answers 2
Other answers about setting PATH in /etc/environment or other places may work if you are calling shell commands from your units. However, if you you are trying to run:
And you want mycommand to be derived from $PATH , then this is not going to work. systemd does not use $PATH to resolve these. It uses a list of paths which are defined at compile-time.
If the command is not a full (absolute) path, it will be resolved to a full path using a fixed search path determined at compilation time. Searched directories include /usr/local/bin/, /usr/bin/, /bin/ on systems using split /usr/bin/ and /bin/ directories, and their sbin/ counterparts on systems using split bin/ and sbin/. It is thus safe to use just the executable name in case of executables located in any of the «standard» directories, and an absolute path must be used in other cases. Using an absolute path is recommended to avoid ambiguity. Hint: this search path may be queried using systemd-path search-binaries-default .
The best practice is to use absolute paths when using Exec*= fields. This avoids the need for $PATH , and ensures the behavior of your service cannot change if someone sneaks an extra binary somewhere else like /usr/local/bin . Usually, non-templated services are not designed to be dynamic (designed to change contents or intent based on the local environment).
How to load environment variables for the process of a systemd service?
i am developing some services scripts that need to be executed on boot on Raspbian (Jessie) and i decided to use systemd. I just started to read some quick tutorials on how to use it, but I have problems with the environment. The processes that are executed require (in their code) some environment variables that i set in a shell script but they aren’t loaded, despite the use of an EnvironmentFile=/path/to/my/file. I have a service executing a Python3 script and another for a Node.js app. I have been searching for alternative solutions since yesterday, but nothing seems to work. Maybe I just didn’t understand how systemd works ? It must be possible to do so, so i am asking you. Here are my unit files: For the python script:
[Unit] Description=My awesome python script After=multi-user.target [Service] ExecStart=/usr/local/bin/python3.6 /home/pi/Desktop/myawesomescript.py Restart=Always RestartSec=5 StandardOutput=syslog StandardError=syslog SyslogIdentifier=mypython User=pi EnvironmentFile=/home/pi/.bash_vars [Install] WantedBy=multi-user.target
[Unit] Description=My awesome Node.js socket.io app Requires=After=mypython.service # Requires the python script to be running [Service] ExecStart=/usr/bin/node /home/pi/Desktop/myawesomenodeapp/src/index.js Restart=always RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=mynodeapp User=pi EnvironmentFile=/home/pi/.bash_vars [Install] WantedBy=multi-user.target
Thank you for taking your time and sorry for my bad english ! PS: Please let me know if you need more informations
Actually, DarkKnight’s answer helped me to understand what I missed. Thank you anyway for the suggestion, I will take a look and see if its features could be useful !
1 Answer 1
Environment can be set in systemd service file as below under Exec options
Environment=LD_LIBRARY_PATH=/usr/lib
Below is the official documentation of systemd Environment/EnvironmentFile usage
Environment=
Sets environment variables for executed processes. Takes a space-separated list of variable assignments. This option may be specified more than once, in which case all listed variables will be set. If the same variable is set twice, the later setting will override the earlier setting. If the empty string is assigned to this option, the list of environment variables is reset, all prior assignments have no effect. Variable expansion is not performed inside the strings, however, specifier expansion is possible. The $ character has no special meaning. If you need to assign a value containing spaces or the equals sign to a variable, use double quotes («) for the assignment.
Environment=»VAR1=word1 word2″ VAR2=word3 «VAR3=$word 5 6» gives three variables «VAR1», «VAR2», «VAR3» with the values «word1 word2», «word3», «$word 5 6».
See environ(7) for details about environment variables.
EnvironmentFile=
Similar to Environment= but reads the environment variables from a text file. The text file should contain new-line-separated variable assignments. Empty lines, lines without an «=» separator, or lines starting with ; or # will be ignored, which may be used for commenting. A line ending with a backslash will be concatenated with the following one, allowing multiline variable definitions. The parser strips leading and trailing whitespace from the values of assignments, unless you use double quotes («).
The argument passed should be an absolute filename or wildcard expression, optionally prefixed with «-«, which indicates that if the file does not exist, it will not be read and no error or warning message is logged. This option may be specified more than once in which case all specified files are read. If the empty string is assigned to this option, the list of file to read is reset, all prior assignments have no effect.
The files listed with this directive will be read shortly before the process is executed (more specifically, after all processes from a previous unit state terminated. This means you can generate these files in one unit state, and read it with this option in the next).
Settings from these files override settings made with Environment=. If the same variable is set twice from these files, the files will be read in the order they are specified and the later setting will override the earlier setting.
pass environment variables to services started with systemctl
I have a nodeJS service built using NodeJs. This service requires some environment variables to be passed to it. Moreover, I created a systemd unit file to start it from systemctl. For some weird reasons, the service, when started with systemctl, does not read the environment variables. For instance, one environment variable is the HOST, which contains the IP to which the sails app will be binded. However, if I start the service with sails lift or node app.js, it does read the environment variables. Here is the unit file:
[Unit] Description=project [Service] ExecStart=/usr/bin/node /mnt/project/app.js Restart=always StandardOutput=syslog [Install] WantedBy=multi-user.target
I tried everything. I added the environment variables to /etc/environment and pointed the unit file to it, I also added them to the unit file, but nothing worked.
2 Answers 2
This happens because a unit in systemd is encapsulated from rest of the system. Have you tried setting it directly in the unit file like this?
[Service] Environment="FOO=foo" 'BAR=bar'
The variables FOO and BAR are then available in your unit. For example if this is my ExecuteStart:
With the Environment option from above this would result in the following output:
An alternative would be doing it via a environment config file. For this you can read the fedora dokumentation:
This link contains some useful examples with docker: