How to get the machine IP address in a “systemd” service file

Solution 1:

Getting machine hostname with systemd.

While I am not deeply familiar with systemd looking over this tutorial website as well as the official systemd man pages, what you seem to be looking for is a “specifier” value:

Many settings resolve specifiers which may be used to write generic unit files referring to runtime or unit parameters that are replaced when the unit files are loaded.

And the specific specifier I believe would work here is %H which is for “Host name” and described as:

The hostname of the running system at the point in time the unit configuration is loaded.

So checking your example systemd script, change your [Service] chunk to be this:

[Service]
Type=simple
User=redmine
Group=redmine
ExecStart=/usr/bin/ruby /home/redmine/redmine/bin/rails server webrick -e production -b %H -p 3000

Note the line with the ip=() assignment is gone and the ExecStart command now uses %H instead of $ip.

An idea for getting an IP address with systemd.

That said, it seems that systemd only provides a hostname via the %H “specifier.” Which is odd if you ask me. So while I have no deep experience with systemd, I believe I understand what could be done to achieve the goal of this post.

The key would be to setup an EnvironmentFile for systemd to read. Read up on how to use an EnvironmentFile over here on this site.

So let’s say you created a simple Bash script like this; let’s name it write_ip_to_file.sh and feel free to change the IP address fetching logic of ip=$() to match what works on your setup:

#!/bin/bash
ip=$(/sbin/ifconfig eth0 | awk '/inet addr/ {split ($2,A,":"); print A[2]}');
echo IP=$ip > ~/ip.txt;

All that would do is output the IP address of eth0 to a text file named ip.txt in your home directory. The format would be something like this:

IP=123.456.789.0

Got that? Good. Now in your systemd script, change your [Service] chunk to be something like this; be sure to set [your username] to match the username of the directory where ip.txt is saved:

[Service]
Type=simple
User=redmine
Group=redmine
EnvironmentFile=/home/[your username]/ip.txt
ExecStart=/usr/bin/ruby /home/redmine/redmine/bin/rails server webrick -e production -b $IP -p 3000

And what that would do is load the config in ip.txt and assign $IP the value of 123.456.789.0. I believe that is what you are looking for.

The key factor here is to get write_ip_to_file.sh to run on boot or perhaps even by the systemd script itself.

Another idea for getting an IP address with systemd.

But with that said, I have a better idea (if it works): Move that whole ExecStart command into the Bash file called redmine_start.sh and make sure the system can read and execute it. The contents of redmine_start.sh would be as follows; feel free to change the IP address fetching logic of ip=$() to match what works on your setup:

#!/bin/bash
ip=$(/sbin/ifconfig eth0 | awk '/inet addr/ {split ($2,A,":"); print A[2]}');
/usr/bin/ruby /home/redmine/redmine/bin/rails server webrick -e production -b $IP -p 3000

And then change your [Service] chunk to be something like this; be sure to set [your username] to match the username of the directory where redmine_start.sh is saved:

[Service]
Type=simple
User=redmine
Group=redmine
ExecStart=/home/[your username]/redmine_start.sh

If you follow the logic, if all of the logic of ExecStart is contained in redmine_start.sh then you can use that Bash trick to get the IP address, assign it to a variable and then start Redmine there. The systemd script would just be managing when/how to start that.

Getting machine IP address with init.d.

And for the reference of init.d users, I use Ubuntu and when I need to grab a current working system IP address in a Bash or an init.d startup script I run something like this:

ip=$(/sbin/ifconfig eth0 | awk '/inet addr/ {split ($2,A,":"); print A[2]}')

Of course you need to change /sbin/ifconfig to match the location of ifconfig on your system, and then also change eth0 to match the network interface you want to get the IP address of.

But once adjusted to match your setup and needs, that successfully gets an interface’s IP address and assigns it to the variable ip which can then be accessed as $ip in your script.

Solution 2:

Maybe this construction will work. Try it:

[Service]
Type=simple
User=redmine
Group=redmine
PermissionsStartOnly=true
ExecStartPre=/bin/bash -c "/bin/systemctl set-environment ip=$(/sbin/ip -o -4 addr list eno16777736 | awk '{print $4}' | cut -d/ -f1)"
ExecStart=/usr/bin/ruby /home/redmine/redmine/bin/rails server webrick -e production -b ${ip} -p 3000

Solution 3:

Use host IP addresses and EnvironmentFile

You can write your host IP addresses into /etc/network-environment file using setup-network-environment utility. Then you can run your app following way:

[Unit]
Requires=setup-network-environment.service
After=setup-network-environment.service

[Service]
EnvironmentFile=/etc/network-environment
ExecStart=/opt/bin/kubelet --hostname_override=${DEFAULT_IPV4}

Source: https://coreos.com/os/docs/latest/using-environment-variables-in-systemd-units.html

Solution 4:

Use hostname -i to get your (first) IP address and save it in SystemD's environment. You can access this variable with ${HOST_IP}.

Documentation:

  • man hostname for parameters -i and -I
  • man systemctl for set-environment
  • man systemd.service for ExecStartPre

Example

[Service]
ExecStartPre=/bin/sh -c "systemctl set-environment HOST_IP=$(hostname -i)"