Diagnosing Forgejo Actions Runners That Stay Pending or Never Start
A step-by-step diagnostic guide for Forgejo self-hosted Actions runners that stay pending: verify runner heartbeat, workflow triggers, concurrency limits, and label matching — with concrete fixes and escalation criteria.
29 Sept 2026, 22:17 UTC

Recognizable Condition
A workflow run appears in the Forgejo UI with status Pending or Queued. No job begins execution. The runner may show as Online but idle, or as Offline. Push or manual trigger events are received, yet no pickup occurs, while other repositories on the same instance may work normally.
Cause and Diagnostic Mapping
| Symptom | Likely Cause | Quick Verification |
|---|---|---|
| Runner shows Offline or stale heartbeat | Runner not registered to the correct instance, or registration token expired | Admin → Actions → Runners: check Active status and last heartbeat timestamp |
| Workflow stuck at Queued with no runner assignment | Invalid trigger syntax, path filter mismatch, or workflow file outside .forgejo/workflows | Open workflow run page; inspect pending job for trigger mismatch messages |
| Multiple runs queued, none start | Concurrency limit per instance or per repository reached | Review instance Actions queue; check actions.max_concurrent_jobs in app.ini |
| Runner Online but job never picked up | Runner OS/architecture or labels do not match job runs-on requirements | Compare runner labels (UI) with workflow runs-on matrix |
Ordered Diagnostic Checks
- Verify runner registration and heartbeat
In the Forgejo web UI, navigate to Admin → Actions → Runners. Confirm the expected runner appears with status Active and a recent heartbeat (within the last minute). If the runner shows Offline or the heartbeat is stale, the runner process is not communicating with the instance. - Confirm workflow file location and trigger validity
Ensure the workflow YAML exists under.forgejo/workflows/(not.github/workflows/) in the repository’s default branch. Validate trigger syntax:
Path filters must match changed files; a mismatch prevents the run from being queued for execution.on: push: branches: [main] paths: - 'src/**' workflow_dispatch: - Check repository Actions settings and branch protection
In the repository settings, verify Actions → General → Enable Actions is on. Branch protection rules that require status checks can block workflow execution if the required check never runs. - Inspect instance Actions queue and runner logs
On the Forgejo server, examine the Actions queue and runner process logs for pickup errors or workflow parsing failures. Typical log locations (systemd service):
Look for lines containingjournalctl -u forgejo-actions-runner -fpickup,parse, orqueue.
Fixes Tied to Findings
Runner Offline or Stale Heartbeat
Re-register the runner using a fresh registration token. Tokens are single-use and time-limited; re-registration invalidates the previous token.
- In the UI, go to Admin → Actions → Runners → New runner to generate a token.
- On the runner host, stop the existing runner service:
sudo systemctl stop forgejo-actions-runner - Remove the old runner configuration directory (typically
~/.forgejo-runneror the configured--configpath). - Run the registration command with the new token:
forgejo-runner register \ --instance https://forgejo.example.com \ --token REGISTRATION_TOKEN_FROM_UI \ --name "linux-amd64-runner" \ --labels "ubuntu-latest,ubuntu-22.04,docker" - Start the service:
sudo systemctl start forgejo-actions-runner
Permissions: Runner process runs as the service user (often git or a dedicated forgejo-runner user). Ensure that user owns the configuration directory and can write logs.
Workflow Trigger or Path Mismatch
Correct the workflow file:
- Move the file to
.forgejo/workflows/ci.yaml. - Adjust
on.push.branchesandon.push.pathsto match the repository’s branching strategy and file layout. - Push a test commit that satisfies the path filter, or trigger manually via
workflow_dispatch.
Concurrency Limits Exhausted
Check the instance configuration in app.ini:
[actions]
MAX_CONCURRENT_JOBS = 10
REPOSITORY_MAX_CONCURRENT_JOBS = 5If limits are reached, either raise them (requires Forgejo restart) or cancel stuck runs from the UI (Actions → Runs → Cancel). Note: the default SQLite database serializes writes and can cause queue delays under concurrent load; PostgreSQL is recommended for production.
Runner Label or Platform Mismatch
Compare the runner’s reported labels (visible in Admin → Actions → Runners → Details) with the workflow’s runs-on value. A job requesting runs-on: ubuntu-latest will not match a runner labeled only self-hosted,linux,arm64. Add the required labels during registration or update the runner’s label set by re-registering.
Concrete Diagnostic Example
Scenario: A repository’s ci.yaml uses runs-on: ubuntu-22.04. The single registered runner shows labels ubuntu-latest,docker but not ubuntu-22.04. The workflow run stays Queued indefinitely.
Diagnostic steps:
- Open the workflow run page → pending job shows Waiting for runner with labels: ubuntu-22.04.
- Runner details page confirms labels:
ubuntu-latest,docker. - Fix: re-register runner with
--labels "ubuntu-latest,ubuntu-22.04,docker". - After restart, the queued job is picked up within seconds.
Escalation Criteria
Escalate to instance administrator or upstream when:
- Runner process crashes repeatedly (check
journalctl -u forgejo-actions-runnerfor segfaults or panic logs). - Database lock or timeout errors appear under concurrent runs (SQLite
database is lockedor Postgresdeadlock detected). - Jobs remain stuck after runner restart, configuration correction, and concurrency limit increases — indicating instance-level resource exhaustion (CPU, memory, disk I/O) or a version-specific regression.
Practical Verification Checklist
- UI: Admin → Actions → Runners shows runner Active with heartbeat < 60 seconds ago.
- Workflow run page: pending job shows no runner selection error; trigger matches event.
- Server logs:
journalctl -u forgejo -f | grep -i actionsshows successful workflow parsing and queue processing. - Test: push a minimal workflow with
workflow_dispatchand trigger manually; confirm job starts and completes.
Limitations and Version Sensitivity
Forgejo Actions mirrors Gitea Actions; feature parity and behavior vary across releases. This guide assumes Forgejo v1.21+ (Actions v0.2+). Default SQLite backend is not suitable for concurrent Actions workloads — migrate to PostgreSQL before production use. Runner registration tokens expire; automate rotation in CI/CD pipelines that provision ephemeral runners.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.