Compute
CI โ Compute shows the CPU and memory your CI jobs use: what's running right now, and how much compute your jobs used over the last day, week or month. It counts every job Anvil sees: jobs on Anvil runners, jobs on the runner for repositories hosted on Anvil, and GitHub-hosted jobs.
You see the jobs of your own repositories. Capacity and server load are shown for the whole server, without saying whose jobs use it.
Measured and estimated #
Jobs on Anvil runners are measured: the runner manager reads the container's CPU and memory every 10 seconds. Other jobs have no samples. For them Anvil assumes the job used a whole default-size machine (2 CPUs and 7 GB unless the admin set CI_ESTIMATE_CPUS and CI_ESTIMATE_MEMORY_GB), times how long the job ran. These numbers are marked estimate wherever they appear, and they never count towards "used", only towards "reserved".
- Reserved is the size a job asked for, times how long it ran. A 4-CPU job that ran for 30 minutes reserved 2 CPU-hours.
- Used is what the job actually used: its average CPU over the run, times the run time.
- Used vs reserved compares the two for measured jobs only. A job that reserved 4 CPUs and used 1 on average used 25%: a smaller size would probably do (the job page says which).
Now #
The top of the page refreshes every 5 seconds while the tab is visible and stops while it's in the background.
- CPU and memory gauges. How much of the runner pool running jobs have reserved (Anvil runner jobs, plus Gitea runner jobs as an estimate), and below it how much they use. The pool is the server's cores and memory as the runner manager reports them, unless the admin set
RUNNER_POOL_CPUSandRUNNER_POOL_MEMORY_GB. Reserved can go above the pool: sizes are limits, and jobs rarely use all of them at once. GitHub-hosted jobs don't run on this server and aren't counted. - Server. The server's load average (1, 5 and 15 minutes) next to its number of CPU threads, and the memory in use by everything on it, not only CI.
- Running. Each running job with its size, how long it has run, and for Anvil runner jobs a CPU and a memory sparkline of the last 10 minutes, scaled to the size's limit (the dashed line at the top). Click a job to open its page.
- Queued. Jobs waiting for a runner, and for how long.
History #
Pick 24 h (per hour), 7 days or 30 days (per day, in Amsterdam time). A job counts in the hour or day it started.
- Compute minutes, stacked by repository (the six biggest, the rest as Other) or by runner (Anvil sizes, Gitea runner, GitHub-hosted).
- CPU-hours: used vs reserved. Used, reserved but unused, and reserved by jobs without samples (estimate). The unused part is what a better-fitting size would give back.
- Peak concurrency. The most jobs running at once on this server, with Anvil's limit on concurrent jobs.
- Queue time, median and 95th percentile, from when a job was queued to when it started.
- Server load, average and peak per hour or day, against the number of CPU threads.
- Cache hit rate and cache size for the Actions cache on Anvil runners, when there is cache data.
- By repository: jobs, minutes, CPU-hours reserved and used, the share that is an estimate, and a trend line.
- Top jobs by compute: the jobs that used the most CPU-hours (measured, or the estimate).
Every chart has a Table button with the same numbers as a table, and you can move through a chart with the arrow keys once it has focus.
On the job page #
For a job on an Anvil runner, the Machine panel charts CPU and memory over the whole job, with the size's limit as a dashed line. While the job runs, the chart updates every 10 seconds (marked live).
How long the data is kept #
- Raw samples (every 10 seconds) are kept for 48 hours (
COMPUTE_RAW_HOURS), then rolled up into one-minute averages with the peak of each minute kept, so charts of older jobs keep their peaks. - The one-minute samples are deleted with job logs, after 30 days (
CI_LOG_RETENTION_DAYS). - When a job finishes, Anvil stores what it used (CPU-hours, peak and average memory) with the job, so history stays after the samples are gone.
- Server reports (cores, memory, load, cache size) are kept one per minute, for the same 30 days.
Public numbers #
GET /api/public/compute returns overall numbers for a public page such as wicker.life: running and queued jobs, CPU and memory reserved and used against the pool, jobs, compute minutes and reviews today and for each of the last 30 days, and the cache hit rate. It needs no sign-in, allows any origin, and is computed at most every 15 seconds however often it's called.
It contains counts and totals only: never a repository, owner, branch, workflow, job or person name, and nothing per repository. Sizes appear only as runner sizes (anvil-4cpu, gitea-runner, github-hosted). Values Anvil can't know are null, not 0 (for example CPU used on a day when only estimated jobs ran). The admin can turn it off with PUBLIC_STATS=off; it then answers 404 with a JSON body.
{
"generated_at": "2026-11-14T15:30:00.000Z",
"now": {
"running_jobs": 3, "queued_jobs": 1,
"cpu_used_cores": 2.4, "cpu_reserved_cores": 8, "pool_cpu_cores": 12,
"mem_used_bytes": 3200000000, "mem_reserved_bytes": 16000000000, "pool_mem_bytes": 48000000000,
"by_size": [{ "size": "anvil-4cpu", "running": 2 }]
},
"today": { "jobs": 41, "compute_minutes": 312.5, "reviews": 3 },
"history": [{ "date": "2026-10-16", "jobs": 40, "compute_minutes": 300.1, "cpu_hours_used": 2.1, "cpu_hours_reserved": 6.0, "reviews": 2 }],
"totals_30d": { "jobs": 900, "compute_minutes": 8000, "reviews": 60, "cache_hit_rate": 0.82 }
}
history always has the last 30 days, oldest first, in Amsterdam dates, with zeros on idle days. "Today" and the reserved figures include estimated jobs; reserved counts only jobs on this server, while job and minute counts include GitHub-hosted jobs.