# Set up CI

After you have [pushed a repo](/docs/getting-started/), add a workflow and run it. Codebahn runs hosted CI on both plans. It uses GitHub Actions workflow syntax, so your `.github/workflows` directory works without renaming. Common build, test, and lint workflows typically need no changes. For the differences, see [compatibility](/docs/ci-runners/#compatibility).

Solo includes 200 minutes per month (one concurrent job). Crew includes 1,000 minutes per seat (pooled).

## Prerequisites

- A Codebahn account with a repository ([getting started](/docs/getting-started/))
- Solo or Crew plan for hosted runners, or any plan for [BYO runners](/docs/run-your-own-ci/)

## Add a workflow file

If your repository already has `.github/workflows/`, Codebahn picks it up. You can also use `.codebahn/workflows/`, `.forgejo/workflows/`, or `.gitea/workflows/`; when more than one exists, see [workflow directories](/docs/workflow-directories/) for precedence and de-duplication rules.

If you don't have a workflow yet, create `.forgejo/workflows/ci.yml`. Each example below is a complete project with source, tests, and a working CI workflow. Clone one to get started, or browse the workflow file to copy it into your own repo.

| Language | What it runs | Repository |
|---|---|---|
| Go | `go vet` + `go test` + Docker build | [example-go](https://github.com/codebahn/example-go) ([workflow](https://github.com/codebahn/example-go/blob/main/.github/workflows/ci.yml)) |
| TypeScript | `tsc --noEmit` + Vitest | [example-typescript](https://github.com/codebahn/example-typescript) ([workflow](https://github.com/codebahn/example-typescript/blob/main/.github/workflows/ci.yml)) |
| .NET 8 | `dotnet restore` + `build` + xUnit | [example-dotnet](https://github.com/codebahn/example-dotnet) ([workflow](https://github.com/codebahn/example-dotnet/blob/main/.github/workflows/ci.yml)) |
| Flutter | `flutter analyze` + `flutter test` | [example-flutter](https://github.com/codebahn/example-flutter) ([workflow](https://github.com/codebahn/example-flutter/blob/main/.github/workflows/ci.yml)) |

All workflows use `on: [push, pull_request]` and `runs-on: codebahn-small`.

`runs-on: codebahn-small` routes to Codebahn's hosted runners in Paris (fr-par). `ubuntu-latest` is an alias for `codebahn-small`; you can also use `codebahn-medium` or `codebahn-large` for bigger runners. Go and Node are pre-cached in the job image, so `actions/setup-go` and `actions/setup-node` resolve offline. Popular actions are also [mirrored locally](/docs/action-mirrors/) if you want to keep CI traffic in the EU. Anything else (cloud CLIs, browsers, databases, other runtimes) you install in your workflow. See [CI runners](/docs/ci-runners/) for the full image contents and runner sizes.

## Push to trigger

```bash
git add .github/workflows/ci.yml
git commit -m "ci: add workflow"
git push
```

Open the repository and click the **Actions** tab. Your run appears there with live logs. Pushing a new commit to the same branch cancels any in-progress run on that branch.

## How minutes are counted

Each job's wall-clock time is rounded up to a whole minute, then multiplied by the runner's cost factor. A 5-minute job on `codebahn-small` (1x) uses 5 compute minutes. The same job on `codebahn-medium` (2x) uses 10, and on `codebahn-large` (3x) uses 15. A job that finishes in 20 seconds still counts as a minute.

A workflow with three parallel jobs that each run one minute on the default runner uses three compute minutes.

Concurrency is capped by member count: 2 concurrent jobs (1-5 members), 4 (6-15), 8 (16+). Above the cap, extra jobs wait until a slot frees. That is separate from the minute cap below.

:::caution
Compute minutes are hard-capped. When your plan and top-up minutes are exhausted, new jobs are denied immediately with "CI is paused"; they do not queue. A job already running gets a 30-minute grace window past the cap, then is stopped. Buy a top-up, or enable [automatic top-ups](/docs/billing/#automatic-top-ups) to avoid hitting the cap, then re-run the denied jobs.
:::

## Top-ups

Top-ups are €9 per 1,000 minutes, bought under **Settings > Billing**. Plan minutes drain first; top-up minutes are used only after, and they persist until consumed rather than resetting each month. You can also enable [automatic top-ups](/docs/billing/#automatic-top-ups) to buy packs before you run out. See [billing](/docs/billing/) for limits and warnings.

## Bring your own runner

BYO runners are available on all plans, alongside hosted runners.

You can register your own `forgejo-runner` alongside the hosted ones and route jobs to it by label. Give the runner a custom label, then target it with `runs-on: my-label`; jobs match by exact label. The registration token can be reused until you regenerate it, so generate a new token once your runners are registered.

See [CI runners](/docs/ci-runners/#bring-your-own-runner) for the registration steps, or [Run your own CI](/docs/run-your-own-ci/) for BYO runner setup and external CI alternatives.
