Setting up Gitea’s “Act Runner” daemon on Devuan (SysVinit)

[Skip to the TL;DR if you just want to see the sysv init script.]

I’ve been shuffling some things around in the background, and the main project I’ve been working on lately has a few different places where it makes use of these newfangled “CI/CD” monstrosities. Part of the shuffle has been a shift away from foggy, centralized services to in-house DIY services, and in the course of that effort we’ve started using Gitea for our own web-facing Git hosting.

(I know, I know, you don’t even need a web interface to run a git host… but not every kind of developer is aware of or down with that, and there are some neat little conveniences that come with it, especially in the context of open source projects. Anyway, Gitea is refreshingly lightweight and polished. This seems a decent tradeoff, and running Gitea on real hardware makes browsing code in a web-git thingy snappy for the first time, which is neat.)

So… that said… I needed to set up Gitea’s “runner” service, Act Runner.

Setting Act Runner up on the mainstreamest of monocultural mainstream Ubuntu using Docker is, of course, the most heavily documented path. With the exception of not using Docker, this is what I’ve done.

Side note: Why not Docker? Well… aside from Docker being its own world of problems, consider this note on Gitea’s helpful Act Runner guide page:

If using Docker, the act_runner user should also be added to the docker group before starting the service. Keep in mind that this effectively gives act_runner root access to the system.

I’m not kidding. Here’s a screenshot:

Unfortunately, the default Act Runner config that act_runner generate-config will gleefully barf out makes a lot of Docker-centric assumptions, so at the end of this post I’ll put up an alternative that seems to have worked for me so far.

TL;DR

Get the binary, copy your token from inside Gitea, etc. as indicated here:
https://docs.gitea.com/usage/actions/act-runner

Add a user named “act_runner”. Make his home /opt/act_runner. Copy the executable to /opt/act_runner/act_runner. Make sure you chown -R act_runner:act_runner /opt/act_runner also. In the example below we’re going with /opt/act_runner, but it could be anything — just adjust the script and config accordingly.

Add a script in /etc/init.d/act_runner that looks like this:

#!/bin/bash
#
# act_runner    Startup script for Gitea's "Act Runner" service
#
# chkconfig: 345 99 01
# description: Act Runner is the Gitea CI/CD worker daemon
# processname: act_runner

### BEGIN INIT INFO
# Provides: act_runner
# Required-Start: $local_fs $network $named $time
# Required-Stop: $local_fs $network
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# Short-Description: Start and stop the Act Runner service
# Description: Act Runner is the Gitea CI/CD worker daemon
### END INIT INFO

AR_BIN="/opt/act_runner/act_runner"
AR_HOME="/opt/act_runner"
AR_USER="act_runner"
AR_LOG="/opt/act_runner/log/act_runner.log"

# Set PID file location
PID_FILE="/var/run/act_runner.pid"

start() {
    echo "Starting Gitea Act Runner..."
    # Check if Act Runner is already running
    if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE"); then
        echo "Act Runner is already running."
        return 0
    fi

    # Start the act_runner process
    start-stop-daemon --start --oknodo --pidfile $PID_FILE --make-pidfile --remove-pidfile --chuid $AR_USER --group $AR_USER --chdir $AR_HOME --background --output $AR_LOG --startas $AR_BIN -- daemon --config $AR_HOME/config.yaml
    echo "Act Runner started with PID $(cat $PID_FILE)."
}

stop() {
    echo "Stopping Gitea Act Runner..."
    # Check if Act Runner is running and stop it
    if [ ! -f "$PID_FILE" ] || ! kill -0 $(cat "$PID_FILE"); then
        echo "Act Runner is not running."
        return 0
    fi

    kill $(cat "$PID_FILE")
    rm -f "$PID_FILE"
    echo "Act Runner stopped."
}

restart() {
    echo "Restarting Act Runner..."
    stop
    start
}

status() {
    if [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE"); then
        echo "Act Runner is running (PID $(cat $PID_FILE))."
    else
        echo "Act Runner is not running."
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

exit 0

[NOTE: If you don’t happen to be familiar with init scripts from the era before systemd decided to make the entire Linux universe vulnerable to a single exploit at some point in the future, the above is essentially the standard init script template, adjusted ever so slightly for this particular use.]

[NOTE 2: I have just become aware of a fantastic program start-stop-daemon that handles every wacky case of weirdness that can turn up when daemonizing daemons, programs that have no idea what daemonization is, and everything in between. There doesn’t seem to be a good resource on explaining these cases I can find easily, so I may write one. Anyway, take this script with a grain of salt until I iron this out. The above does work and is safe, but may not be as clean as whatever I adapt it to later.]

Because we’re running out of /opt/act_runner/work instead of using a Docker container, we’ll need a slightly modified /opt/act_runner/config.yaml :

log:
  level: info

runner:
  file: /opt/act_runner/.runner
  capacity: 1
  timeout: 3h
  insecure: true
  fetch_timeout: 5s
  fetch_interval: 2s
  labels:
    - "linux_amd64:host"

host:
  workdir_parent: /opt/act_runner/work

cache:
  enabled: true
  dir: "/opt/act_runner/cache"
  host: ""
  port: 0
  external_server: ""

container:
  network: ""
  privileged: false
  options:
  workdir_parent:
  valid_volumes: []
  docker_host: ""
  force_pull: true
  force_rebuild: false

Do read the little blurb on “labels” in the Gitea docs. They have some semantic significance in a few cases (in particular, when going off the beaten path and not using Docker).

Postscript

If you are new to SysV in general, please read and understand the init script above.

The way SysV init works is actually pretty slick: the init process calls a top-level script which then calls on a set of initialization scripts, typically one for each service. The membership and ordering of those scripts defines the startup service context of the system. It’s super simple and wonderfully explicit. There are no hidden items: the scripts just do what they say and that’s that.

Back in the day RedHat forced systemd on us claiming that systemd would make startup “parallel” and ultra crazy lolspeed mega fast. Like half a second. That never happened. SysV init clocks about the same time as systemd, systemd never really got parallel startup going, the whole thing works like an MS product underneath (binary log files unreadable without special tooling and all), and the original target case, “bringing Linux to mobile!” only sorta-kinda happened via Google’s manifestation of Android — and booting any phone takes comparative eons today.

So bah humbug, Leonnart!

(To be fair, Leonnart was always very patient and civil with the frustrated among us — but that doesn’t fix the underlying issue.)

Fortunately projects like Devuan and Gentoo make running non-systemd smooth, but it does require that you recall some of the graybeard knowledge (like how to write init scripts) that seem to be increasingly lost to time, digital rot, and the tricky problem of search engines having agendas.

[Another side note for the younglings: Gentoo is best used as a binary-distro generator, a “meta-distro”, rather than a direct desktop/server distro where you rebuild the world every time something changes. Rebuilding the world is fun for people who are into that sort of thing, but is not actually the point of Gentoo or where it really shines. I’m still somewhat amazed that Portage works at all, not to mention as well as it does.]

2 thoughts on “Setting up Gitea’s “Act Runner” daemon on Devuan (SysVinit)”

  1. Hello, I might be a bit late but this was an incredibly helpful script. Thank you so much for sharing!

    I had to make one change to make it work, which was adding sbin to the path, because I believe start-stop-daemon is there instead.

    1. Oh, right. There are a handful of things like that with Devuan where the path needs to be corrected for running as root.
      I should write a post with my root environment script setup to clarify things.

      Thanks for pointing that out.
      Have fun doing computer things!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.