CI quota
The CI quota counts the time each user or organization spends on runners you provide, and refuses new jobs once their limit is used up. It covers Forgejo Actions and external CI systems, such as Crow CI, that report their usage to Arbinel. Read Quotas first, because CI limits are ordinary quota rules.
Enable the CI quota
Section titled “Enable the CI quota”[quota.ci]
Section titled “[quota.ci]”| Key | Default | Meaning |
|---|---|---|
ENABLED |
false |
Records CI time and enforces time: rules. Needs [quota] ENABLED. While it is off, nothing is recorded and CI usage reads as zero. |
PLATFORM_RUNNERS |
empty | Actions runners whose time is billed, as instance:<name>, owner:<user-or-org-id>:<name> or repo:<repository-id>:<name>. These survive a runner’s re-registration. |
PLATFORM_RUNNER_IDS |
empty | Further Actions runners by database ID. Update them after a runner re-registers, since that changes the ID. |
RECORD_RETENTION_PERIODS |
12 |
How many months of individual run records to keep. Monthly totals are never dropped. Values below 1 read as 1. |
NOTIFY_THRESHOLDS |
80,100 |
Percentages of a CI rule at which the owner is mailed. See Warning mails. |
[quota.ci.default]
Section titled “[quota.ci.default]”| Key | Default | Meaning |
|---|---|---|
TOTAL |
-1 |
The monthly CI time for an owner none of whose groups has a time: rule. Write it as a duration, such as 500m or 10h. Absent or negative means unlimited. |
Only list scopes whose runner registration you control.
A user who registers a runner named hosted-linux-1 on their own account gets owner:<their-id>:hosted-linux-1, which does not match instance:hosted-linux-1, so their own runner stays free.
Subjects
Section titled “Subjects”| Subject | Counts |
|---|---|
time:ci:all |
All billed CI time |
time:ci:actions |
Time on billed Forgejo Actions runners |
time:ci:external |
Time reported by external CI systems |
time:ci:class:linux |
Time on Linux runners, from any source |
time:ci:class:windows |
Time on Windows runners, from any source |
time:ci:class:macos |
Time on macOS runners, from any source |
Limits for time: subjects are in seconds in the API.
A rule of 30,000 seconds is 500 minutes.
Usage resets at the start of each calendar month in UTC.
A run is charged to the month in which it stopped.
time:ci:actions and time:ci:external are children of time:ci:all, so a time:ci:all rule covers both.
A group whose only CI rule is time:ci:external does not grant Actions time, and its members’ Actions jobs are refused.
The class subjects work differently. They can only lower a limit and never grant time. A class that no rule names is limited by the source rules alone. For example, you can give an owner a large overall budget and a smaller one for a more expensive runner class:
Add ci-free to [quota] DEFAULT_GROUPS next to your storage group.
A group that only has time: rules does not affect storage, and a group that only has size: rules does not affect CI time.
Runner classes
Section titled “Runner classes”Arbinel reads a runner’s class from its labels.
linux maps to Linux, windows to Windows, and macos or darwin to macOS.
A runner with none of these labels counts as Linux.
Behavior at the limit
Section titled “Behavior at the limit”Arbinel checks the quota when a billed runner asks for a job. If the owner is over a rule for the source, or for the class of that runner, the job is cancelled before it starts.
- Only that job is cancelled. Other jobs of the run can still run on self-hosted runners, or on a runner class whose budget is not used up.
- Jobs that are already running are never stopped. Their time is charged, and the next job is refused.
- Jobs on self-hosted runners are never refused.
The run shows a banner that names the rule, the time used and the limit. When billing is on, it also points owners to the Add-ons settings tab. Rerunning the run after the quota resets or is raised clears the banner.
External CI systems
Section titled “External CI systems”An external CI system reports the time it spent through /api/v1/quota/ci.
It authenticates with a token of its own, not a user token.
Register it with one section per system:
| Key | Meaning |
|---|---|
| Section name | The reporter’s name, such as crow. Every record it writes is booked under this name. Lower-case letters, digits, - and _, up to 40 characters, and not actions. |
TOKEN_HASH |
The hex SHA-256 of the reporter’s token. Required. Create it with printf %s "$TOKEN" | sha256sum. |
DISPLAY_NAME |
Optional label shown in usage breakdowns. |
PLATFORM_RUNNER_IDS |
The reporter’s own agent identities whose time is billed. Reports for other agents are accepted but not counted. |
Arbinel ignores a section with an invalid name, a malformed hash, or a hash another reporter already uses, and logs an error.
The reporter uses two endpoints with Authorization: Bearer <token>:
GET /api/v1/quota/ci/check?repo=<owner>/<name>&runner_id=<id>&class=<class>answers whether work may start, withallowed,remaining_seconds,reasonandreason_code. Asking reserves nothing, so several jobs started at once can all be allowed against the same budget.POST /api/v1/quota/ci/usagebooks finished work withrepo,runner_id,external_ref,started,stopped, and optionallysecondsandclass. Sending the sameexternal_refagain charges once, so a reporter can retry safely.
The full request and response shapes are in the API reference at /api/swagger.
Warning mails
Section titled “Warning mails”Arbinel mails an owner when their CI time reaches a percentage of one of their time: rules.
Each percentage is sent at most once per rule per month.
An owner who crosses several at once is mailed only about the highest.
The mail goes to the user, or to every member of an organization’s Owners team, and links to the CI/CD usage page.
Set [quota.ci] NOTIFY_THRESHOLDS to an empty value to turn the mails off.
Cron task
Section titled “Cron task”cleanup_quota_ci_records runs every night and deletes run records and sent-mail markers older than RECORD_RETENTION_PERIODS.
It appears on the admin cron page as “Delete CI usage records past the retention window”.
API for owners
Section titled “API for owners”With quota and CI quota on, owners can read their usage:
GET /api/v1/user/quota/ciandGET /api/v1/user/quota/ci/recordsGET /api/v1/orgs/{org}/quota/ciandGET /api/v1/orgs/{org}/quota/ci/records
The web view is the CI/CD usage settings page, described in Usage and add-ons.