---
title: "A GitHub Actions runner in two lines"
description: "Move GitHub Actions jobs to Promigence sandboxes with one workflow line, on runners we host or ones you start with one command, and run GitLab CI jobs the same way with a tagged runner."
url: "https://www.promigence.ai/docs/guides/github-actions-runner"
site: "Promigence"
---

# A GitHub Actions runner in two lines

Move GitHub Actions jobs to Promigence sandboxes with one workflow line, on runners we host or ones you start with one command, and run GitLab CI jobs the same way with a tagged runner.

Your workflow file stays almost the same. One line changes in it. The second step is turning CI on for the repository
in the console, which puts its jobs on our hosted runners, or one command that starts runners from a machine of yours. The steps,
caches and repository secrets stay where they are.

## Line one: the workflow

```yaml
jobs:
  test:
    runs-on: promigence        # was: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci && npm test
```

Sizes are picked in the label. `runs-on: promigence-small`, `promigence-medium` or `promigence-large` choose the
size per job. A job that only says `promigence` gets the default size: medium on our hosted runners unless you set
another for the repository in the console, and small with the command below unless you set `--tier`. Small has less
memory than GitHub's `ubuntu-latest` on a private repository (2 vCPU and 4 GiB, where GitHub's has 2 CPUs and 8 GB),
so a job that ran there belongs on `promigence-medium` (4 vCPU, 16 GiB): keep the default, or pass `--tier medium` to
the command below.

The runner image comes with git and Python plus a compiler toolchain, but no Node.js or other language runtime. A job that relied
on a tool GitHub's own image preinstalls adds its setup step, such as `actions/setup-node` above, as it would on any
self-hosted runner.

## Line two: turn CI on in the console

Open **Runners** in the console and choose **Add a repository**, then pick the repository from a GitHub account you
connected. Its jobs then run on runners we host, each in a fresh sandbox started for the job, and nothing has to keep
running on your side. Up to 10 of the repository's jobs run at once unless you change it there, and each job is billed
for the seconds it runs.

## Or run the runners yourself

On any machine with the CLI and a GitHub token:

```bash
export GH_TOKEN=<your token>
promigence runners github --repo OWNER/REPO
```

The command picks up queued jobs with a matching `runs-on` label and runs each in its own sandbox, until you press
Ctrl-C. Use `--org ORG` instead of `--repo` to take the jobs of every repository in an organization, and
`--runner-group NAME` to place the runners in a group other than `Default`.

### The token

The token is read from the environment: `GH_TOKEN`, then `GITHUB_TOKEN`, or the variable you name with `--token-env`.
A fine-grained token needs Actions read and Administration write on the repository. With `--org` it needs
Self-hosted runners write on the organization. A classic token needs `repo`, plus `admin:org` for an organization.
GitHub Enterprise Server works too: pass its API address with `--github-api URL`.

### The command's first run builds the runner image

Each size runs from a runner snapshot that is built the first time it is needed. The CLI asks before it builds one,
because the build is billed per second at that size's rate while it runs, whether it succeeds or not
([quickstart](https://www.promigence.ai/docs/quickstart)), and every later job of that size reuses the snapshot with no further build
charge. To do it ahead of time, run `--prepare`, which builds the snapshot for `--tier` and exits:

```bash
promigence runners github --repo OWNER/REPO --tier medium --prepare
```

In a script where nobody can answer the prompt, `--yes` builds a missing runner snapshot without asking.

## Control the bill

Hosted runners take the repository's machine size and how many of its jobs run at once from the console, and each job
is billed for the seconds it runs. The command's flags set the limits:

| Flag | What it controls |
|---|---|
| `--cap USD` | The most one job may cost (default: what its full timeout costs at its size) |
| `--timeout DUR` | The longest a job may run (default: the longest sandbox your plan allows) |
| `--max M` | How many sandboxes may run at once |
| `--warm N` | Keep N runners waiting so a job starts the moment it is queued. They are billed while idle |
| `--idle-timeout DUR` | Stop an on-demand runner that no job took |

`--warm` is the one setting that costs money while nothing runs. Leave it at zero unless queue time matters more to you
than idle time.

On Ctrl-C the command waits for running jobs to finish. `--kill-on-exit` cancels them instead.

## GitLab CI

GitLab works with runner tags instead of a `runs-on` line. For hosted runners, create a project runner in GitLab
(Settings, CI/CD, Runners, New project runner) tagged `promigence`, add the project under Runners in the console and
paste the runner's token there. It starts with `glrt-`, and it is not shown again once saved. Every job that says
`tags: [promigence]` then runs on Promigence.

For a runner of your own instead, create one in GitLab with the tags your jobs use, put its authentication token in
`GITLAB_RUNNER_TOKEN`, then:

```bash
export GITLAB_RUNNER_TOKEN=<glrt- runner token>
promigence runners gitlab
```

Jobs with those tags now run on Promigence, each in its own sandbox. A self-managed GitLab needs `--url https://gitlab.example.com`.

`--executor docker` (the default) runs each job in a container, using the job's `image:` or `--default-image`. With
`--executor shell` the job runs directly in the sandbox.

Unless told otherwise, the command keeps one runner up, billed while idle; `--pool N` changes that. To start runners only when jobs
are waiting, give it a GitLab token with `read_api`:

```bash
promigence runners gitlab --api-token-env GITLAB_API_TOKEN
```

`--cap`, `--timeout`, `--max`, `--tier`, `--prepare`, `--yes` and `--kill-on-exit` work as they do for GitHub.

Jobs on this runner can read the runner token. Use it only for projects whose CI code you trust.

## What changes for your jobs

Not much. Each job gets a fresh sandbox, is billed for the seconds it runs and stops at its cap. Steps that call
`actions/checkout`, set up a toolchain or restore a cache run as written. When a job fails, the log in GitHub or GitLab
is where you read it, the same as before.

## Checklist

Hosted:

1. Change `runs-on` to `promigence` (or a sized label).
2. Open Runners in the console, choose Add a repository and pick the repository.

Running the runners yourself:

1. Change `runs-on` to `promigence` (or a sized label).
2. Export a token with the scopes above.
3. Run `promigence runners github --repo OWNER/REPO --prepare` once per size.
4. Start `promigence runners github --repo OWNER/REPO` and leave it running.
5. Decide on `--cap` and on `--warm`, which is billed while idle.

