Systemd service file for cardano-node

hi all :slight_smile:

yesterday @disasm mentioned to use systemd to manage your cardano-node process and to set the limits in it, and to use SIGINT. here’s what I’m using: (of course YMMV)

[Unit]
Description=Shelley Pioneer Pool
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/local/bin/cardano-node run --topology ./config/ff-topology.json --socket-path ./socket/core-node.socket --database-path ./db/ --config ./config/ff-config.json --host-addr 0.0.0.0 --port 3000
KillSignal = "SIGINT"
RestartKillSignal = "SIGINT"
StandardOutput=journal
StandardError=journal
SyslogIdentifier=cardano-node

LimitNOFILE=32768

Restart=on-failure
RestartSec=15s
WorkingDirectory=~
User=cnode
Group=cnode

[Install]
WantedBy=multi-user.target

adapt to your PATH for cardano-node and its files/dirs. also don’t forget to create a user and update the file too.

hope this can help :slight_smile:

Cheers

4 Likes

ps: here’s my user and directory structure for reference:

root@htn-cardano-node:~# tree /home/cardano-node/
/home/cardano-node/
├── config
│   ├── ff-config.json
│   ├── ff-genesis.json
│   └── ff-topology.json
├── db
├── logs
└── socket
root@htn-cardano-node:~# id cnode
uid=1001(cnode) gid=1002(cnode) groups=1002(cnode)

you can create it with:

useradd -c "user to run cardano node" -m -d /home/cardano-node -s /sbin/nologin cnode
passwd -d cnode

I’m using it like this:

[Unit]
Description=Cardano Node                                                                                                Wants=network-online.target
After=network-online.target

[Service]
User=cardano-node
Group=cardano-node
EnvironmentFile=/opt/haskell/producer/configuration/cardano-node.environment
LimitNOFILE=16384
WorkingDirectory=/opt/haskell/producer
ExecStart=/opt/haskell/producer/cardano-node run --config $(CCONFIG) --topology $(CTOPOLOGY) --database-path $(CDB) --s$KillSignal = "SIGINT"                                                                                                   RestartKillSignal = "SIGINT"                                                                                            StandardOutput=journald
StandardError=journald
SyslogIdentifier=cardano-node
Restart=on-failure

[Install]
WantedBy=multi-user.target

I use an environment file to set the topology and stuff, this way if I want to change the files or have multiple configs I can quickly adapt without editing the service file and reloading the daemon.

3 Likes

the environment file is great! I didn’t know about that. Thank you :slight_smile:

here’s the new one with the environment, thank you @FrMixx :slight_smile:

root@htn-cardano-node:/home/cardano-node# tree /home/cardano-node/
/home/cardano-node/
├── cardano-node.environment
├── config
│   ├── ff-config.json
│   ├── ff-genesis.json
│   └── ff-topology.json
├── db
├── logs
└── socket
CONFIG="./config/ff-config.json"
TOPOLOGY="./config/ff-topology.json"
DBPATH="./db/"
SOCKETPATH="./socket/core-node.socket"
HOSTADDR="0.0.0.0"
PORT="3000"
[Unit]
Description=Shelley Pioneer Pool
After=multi-user.target

[Service]
Type=simple
EnvironmentFile=/home/cardano-node/cardano-node.environment
ExecStart=/usr/local/bin/cardano-node run --config $CONFIG --topology $TOPOLOGY --database-path $DBPATH --socket-path $SOCKETPATH --host-addr $HOSTADDR --port $PORT
KillSignal = SIGINT
RestartKillSignal = SIGINT
StandardOutput=journal
StandardError=journal
SyslogIdentifier=cardano-node

LimitNOFILE=32768

Restart=on-failure
RestartSec=15s
WorkingDirectory=~
User=cnode
Group=cnode

[Install]
WantedBy=multi-user.target
2 Likes

Did anyone get systemd to call tmux and launch the nodes from there?

@Umed_SKY I didn’t try because I prefer SimpleView over LiveView, but I suppose it’s just a matter of scripting the start (like official scripts to), and using systemd to launch the script instead of the process directly. I wouldn’t know how to handle the ExecStop or restart tho.

I use LiveView with systemd. The only drawback is, that you should not quit cardano-node while being attached to the session manually - the session will stall because there is no real tty connected to tmux. it will go crazy :wink: But other than that, it works quite well and systemd also restarts the thing if its killed/crashed. The trick to connect to the system tmux-session is to specify the tmux-socket path.

I added a system user called cardano:
sudo useradd -r -m -d /opt/cardano -s /sbin/nologin cardano

Permissions on home directory:
sudo chmod -R 770 /opt/cardano

Add your normal username to the group cardano:
sudo usermod -aG cardano *username*

Service file for systemd in /etc/systemd/system:

[Unit]
Description=Cardano Node
Requires=network.target

[Service]
Type=forking
Restart=always
RestartSec=5
User=cardano
Group=cardano
WorkingDirectory=/opt/cardano
ExecStart=/opt/cardano/service-start.sh
ExecStop=/opt/cardano/service-stop.sh
ExecReload=/opt/cardano/service-stop.sh && /opt/cardano/service-start.sh
LimitNOFILE=32768

[Install]
WantedBy=multi-user.target

I do have a subdirectory, named after the configuration in use for this node, e.g. mainnet-relay2, cotainging the topology, config, etc. Also in the home-directory /opt/cardano these scripts are present and executable.

service-start.sh:

#!/bin/bash
# Start cardano-node with settings from environment-file
source service-env.sh

CMD="cardano-node run \
  --topology ${CONFIG_HOME}/topology.json \
  --database-path ${CONFIG_HOME}/db \
  --socket-path ${CONFIG_HOME}/db/node.socket \
  --host-addr ${HOST_ADDRESS} \
  --port ${HOST_PORT} \
  --config ${CONFIG_HOME}/config.json"

# Check whether tmux session-name already exists
tmux -S ${TMUX_SOCKET} has-session -t "${TMUX_SESSION}" &> /dev/null

if [ $? -ne 0 ]; then
    # Start new session in tmux with socket path
    tmux -S ${TMUX_SOCKET} new-session -s ${TMUX_SESSION} -d ${CMD}
else
    # Message to stderr
    >&2 echo "Session \"${TMUX_SESSION}\" already exists on socket \"${TMUX_SOCKET}\"."
    exit 1
fi

service-stop.sh

#!/bin/bash

# Stop cardano-node with settings from environment-file
source service-env.sh

tmux -S ${TMUX_SOCKET} has-session -t "${TMUX_SESSION}" &> /dev/null

if [ $? -eq 0 ]; then
    echo -n "Killing session \"${TMUX_SESSION}\" on socket \"${TMUX_SOCKET}\"... "
    tmux -S ${TMUX_SOCKET} kill-session -t "${TMUX_SESSION}"
    if [ $? -eq 0 ]; then
        echo "Ok"
    else
        echo "Error"
        exit 1
    fi
else
    # Message to stderr
    >&2 echo "Session \"${TMUX_SESSION}\" not found on socket \"${TMUX_SOCKET}\"."
    exit 1
fi

service-env.sh:

#!/bin/bash

CONFIG_NAME="mainnet-relay2"
CONFIG_HOME="$(pwd)/${CONFIG_NAME}"
HOST_ADDRESS="YOUR_IP"
HOST_PORT=YOUR_PORT

# Specification of socket file allows users to share the sessions (if both in same group)
TMUX_SOCKET="${CONFIG_HOME}/tmux.socket"
# Session name
TMUX_SESSION="cardano_${CONFIG_NAME}"

service-attach.sh

#!/bin/bash

# Attach to tmux-session with settings from environment-file

source service-env.sh
tmux -S ${TMUX_SOCKET} attach-session -t "${TMUX_SESSION}"

Then, add the usual systemd reload, enable, and start. Be sure not to call the start script directly using sudo. Otherwise the service will not be able to overwrite the existing tmux.socket-file (owned by root) and cannot start at all.

When logging into your host using ssh, just execute

cd /opt/cardano/ && ./service-attach.sh

and you will be able to see the tmux-screen of the service executed as the system user cardano.

:slight_smile:

4 Likes

Hi Hanswurst!

Thanks for the sharing this! Helps a lot.

Could you kindly clarify this sentence
“The only drawback is, that you should not quit cardano-node while being attached to the session manually - the session will stall because there is no real tty connected to tmux.”

  • Dummy question here but what is tty? and how would you recommend manually closing a running node? For instance if we update cardano-node etc…

Thank you!

Wiki for tty; Here the instance of a programm that let’s you input commands and executes them.

To start, stop or restart cardano node you would use

sudo systemctl [start/stop/restart] cardano

Using this you don’t have any problem with permissions for the tmux.socket and your terminal (=tty) being stuck in nowhere, because there was no terminal launched before starting tmux.

1 Like

Thanks! I think I’m getting a better understanding of how this is put together.
Your scripts are amazing, thanks for sharing!

Perhaps you could help me here with a question on the new user added - cardano. If I understood it correctly, this user doesn’t require a passwd and has permissions to access home. Wouldn’t this be a security issue for the node?

The systemuser cardano can not login (shell is /sbin/nologin) but is used to run the service. It can access its own home directory /opt/cardano- where all data resides. How else could it write new blocks to the database?

I just seen it. If you still need it I ll send you the service file when I get out of the bed and had some brekkie/brunch, which is very unlikely would happen soon on Saturday morning.:slight_smile: Probably in an hour or two.

Or just paste here.

Here comes if you still need it.
It could be simpler (i.e. you do not need any session conf file stored),
but I did not want to spend too much time on it when I was applying it.

$ cat /etc/systemd/system/cardano@.service
# The `shmn` is a service user.
#
# sudo cp cardano\@.service /etc/systemd/system &&
# sudo systemctl start cardano@shmn.service &&
# systemctl is-active cardano@shmn.service ||
# systemctl enable cardano@shmn.service
#
# Doc for %h %u in man -s 5 systemd.unit

[Unit]
Description=Cardano's Staking Pool Node (%i)
After=multi-user.target

[Service]
Type=forking
Restart=on-failure
RestartSec=5s
User=%i
Group=ops

# As the service user i.e. `shmn` Shelley Haskell Main Net
# mkdir -p "/opt/cardano/shmn/.config/tmux/sessions"
# cat << EOF > "/opt/cardano/shmn/.config/tmux/sessions/cardano"
# send-keys -t Cardano "~/scripts/node.sh \; exit \$?" Enter
# EOF
ExecStart=/usr/bin/tmux new-session -d -s Cardano
ExecStartPost=/usr/bin/tmux source-file /opt/cardano/%i/.config/tmux/sessions/cardano
ExecStop=/usr/bin/tmux kill-server

LimitNOFILE=16384

[Install]
WantedBy=multi-user.target
2 Likes

The discussion of systemd templates has also developed a bit on this page:

Hey @hanswurst,

thanks for sharing your script. Could you explain why u prefer type=forking instead of type=simple? Most of the service files I’ve seen were using type=simple!

Thank you :slight_smile:

This service file was intended to be used with LiveView (“those were the times…” :wink: ). Instead of executing a single file with parameters it calles bash, which executed the script which executed cardano-node and tmux.

My current service-file looks similar to everybody else’s:

[Unit]
Description=Cardano Node
Requires=network.target

[Service]
Type=simple
SyslogIdentifier=cardano-node
Restart=always
RestartSec=5
User=cardano
Group=cardano
KillSignal=SIGINT
LimitNOFILE=32768
WorkingDirectory=/opt/cardano/cardano-node-mainnet
ExecStart=cardano-node run --topology ./topology.json --database-path ./db --socket-path ./db/node.socket --host-addr [insert public IP] --port [insert port] --config ./config.json

[Install]
WantedBy=multi-user.target
1 Like