---
title: "Managing Runners"
description: "Monitor runners and queued work, stop runners, and withdraw queued work."
section: "runners"
url: "https://forkline.dev/docs/runners/managing"
markdown: "https://forkline.dev/docs/runners/managing.md"
source: "src/content/docs/runners/managing.md"
---

# Managing Runners

Learn how to monitor, stop, and manage your runners and the work waiting to become one.

## Runner Dashboard

The Runners page lists your runners together with the work that is still waiting for one:

![Runners list page](https://forkline.dev/docs/screenshots/runners/runners-list-page.webp)

### Accessing Repository Actions

To show repository-specific actions such as **Start runner** and repository settings, open the runners view for a single repository.

You can do this in two ways:

1. On the **Runners** page, click the repository in the **Repository** column.
2. On the **Repositories** page, click the repository card.

Once a repository is selected, the page shows controls for starting a runner and opening that repository's settings.

> **Note**: On the **Repositories** page, clicking the repository name opens the repository on your git provider. Click the card if you want to stay in Forkline.

## Runner States

### Active States

| State | Description | Actions Available |
|-------|-------------|-------------------|
| Pending | Waiting for provisioning | Stop |
| Starting | Initializing, cloning repo | Stop |
| Running | Ready for connections | Stop, Connect |

### Terminal States

| State | Description |
|-------|-------------|
| Stopped | User-initiated stop |
| Failed | Error during execution |
| Completed | Task finished (for webhook runners) |

### Queued Work

Work that arrives while no runner can take it appears in the same list as runners, with the **queued**
state. A queued row shows the repository, the issue or pull request it waits for, how long it has been
waiting, the reason it is waiting, and the size it asked to run on.

A queued row is not a runner:

- It does not use one of your concurrent runner slots, and it cannot be stopped or deleted.
- There is no session to open yet. Its title links to the issue or pull request it waits for when
  Forkline knows one; a run started from a prompt alone has no link. The repository column still
  filters the list to that repository.
- It can be withdrawn. See [Withdrawing queued work](#withdrawing-queued-work).

Waiting reasons are stated plainly, because the row never says *why* capacity is short — only that
work is waiting:

| Reason shown | What it means |
|--------------|---------------|
| Waiting for a runner | No runner has taken this work yet |
| Waiting to retry | An earlier attempt failed; the work is held back until its retry is due |

> **Warning**: The queue is not first-in, first-out. Work in backoff is skipped until its retry time,
> so a newer row can start before an older one. Forkline does not show a position in the queue or a
> time when work will start, because neither can be promised: the next free slot depends on when
> other runners finish, and a retry only says when work becomes eligible again.

When a runner takes the work, the queued row is replaced by that runner on the next refresh of the
list. The two rows are the same work at different moments, not two entries that swap places.

### When starting a runner is accepted as queued work

Starting a runner with an initial prompt while nothing can take it is not refused. The request is
accepted and the work is queued. The dialog tells you it was queued and why, instead of reporting a
failure. Find it in the list under the **Queued** status filter, where you can withdraw it like any
other queued work.

The reasons a prompted start can be queued (rather than refused) are separate from the reasons a
queue row waits. They describe why the platform could not start the runner right away:

| Reason | What it means |
|--------|---------------|
| Runner busy | The runner that would take this work is already running something else |
| Concurrency limited | Your concurrent runner limit is reached |
| No runner | No runner is available for this repository right now |
| Credits | Your balance is not enough to run it |

A start without a prompt asks for an interactive runner, which needs capacity right away: if no slot
is free, it is reported as a failure rather than queued, because there would be nothing to wait for.

## Withdrawing queued work

Queued work that has not been given to a runner yet is yours to withdraw. On the row, click
**Cancel** and confirm. Withdrawing:

- removes the work from the queue, so it will not start later,
- changes nothing about any runner — it is not a stop, and nothing is interrupted because nothing
  had started,
- is per row. There is no bulk cancel: selection and bulk actions are runner actions.

If a runner takes the work while your cancel is on its way, the cancel is rejected with a conflict
(the API answers 409) and the list shows the runner that took it. Cancelling it then would strand a
running session, so stopping that runner is a separate decision you make from its own row.

Cancellation is owner-scoped: you can only withdraw your own queued work. If you try to cancel a
task that is already cancelled, the request succeeds (idempotent). **Cancelling never stops a
runner.**

## Filters, search, sorting and paging

The list is one thing, so these controls apply to queued work and runners alike:

- **Search** matches the whole list, not only what is currently on screen.
- **Status** narrows to one kind. **Queued** shows only queued work and pages through the whole
  queue; a runner status such as **Running** shows only runners.
- **Sorting** a column applies to both kinds. With no column chosen, the list is grouped with queued
  work first and oldest first within each group. That is a reading order, not the order work starts in.
- **Paging** is over the combined list, and the page size applies to both kinds. A link to
  `/runners?status=queued&page=2` is the second page of the queue. Changing a filter, the search or
  the sort starts you at page 1 of the new result; withdrawing work that shortens the list moves you
  to the last page that still exists instead of leaving you on an empty one.

## How the list stays current

Rows update on their own. The page holds one live connection to Forkline that reports changes to your
queued work and to your runners. Each change arrives as a hint rather than as data, so the list is
read once: changes that land together, within about a tenth of a second, are folded into a single
read instead of one read each.

Nothing is polled. An idle runners page makes no further list requests at all, and a page in a hidden
tab reads nothing while it is in the background — when you come back it reconciles with one fresh
read.

If delivery to your browser is interrupted you'll see a quiet **Reconnecting** note: what is on screen
may be a moment behind. It is advisory, and you never need to act on it — when delivery resumes the
list is read again. Rows and the counts in the footer come from the same read, so they always describe
the same moment.

If a refresh fails, the rows stay on screen under a **This list may not be current** banner with
**Try again**. Nothing is silently removed or renumbered while you decide what to do.

## Stopping Runners

### From Runners Page

1. Navigate to **Runners**
2. Find the runner you want to stop
3. Click the **Stop** button on the runner row
4. Confirm the action

### From Repositories Page

1. Navigate to **Repositories**
2. Click the repository card to open its runners view
3. Find the runner you want to stop
4. Click the **Stop** button
5. Confirm the action

> **Warning**: Stopping a runner deletes all uncommitted changes. Push your work before stopping.

### Automatic Stops

Runners stop automatically when:

- **Balance exhausted**: Your balance reaches zero
- **Heartbeat timeout**: Runner stops sending health checks
- **Error**: Unrecoverable error occurs

## Runner Limits

### Maximum Active Runners

Control how many runners can run simultaneously:

1. Go to **Settings** → **General**
2. Find **Maximum Active Runners**
3. Set your limit (or leave empty for unlimited)

![General settings with runner limits](https://forkline.dev/docs/screenshots/settings/general-settings.webp)

```text
# Example: Limit to 3 concurrent runners

When limit is reached:
- "Start runner" button is disabled
- Message: "Maximum active runners reached"
- Stop an existing runner to start a new one
```

> **Tip**: Set a limit to prevent accidental usage from forgotten runners.

## Webhook Runners

Runners started by webhooks have additional behavior:

- **Auto-start**: Created when a webhook task arrives
- **Idle timeout**: Stop after 5 minutes of inactivity
- **Task queue**: Handle multiple tasks sequentially

> **Info**: Webhook runners are optimized for cost-efficiency. They automatically stop when idle.

## Next Steps

- [Understand runner sizes](https://forkline.dev/docs/runners/sizes) for optimization
- [Set up webhooks](https://forkline.dev/docs/automation/webhooks) for automation
