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)"