Writing a systemd unit file with a environment-set executable path

From the "Command lines" section in systemd.service(5):

Note that the first argument (i.e. the program to execute) may not be a variable.

I was going to suggest using the instance specifier %i (you can read more about it in systemd.unit(5)), but (now we're back in systemd.service(5)):

the first argument of the command line (i.e. the program to execute) may not include specifiers.

I think the best option at this point really is creating a shell script that wraps the execution of the java binary as suggested by Warren Young or you could ExecStart a shell directly like in the example for shell command lines in the "Command Lines" section of systemd.service(5) which has the following example:

ExecStart=/bin/sh -c 'dmesg | tac'

so you could do (untested):

ExecStart=/bin/sh -c '${JAVA_HOME}....'

Another similar option is to use /usr/bin/env:

ExecStart=/usr/bin/env "${JAVA_HOME}/bin/java" -jar ...

This way you can omit ' quotes around the whole command which is useful if you need to nest quoted stuff.

PS. As a side note, it is important to enclose variable names in {braces} in Systemd files or else they will not be recognized correctly.


Another, rather different option assumes usage of another system tool: alternatives. If your Java SDKs come from system packages, then most probably you are already set. If you install them by hand, you would have to add them to the system with something like that:

alternatives --install /usr/bin/java java /path/to/your/sdk/bin/java 3

The last number is priority (higher is more important). You can check existing priorities by issuing command alternatives --display java so you can decide what priority to pick for your new SDK.

Once installed you can just use /usr/bin/java in your service file and run alternatives --config java before starting service to determine which version you'd like to choose. Haven't really tried it, but it appears that you can do something like:

alternatives --set java /path/to/your/sdk/bin/java

...and have your SDK chosen from some script. This might be an option if the interactive interface for alternatives --config isn't suitable for your scenario. You might even do the setting in service file with ExecStartPre directive.

I really dislike wrapper script. Much of its contents probably will be identical to dreaded SysV init script (at least it's start section). Most probably service file starting with wrapper script would have to be a bit complicated by the fact, that command being executed isn't the process being run. If you can take time to go through many systemd directives, you just might find a cleaner way to achieve what you want without sticking to clunky wrapper scripts.

Tags:

Java

Systemd