Several GitHub organizations on one control plane: Repositories → Another organization makes a private App in that organization too (a private App belongs to one account), kept beside the first; its jobs run on the same runners, with the same limits. Each organization is a row (Install, Manage, Remove), and Control plane → Permissions says for each what its App asks for and what was accepted. Removing one uninstalls its App there; the App itself is deleted on GitHub.
Cloudflare (experimental): jobs can run inside GitHub’s full runner image, which a container’s 20 GB disk cannot hold. Runners → Cloudflare → GitHub’s full image takes the address the image is published at; the container shows it as one file and fetches only what a job reads (several pieces at once, the commonly used ones first), with a writable layer on its own disk. GitHub’s runner starts inside it as on GitHub’s machines (same tools, tool cache and environment). Empty: the small image, as before; if the image cannot be reached, the small image is used.
New program image-reader (Linux): shows an image kept as small pieces at a public address as one local file.
AWS taking back a spot machine mid-job: the machine tells the control plane and stops its runner, so the job fails at once (GitHub otherwise waits many minutes for a runner it no longer hears from). Once the run has finished, that job is run again as a new attempt (with the jobs that need it), on an on-demand machine. GitHub’s App needs Actions (write) for that: Control plane → Permissions says how; without it the job says it was not run again, and why.
Two spot machines taken back within a quarter of an hour: jobs start on on-demand machines for half an hour.
AWS spot machines try each zone of a region, cheapest first, also in the region’s default network (before, AWS picked one zone, and a full zone meant the next region).
A job AWS had no room for says what each region answered; “no GPU quota” only when every region refused for quota.
Workflows → Switch workflows: a prompt to paste into your coding agent (Claude, Codex, Copilot, Gemini) that moves a repository’s jobs to SuperCI: every workflow, or one workflow first. Short prompts: what to change, what stays on GitHub (with why), one pull request (or GitLab merge request) with a table of the changes.
Signing in with Modal: Modal’s page does not come back to the dashboard by itself, so while it waits every page looks again and notices the approval; Control plane → Permissions says to approve in Modal’s tab and close it.
Runners → a provider’s settings → Remove: jobs stop going there at once, then what SuperCI made for it in that cloud is deleted with your sign-in there (AWS: its machines, their network and the control plane’s role; Modal or Cloudflare: the runner agent). The control plane and your sign-ins stay; adding it back is Add provider. Not signed in there: sign in from the dialog, or remove it from SuperCI only (what stays is listed). Jobs running there are stopped, after saying so.
A control plane in AWS can remove its AWS runners too (its own role keeps its control-plane policy) and add them back.
Control plane → Permissions: Modal’s runners work without a sign-in; it says so.
The runner that fails a job on GitHub (see 0.9.15) is tried on each place in turn, and again each minute for half an hour while GitHub still has the job queued; before, a container that would not start left the job queued for a day.
Overview: jobs from computers (from before they were set aside) are named so.
GPUs: runs-on: superci/gpu=any gets one NVIDIA GPU (the least costly: T4), or name one: superci/gpu=l4, gpu=a10g, gpu=l40s on AWS or Modal, gpu=a100, gpu=h100, gpu=h200, gpu=b200 on Modal. AWS uses RunsOn’s image with NVIDIA’s drivers; Modal’s sandboxes get the GPU, priced per second.
Windows: runs-on: superci/os=windows runs on AWS (Windows Server 2025, x64), spot by default, priced at Windows’ rates.
An AWS account that allows no GPU machines (AWS’s quota for them starts at 0) fails GPU jobs at once, saying which quota to raise, instead of trying for a day.
The machine picker (Workflows) offers Windows and GPUs.
AWS machines start in a network of their own per control plane and region, with nothing let in: jobs can’t reach each other or your other machines in AWS. The dashboard makes it when AWS is connected, when a region is added, with an update, or with Control plane → Permissions → Review; it is removed with the control plane. Until then, machines use the region’s default network as before.
AWS spot machines start in the region’s cheapest zone for their type, then the next when it has no room.
GitLab: each job gets a runner of its own, paused once it takes its job and removed after (runners were shared by jobs with the same tags). Runners from before are removed.
Your own computers are coming soon: until jobs on them can be kept apart from the computer as reliably as on a cloud, they can’t be added. The computer agent (superci join) and its routes are gone; a macOS job fails at once, pointing to GitHub’s macos-latest.
Your Mac runs macOS jobs in a VM of its own per job (Tart, on Apple’s virtualization; Cirrus Labs’ plain macOS image), deleted after the job, so nothing a job does stays on the Mac. Two run at once at most. Jobs wait while the Mac fetches the image (once, about 27 GB).
Running jobs on a computer itself (as you, with your files) is off unless turned on for that computer.
A computer’s jobs run in a folder without spaces in its path (GitHub’s runner does not quote a step’s script).
Workflows → Limits lists the public repositories GitHub’s App is installed on (jobs come only from those), each allowed with one click, instead of typing names.
A job nothing here can run (a machine no place has, like macOS with no Mac added; one larger than allowed; a place not added) fails at once on GitHub, saying why, instead of waiting a day: a small runner takes it and fails it before any step. A GitLab job like it is cancelled.
A job waiting for a computer that went offline (the only place that can run it) fails after five minutes, the same way.
Control plane → Permissions: for AWS, GitHub and GitLab, whether they allow what this version asks for, or what is missing, why, and how to give it. AWS’s role is read while signed in to AWS and given what it lacks with Review → Allow; not signed in, what AWS refused lately is shown. GitHub: the App’s and each installation’s permissions, with links to give them. GitLab: the token’s scopes.
An update lists the permissions it also asks for (sidebar and Changelog), and gives AWS’s what it lacks when signed in to AWS.
AWS’s permissions are made from one list in the code, each with what it allows, why and since when; the CloudFormation template (no longer used) is gone.
Each provider on Runners shows this month as its cloud bills it: Cloudflare’s containers after the usage Workers Paid includes, Modal’s apps after the plan’s credits (from Modal’s own billing), AWS at the prices it billed.
An AWS machine’s cost includes what it sent (CloudWatch’s count, at AWS’s price for data sent out), added a few minutes after it ends.
A Modal control plane no longer keeps a container up around the clock: its sweep’s container goes as soon as it is done (it cost about $5 a month idle).
Costs are settled when a job ends. AWS: at the prices AWS billed for its time — its zone’s spot price for each hour (or the on-demand list price), its disk at the region’s gp3 price, and its public address ($0.005/h) — a minute at least. Cloudflare: as Cloudflare metered its container, read by the dashboard while signed in to Cloudflare. Until then a cost is marked as estimated (≈).
Modal sandboxes are sized in CPUs as GitHub counts them (two to one of Modal’s cores) and held to what they ask for, so each is billed exactly its size.
The comparison with GitHub uses GitHub’s arm64 prices for arm64 jobs; job costs show to the hundredth of a cent.
Jobs from public repositories are refused unless the repository is allowed (Workflows → Limits); runs from a fork’s pull request and pull_request_target runs are refused even then.
A label asking for a machine larger than allowed (32 CPUs unless set otherwise in Workflows → Limits) is refused; every cloud runs at most 20 jobs at once unless set otherwise.
Monthly budgets count on-demand AWS machines (estimated from their size) and the time a runner waits for its job; a Cloudflare container’s wait counts its memory and disk (CPU is billed as used).
An AWS machine powers itself off at its time bound twice over (a timer and a scheduled shutdown), so a job ending the timer no longer keeps it up.
A control plane that moved forwards only events signed by GitHub or GitLab; job history older than two months is removed.
The dashboard answers only on this computer’s own address and makes changes only from its own pages.
Moving to an AWS or Modal control plane switches over without waiting for it to read its settings again (it reads them fresh for a move’s steps); a move brings Cloudflare’s Location along.
Changes saved for a Cloudflare control plane take effect in seconds (its Worker hands the Durable Object the settings as they are now; the Durable Object kept the ones it started with for up to half a minute).
A runner that takes a job other than its own (GitHub gives a waiting job to any runner with the label) no longer leaves its own job waiting forever when the other job’s machine had failed: the job it was started for gets a new machine.
Cloudflare has a Location in its settings: where its containers start (eastern North America by default, near GitHub and GitLab; another area; or Automatic, where Cloudflare chooses).
Region and location lists mark the one closest to the code hosts you connected (GitHub, gitlab.com).
A Cloudflare control plane takes a new dashboard session’s key at once (its Worker reads the secrets fresh and vouches for the key; the Durable Object kept the secrets it started with for about 15 seconds).
After a move or an update the page reads afresh, so the one in use shows at once; a move says where it went.
AWS’s regions are a list in its settings: drag to reorder, add one, remove one.
Saving a dialog shows Saving… on its button until it is done; a control plane that does not answer for a moment (a Worker restarting after a setting changed) keeps its last view, so its runners do not seem to vanish.
Reordering runner providers saves in place (“Saving the order…”, then “Order saved”); the page does not reload.
A faster first look: the dashboard searches every cloud at once and hands each control plane its key at once; an AWS or Modal control plane takes a new dashboard key at once (it read its settings every 15 or 10 seconds before).
An AWS control plane runs with 1024 MB (it had 256 MB, about a seventh of a CPU): it starts and answers several times faster. Update sets it on one set up before.
No more flicker while a page waits: it is redrawn only when something changed.
Overview has no control plane chip; Workflows’ default machine is one card, and Choose a machine… picks another.
A job whose label names a runner provider this control plane has not added says so plainly (“its label needs Cloudflare runners, which this control plane has not added”).
Workflows: the default machine first; then the steps for GitHub Actions or GitLab CI; another size from Make a label…, a dialog.
States read as words with a capital (“Connected”, “Ready”); the ⋯ menu no longer nudges its row.
Moving is non-destructive and runs in the background, its steps in its row. Your runner providers come along (Cloudflare’s containers, Modal’s sandboxes, AWS), and so does job history; nothing is removed from the old control plane, so you can move back. Move here first says what comes along and what needs a sign-in.
The switch is safe: the new control plane waits on standby until GitHub and GitLab are pointed at it, and the old one passes on any event that still reaches it.
Delete a control plane that is not in use (its Worker, function or app and storage, and the runner agents made for it); Stop using SuperCI lets GitHub and GitLab go and deletes everything from your clouds.
Control plane is one list: the one in use, others to move to, and each cloud without one with Set up (its progress in its row). Delete and Stop using SuperCI are in each one’s menu (⋯).
Overview: no “Needs a look” list (each job’s row says what went wrong); GitHub’s price behind a small mark; a quiet line when there are no jobs yet; pages no longer flash “Reading your control plane” when one is slow to answer.
One control plane is in use at a time. Another (in another cloud or account) is somewhere to move to: Move here brings your GitHub App and its installations, GitLab and its projects’ webhooks, the order of your runner providers, the default machine and your computers; jobs running at the time finish where they are.
Every control plane answers to the same label (runs-on: superci), so workflows do not change on a move. A cloud account holds one control plane.
A job whose machine’s start was cut off midway (the control plane stopped while a cloud answered) is placed again after two minutes, twice at most; before, it stayed queued until GitHub redelivered its event.
Your computers follow a move by themselves. Runner providers tied to a control plane (AWS, Cloudflare, Modal) are added again on the new one; Runners says which.
Jobs keep their names and their workflow’s (GitLab: their stage), so lists say “test · app · ci” rather than numbers.
Overview, rethought: jobs in a table (name, runner in words, time, cost, how it ended); what needs a look grouped by what happened, in plain words; where jobs ran only when more than one provider ran them. The control plane is one quiet chip with its own icon.
Cloudflare’s errors are recorded by their message.
AWS machines can go to several regions in order: when one has no spot capacity (or quota) left, the next. Adding AWS sets US East (N. Virginia), closest to GitHub with the most spot capacity, then Ohio and Oregon; change them in AWS’s settings on Runners.
Adding AWS says what your account may run there (its spot CPU quota, as jobs of 4 CPU) and links to AWS’s page to ask for more when it is low. Regions show their full names.
Workflows, in the sidebar: how jobs ask for your runners, for the code hosts you connected, with the default machine and the picker for another.
The control plane and What’s new are pages of their own (Settings before); Overview says which line is your control plane.
A machine that fails to start (Cloudflare’s “temporarily unavailable”, a spot shortage) is tried again, twice, instead of failing the job; a start always gets a fresh container.
Overview, after supercov’s report: the control plane and your label on one quiet line; Jobs (how many got a machine, the median wait for one, what they cost against GitHub’s runners, what needs a look, the latest); where jobs ran.
Every page waits in the shape of its own cards, the loader inside the first.
Overview’s measures: jobs in the last day with a chart by hour (those that ran, those that did not), the median wait for a machine, and spend next to GitHub’s price for the same jobs. Pages’ headers no longer float.
Repositories: a Use it card switching between GitHub Actions and GitLab CI, with the steps for each; GitLab’s projects and connection are its own settings page (Settings on its row).
GitLab projects say when GitLab paused or disabled their webhook, and when the last event from GitLab arrived; Turn on all.
GitHub jobs with services: and container: (and docker:// steps) run on Cloudflare: Docker starts when a job first uses it, and containers share the machine’s network (Cloudflare has no bridge networks). Service names and job.services.<name>.ports work as on GitHub; two services on one port would collide.
Runner pools are called runner providers (Add a provider).
GitLab CI, next to GitHub: connect GitLab with a token (made on GitLab in one click, with the scopes ticked), then turn projects on. Jobs whose tags: name your label (tags: [superci/cpu=8]) run on your runners; GitLab’s own runners keep the rest.
GitLab jobs run in Docker, as on GitLab’s own runners: on AWS spot machines, Cloudflare containers (services on localhost) and your computers (macOS jobs on the Mac itself). Modal’s sandboxes have no Docker, so they skip GitLab jobs and say why.
One project runner per set of tags is made once and reused (GitLab has no runner for one job); each machine takes one job and ends.
Repositories, in the sidebar: where jobs come from, GitHub and GitLab in one list (GitLab’s connect form opens in its row, its projects below), and how a workflow asks for your runners on both. Overview’s second step offers either.
Modal as a control plane: a web endpoint in your Modal workspace, its state and settings in Modal Dicts, a sweep every minute. Sign in with Modal on the connect screen and deploy (about a minute).
A Modal control plane starts Modal sandboxes for jobs itself (Add runners → Modal → Add), sized per job.
/health also says the control plane’s runs-on label.
Cloudflare containers run until their job ends: under per-container scheduling Cloudflare stopped a container soon after its Durable Object went idle, which would have cut longer jobs short.
Spend counts only what machines were paid for: a container from when its job began (one that never began cost nothing), an AWS machine from its launch.
GitHub’s price for the same jobs follows each job’s size (2-core $0.006 a minute, 4-core $0.012, and so on) and shows as a small line under what was spent.
A machine that never begins its job (a container that does not start, a spot machine that does not boot) is tried again, twice, then the job fails saying why, instead of staying queued.
A Cloudflare container that cannot start says so as the job’s error.
Cloudflare containers are sized per job (Cloudflare’s per-container scheduling): 1 to 4 CPU, up to 12 GB and 20 GB disk. The standard is the largest, 4 CPU and 12 GB, since Cloudflare bills CPU only while it is used.
Memory a label leaves out is each pool’s usual amount: 4 GB per CPU, up to what the pool has (12 GB on Cloudflare). The machine picker shows what each pool gives.
Cloudflare containers start in eastern North America, near GitHub, and run 20 jobs at once unless a pool’s settings say otherwise.
Updating a control plane waits while jobs run on Cloudflare (the update gives its containers a new setup).