Troubleshooting & Common Issues

This guide collects the problems people most often hit when running a self-hosted runner or configuring a project, and how to resolve them. Each entry is written as symptom → likely cause → resolution so you can scan for what you are seeing and jump straight to the fix.

If you cannot find your issue here, turning on local runner logs is usually the fastest way to see what the runner is doing on your machine.

Docker is not installed or not running

Symptom. The runner starts but no agent session ever begins, or it reports that it cannot reach Docker / cannot start a container.

Likely cause. The runner always runs agents inside Docker containers, so a working Docker engine must be available on the host. Either Docker is not installed, or it is installed but the daemon is not running (for example, Docker Desktop is closed).

Resolution.

  • Install Docker Desktop or Docker Engine if it is not already installed.
  • Make sure the daemon is actually running. Open Docker Desktop, or start the Docker service on Linux.
  • Confirm Docker works for your user by running a simple container:

sh docker run --rm hello-world

If that command fails, fix Docker first — the runner cannot work until it succeeds. On Linux, if it fails with a permissions error, make sure your user can talk to the Docker daemon (for example, that it is in the docker group).

  • Once Docker is running, start the runner again.

Node.js version is too old

Symptom. Installing or starting the runner fails, or you see an error mentioning an unsupported or unexpected Node.js version.

Likely cause. The runner requires Node.js 20 or newer. An older Node version on the machine will fail to install or run the CLI.

Resolution.

  • Check your version:

sh node --version

  • If it is below 20, install Node.js 20 (or a newer LTS release). A version manager such as nvm, fnm, or volta makes it easy to install and select a newer version per machine.
  • After upgrading, reinstall the runner so it runs on the new Node:

sh npm install -g @setforth/self-hosted-runner

Runner won't connect

Symptom. The runner starts but never comes online in your dashboard, or it reports that it cannot authenticate or reach the platform.

Likely cause and resolution. There are three common causes. Work through them in order.

Invalid or expired API key

The apiKey in runner.config.yml is wrong, was truncated when copied, or has been rotated.

  • The apiKey is shown once, at registration time. If you did not save it, you cannot retrieve it — register the runner again (or rotate its key from the runner's menu in the dashboard) and copy the freshly generated config.
  • If you rotated the key, the old one stops working immediately. Update runner.config.yml with the new value.
  • Confirm the config resolves to a complete identity before starting:

sh set-forth-runner validate-config --config ~/.set-forth-runner/runner.config.yml

Wrong platform URL

The platformUrl in runner.config.yml does not point at the right SetForth API origin.

  • Use the value from the config the dashboard generated for this runner. Do not hand-edit it unless you have a specific reason.
  • If you are running as a managed service, remember the service has no shell environment — every connection value, including platformUrl, must be present in the config file itself, not in environment variables.

Network or firewall blocking the connection

The runner cannot reach the platform over the network.

  • The runner only makes outbound HTTPS connections to SetForth, so there is nothing inbound to open. Make sure outbound HTTPS to the platform URL is allowed by your firewall or proxy.
  • If your network requires an HTTP proxy for outbound traffic, configure it for the runner process the way you would for any other Node program (for example via the standard proxy environment variables).
  • Verify basic connectivity from the same machine and user that runs the runner.

Runner is connected but no sessions run

Symptom. The runner shows as online, but tasks never start on it.

Likely cause and resolution. Usually one of the following.

Execution target mismatch

A project's execution target decides where its agents run. If a project is set to Cloud, its work never goes to your self-hosted runner.

  • Open the project's Settings and check the execution target. For work to land on your runner, set it to Self-hosted, or to Auto (where either pool can run the work, and the cloud is used only when no self-hosted runner is online).
  • See Project Configuration for what each option means.

You're on a plan where cloud agents start automatically

On eligible plans, cloud agents are provisioned and started for you when a task is ready — there is nothing to install and no runner to operate. If your projects use cloud (or auto) execution, a self-hosted runner is simply not needed, and you do not need to keep one online.

  • If you intended to use the cloud, you can ignore the runner entirely — your work runs in managed environments automatically.
  • If you specifically want work on your own hardware, set the project's execution target to Self-hosted and keep a runner online.

No runner online for a self-hosted project

If a project is set to Self-hosted and no runner is online, agent work simply waits until one is. (On Auto the work would instead run in the cloud when no runner is online, so it would not wait.)

  • Confirm the runner is actually running and shows as online in the dashboard.
  • Register and start a runner before relying on self-hosted execution.

No Anthropic credentials on the host

The runner is online and a session starts on it, but the agent never makes progress because the host has no Anthropic credentials. The agent is powered by Claude, so the host must supply credentials for it to run.

  • Provide credentials on the runner's host one of two ways: - Set the ANTHROPIC_API_KEY environment variable in the runner's environment (billed to your Anthropic API account), or - Sign in once on the host with claude login to use your Claude subscription.
  • If both are present, the runner uses ANTHROPIC_API_KEY.
  • Restart the runner after setting credentials so it picks them up.
  • See the Self-Hosted Runner guide for details.

Compose file rejected when saving

Symptom. Saving your project's Docker Compose YAML fails, and the dashboard tells you which service and which directive caused the rejection.

Likely cause and resolution. Sibling services run next to the agent on the same machine, so the compose file is checked against a few safety rules before it is saved. Each rule maps to a specific fix.

Privileged container

The file is rejected because a service sets privileged: true.

  • Remove privileged: true from that service. Standard public images (databases, caches, message brokers) do not need it.

Host filesystem bind mount

The file is rejected because a service mounts a path from the host machine — either directly in its volumes, or through a named volume defined as a bind to a host path.

  • Services may use named volumes only, not host paths. Remove the host bind mount.
  • If you were bind-mounting a host path to share your checked-out code with a sibling, mount the named workspace volume instead:

yaml services: fixtures: image: my-loader:latest volumes: - workspace:/repo

Reserved service name

The file is rejected because a service uses a reserved name. The reserved names are agent, workspace-init, and docker.

  • Rename the service to anything else — for example db instead of agent.

Other restricted directives

The compose checks also reject host-namespace sharing (network_mode: host, pid: host, ipc: host, uts: host, container:-style values, and userns_mode: host) and added capabilities or devices (cap_add, devices, device_cgroup_rules, security_opt, cgroup_parent, group_add).

  • Remove the flagged directive from the service named in the message. A standard service definition without these directives passes as-is.

See Project Configuration for the full list of restrictions and examples.

Setup script fails during a session

Symptom. A session starts but the agent never gets going, and the failure points at the project's setup script.

Likely cause. The setup script (setup.sh) runs at the start of every session to prepare the workspace. If a command in it exits with an error — a failed dependency install, a missing tool, a wrong path — the session cannot proceed.

Resolution.

  • Open your project's Settings and review the setup script. Run the same commands yourself, from a fresh checkout of the repository at /workspace, to reproduce the failure locally.
  • Common causes: a dependency step that needs network access your runner's machine does not have, a tool that is not installed on the host image, or a command that assumes files the fresh checkout does not yet have.
  • On a self-hosted runner, make sure the host (and any sibling services in your compose file) actually provide what the script expects — for example, that a required CLI is installed and a database service is reachable.
  • Keep the script strict (set -euo pipefail) so failures surface early and clearly, and fix the failing command. Leave the script empty if your project needs no preparation.

Enable and find local runner logs

When you need to see what the runner is doing on your own machine — for self-diagnosis of any of the issues above — turn on local logs. These are written on the host so you can read them directly.

Enable them in runner.config.yml:

local_logs:
  enabled: true
  directory: ~/.set-forth-runner/logs
  • Set local_logs.enabled: true to start writing per-session logs on the host.
  • directory is optional; it chooses where the logs are written.
  • You can also override the location with the RUNNER_LOG_DIR environment variable.

Find them in the directory you configured (or the default location if you did not set one). After reproducing a problem, look at the most recent log to see where the session stopped — for example a Docker error, a failing setup script command, or a connection problem.

Restart the runner after changing local_logs so the new setting takes effect.

Upgrading a stuck or outdated runner

Symptom. The runner is on an old version, or seems stuck and you want it on the latest build.

Likely cause. Runner upgrades are manual — a runner keeps running its installed version until you update it.

Resolution. Install the latest version, then drain the runner so it finishes its current session and restarts cleanly on the new build:

npm install -g @setforth/self-hosted-runner@latest
set-forth-runner drain
  • If you run the runner as a managed service, it restarts automatically after draining, so it comes back on the new version on its own.
  • If you run it in the foreground, start it again after the upgrade:

sh set-forth-runner start

Draining (rather than killing the process) lets in-progress work finish instead of being interrupted.