15

I need to get the IP of the machine and use it inside my service:

[Unit]
Description=Redmine server
After=syslog.target
After=network.target

[Service]
Type=simple
User=redmine
Group=redmine
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

# Give a reasonable amount of time for the server to start up/shut down
TimeoutSec=300

[Install]
WantedBy=multi-user.target

How can I execute this command inside the systemd service file and get its result:

ip=$(/sbin/ip -o -4 addr list eno16777736 | awk '{print $4}' | cut -d/ -f1)

in order to use its result infor xxx.xxx.xxx.xxx, sth like this:

ExecStart=/usr/bin/ruby /home/redmine/redmine/bin/rails server webrick -e production -p 3000 -b $ip
1

4 Answers 4

14

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.

7
  • 1
    This is for a normal script. But my question was how to do it in a sth.service file in order to supply the current IP address of that interface.
    – elekgeek
    Commented Sep 5, 2015 at 12:03
  • 1
    but this is not working, look at the service code in the first post, this is not working..
    – elekgeek
    Commented Sep 6, 2015 at 19:57
  • 1
    btw, this is a service, and when I view the status, it shows that $ip is not returning any results, and hence the ip is empty and the service fails to start..
    – elekgeek
    Commented Sep 6, 2015 at 20:44
  • Thank you for your effort, but %H will not work, option -b needs an IP, not a hostname. Ihave been trying to get the specifier for the machine IP, but apparently I can't find it, or don't know how to search for it.
    – elekgeek
    Commented Sep 7, 2015 at 12:06
  • 1
    Now this is a good answer (however many iteration it required to reach that state). Single point of reference for all the research I ended up doing and more, too.
    – Samveen
    Commented Sep 16, 2020 at 6:43
8

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
1
  • Confirmed working with systemd 228
    – mleu
    Commented Dec 21, 2017 at 13:15
6

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

3

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)"
1
  • hostname -i will return a ip list when there are multiple NICs Commented Dec 1, 2021 at 9:23

You must log in to answer this question.

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