90

Is it possible to give my systemd service more than one argument?

I'd like to execute a program with multiple arguments which have to be decided by the final user.

E.g: ./program arg1 arg2

To start it a single argument app I'd need something like systemctl start arg1@program, where in the service definition I have ExecStart = /usr/bin/program ℅i.

Thanks!

3
  • 1
    Sounds like what you want is a config file.
    – Daniel B
    Commented Mar 14, 2014 at 10:27
  • I need to change it on the go. Do I strictly need a conf file?
    – peperunas
    Commented Mar 14, 2014 at 10:33
  • @peperunas you don't need a conf file, see my answer which works without any extra files
    – nonagon
    Commented Jan 4, 2018 at 16:23

4 Answers 4

71

Yes you can! Define them in a file somewhere and add them to EnvironmentFile in your systemd Service. For example, say the contents of /etc/.progconf are:

ARG1=-o
ARG2=--verbose

And your .service file:

EnvironmentFile=/etc/.progconf
ExecStart=/usr/bin/prog ${ARG1} ${ARG2}

You can write to that file if you need to change them on the go. A service shouldn't change its options very often, maybe consider autostarting or cron if you need to achieve that.

For more examples check: https://wiki.archlinux.org/index.php/Systemd/Services

9
  • 1
    Heh, quite handy, didn't think of that. I have to agree though: Service parameters don't change on a regular basis, and neither do their config files.
    – Daniel B
    Commented Mar 14, 2014 at 11:55
  • if the service file uses environment variables, can you say VAR1=... VAR2=... systemctl start foobar.service to pass the variables? Commented Jun 10, 2015 at 18:16
  • 8
    @JohannesSchaub-litb, no, you can't. There is a PassEnvironment directive, but it takes variables from the systemd process (normally PID 1), not from systemctl. Environment variables from the systemctl process don't get propagated to the service being started.
    – cjm
    Commented Mar 31, 2016 at 19:55
  • 2
    But upstart can run multiple instances of the same service, with different parameters. For example a mail server on eth0 and another instance of said mailserver on eth1, passing the parameter to upstart and managing them as separate services. Can systemd do this?
    – LtWorf
    Commented Jan 26, 2017 at 10:03
  • 1
    It looks like the variable name needs to be enclosed in curly braces, a la ${varname}, according to what I see in the reference (and my experience) Commented Apr 24, 2023 at 21:03
48

I wanted to do the same thing, but without a separate file for each combination of arguments. I found that I could pass one long argument with spaces and then use systemd's environment variable space-splitting feature to separate the arguments.

I made a service with filename [email protected] (note the trailing 'at sign' which is required when a service takes arguments).

[Unit]
Description=Test passing multiple arguments

[Service]
Environment="SCRIPT_ARGS=%I"
ExecStart=/tmp/test.py $SCRIPT_ARGS

I run this with sudo systemctl start argtest@"arg1 arg2 arg3".service and it passes arg1, arg2 and arg3 as separate command-line arguments to test.py.

11
  • "note the trailing ampersand": I find no ampersand in your reply. Can you edit your reply to be clearer on this point? Commented Apr 4, 2018 at 23:12
  • Yes, sorry about that!
    – nonagon
    Commented Apr 5, 2018 at 2:20
  • I think the @ is only needed when you use the %I as you do. It's an instance of the service.
    – Toby
    Commented Apr 9, 2018 at 12:21
  • 3
    On my debian I get Invalid unit name "argtest@arg1 arg2.service" was escaped as "argtest@arg1\x20arg2.service" (maybe you should use systemd-escape?)
    – sgargel
    Commented Nov 29, 2021 at 9:35
  • 1
    > maybe you should use systemd-escape? sudo systemctl start $(systemd-escape --template [email protected] "arg1 arg2 arg3") Commented Apr 28, 2022 at 17:48
7

Easiest I have found is:

ExecStart=/bin/bash -c "\"/path/with spaces/to/app\" arg1 arg2 arg3 \"arg with space\""

Keeps it all self contained.

Having said that, I have found that at least on Ubuntu 18.04 LTS, I don't even need to do that, I can do this and it works fine:

ExecStart="/path/with spaces/to/app" arg1 arg2 arg3 "arg with space"

$vars work as arguments with this pattern as well.

3

Tell your users to override the service by creating a drop-in snippet through the use of the edit option in systemctl.

Firstly, start with some sane defaults. Let's assume the ExecStart part of your service declares the following:

[Service]
ExecStart=./program arg1

Now any user of this service can decide that they want to use a different set of arguments for running the program. They can do that by creating a drop-in snippet with:

systemctl edit myservice.service

In the new file, they simply write the following to override the ExecStart:

[Service]
ExecStart=
ExecStart=./program arg1 arg2

The benefit of doing it this way is that the end result does not involve modifying the system-installed service which may conflict with some package managers.

And that's it!

Now just reload systemd (systemctl daemon-reload) and restart the service (systemctl restart myservice.service).

When the service restarts, it will always also load the user's modifications.

I hope that helps


Sources:

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .