Skip to content

Install a runner

Every method needs two things: the server URL and a registration token from a runner pool (slipway runner token <pool>, or Settings, Runners in the UI). Treat the token like a credential. See Pools and labels.

After first contact the runner stores its own id and credential in a state file (--state-file), so the registration token is only needed once and rotation of the pool token does not disconnect existing runners.

The packages install /usr/bin/slipway, a systemd unit and an environment file.

Terminal window
sudo apt install ./slipway_<version>_amd64.deb # or rpm -i / apk add --allow-untrusted
sudoedit /etc/slipway/runner.env
/etc/slipway/runner.env
SLIPWAY_RUNNER_SERVER=https://slipway.example.com
SLIPWAY_RUNNER_TOKEN=<registration token>
SLIPWAY_RUNNER_EXECUTOR=shell
SLIPWAY_RUNNER_LABELS=linux,x64
SLIPWAY_RUNNER_CONCURRENCY=2
Terminal window
sudo systemctl enable --now slipway-runner
journalctl -u slipway-runner -f

The unit runs as the unprivileged slipway-runner user with NoNewPrivileges, ProtectSystem=strict, ProtectHome and PrivateTmp, and gives running jobs up to an hour to finish on stop (TimeoutStopSec=3600). To use the docker executor add the user to the docker group (SupplementaryGroups=docker in a drop-in).

The runner image contains the binary plus git, the docker CLI with buildx and compose, kubectl, helm, kustomize, yq and jq, and runs as a non-root user.

Terminal window
docker run -d --restart unless-stopped --name slipway-runner \
-e SLIPWAY_RUNNER_SERVER=https://slipway.example.com \
-e SLIPWAY_RUNNER_TOKEN \
-e SLIPWAY_RUNNER_EXECUTOR=docker \
-e SLIPWAY_RUNNER_LABELS=linux,docker \
-v /var/run/docker.sock:/var/run/docker.sock \
ghcr.io/slipway-systems/slipway-runner:latest runner

Mounting the Docker socket gives the runner (and every job) control of the host’s Docker daemon. Prefer rootless Podman, a dedicated VM or the Kubernetes executor. See the security model.

Works with Docker and Podman (any Docker-compatible API).

Terminal window
slipway runner --executor docker \
--docker-host unix:///run/user/1000/podman/podman.sock \
--server https://slipway.example.com --token-file /etc/slipway/token

Options (YAML docker: block): default_image (used when a job has no container, default ubuntu:24.04), pull_policy (always, missing, never), allow_privileged, allowed_volumes (host path prefixes jobs may bind-mount), registries (pull credentials), selinux_label (appends :z, needed on Fedora and RHEL; default on) and network.

Runs steps directly on the host in a per-job workspace under --workdir. Steps inherit the runner’s environment unless shell.inherit_env: false. Use it where containers are impractical (macOS, hardware access) and run it on disposable or dedicated machines.

Creates a pod per job in a namespace. Run the runner inside the cluster (recommended, uses its service account) or point it at a cluster with --kubeconfig.

Terminal window
slipway runner --executor kubernetes --k8s-namespace slipway-jobs \
--server https://slipway.example.com --token-file /etc/slipway/token

See Runners on Kubernetes for the Helm values and RBAC.

Terminal window
slipway runner check # validates configuration, the executor and the path to the server, then exits
slipway runner version
slipway runner list --status online
Symptom Likely cause
Runner never appears Wrong server URL, an egress proxy or firewall blocking outbound HTTPS, or a private CA (use --ca-file). Run slipway runner check --server <url> --executor shell from the runner host: it opens the control stream without registering and says whether a proxy on the way blocks it
Runner registers, then no jobs arrive A proxy between runner and server (Cloudflare, an HTTP/1.1 load balancer) buffers the bidirectional stream. The default --transport auto uses a WebSocket, which such proxies pass end to end; make sure the proxy allows WebSocket upgrades on /api/v1/runner/connect
unauthorized at start Expired or rotated registration token; create a new one
Jobs stay queued No online runner in the pool matches the job’s runs_on labels
Docker steps fail to start Socket permissions, wrong --docker-host, SELinux (selinux_label)

The runner can check for updates (self_update.check, channel stable, every 6 hours by default). On shutdown (SIGTERM) it stops accepting jobs and waits up to --drain-timeout (default 10 minutes) for running jobs. A runner is compatible with a control plane one minor version older or newer.