Self-hosted service
traderepublic/Cilicon avatar
traderepublic/Cilicon

Cilicon: ephemeral macOS CI VMs on Apple Silicon, driven from a single YAML file

🛠️ Self-Hosted ephemeral macOS CI on Apple Silicon

1,190 stars43 forksSwiftMIT

At a glance

What is it?
Cilicon is a macOS app from Trade Republic that boots throwaway macOS VMs through Apple's Virtualization Framework and registers them as GitHub Actions, Buildkite, GitLab or script runners. It is a good fit if you already own Apple Silicon hardware and want clean machines per job; it is a poor fit if you need Linux runners or a GUI-driven fleet.
Who is it for?
Adopt Cilicon if you have spare Apple Silicon Macs, need macOS-only CI jobs, and can accept that image lifecycle management (pulling, pruning ~/.tart, deleting outdated images by hand) is your responsibility rather than the tool's. Do not adopt it if your pipeline is Linux-first, if you need orchestration across many hosts, or if you want a runner pool that manages its own capacity.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap Cilicon fills: macOS jobs without a permanent runner

Hosted CI providers give you Linux containers cheaply and macOS minutes expensively. If your project is a Swift package, an iOS app, or anything that needs Xcode and a real macOS kernel, you either pay per minute or you keep a Mac mini in a cupboard with a runner installed on it. That second option has the usual problem: state accumulates. Derived data, keychain entries, simulators, leftover build artefacts. A job that passes on a clean machine fails on the runner, and nobody can reproduce why.

Cilicon targets exactly that situation. It is a macOS application that uses Apple's Virtualization Framework to create, provision and run ephemeral CI VMs on Apple Silicon, and the README describes the result as near-native performance. Each job gets a machine that did not exist before it started. The intended audience is a team that already owns M-series hardware and wants the clean-machine guarantee without a hosted macOS bill. It supports GitHub Actions, Buildkite Agent, GitLab Runner and arbitrary scripts, so the same app covers several CI systems rather than one.

The Cilicon cycle: clone an image, boot it, register a runner, discard it

The README describes the operation as a simple cycle and illustrates it with a diagram rather than a written protocol, so the mechanism has to be read from the configuration surface. Cilicon takes a source image, boots a VM from it, connects over SSH, installs and starts the appropriate runner binary inside the guest, and lets the job run. When the job finishes, the VM is discarded and the next job starts from the same source image again.

The SSH detail is the significant change in version 2.0. The 1.0 line relied on a user-defined Login Item script inside the VM to bootstrap the runner; 2.0 embeds an SSH client and executes commands on the VM directly. That removes a class of failure where the guest-side script silently did not run, and it makes the host the single point of control. It also means the guest must have SSH enabled and reachable, which the migration notes make explicit: when converting a 1.0 image you must enable SSH and set credentials in the config, or fall back to the default admin:admin.

Image acquisition goes through an integrated OCI client. Cilicon has partly adopted the tart image format and can convert 1.0 images to it, and it can pull pre-built CI images published for tart. The README points at Cirrus Labs' macos-sonoma-xcode images as the recommended source. Downloaded images land in ~/.tart on the host. That directory is the real state of the system: the VMs are ephemeral, the image cache is not.

Getting a runner registered: cilicon.yml and the provisioner blocks

Configuration is a single file, cilicon.yml, expected in the host OS home directory. The README defers the full schema to Config.swift in the repository, which is worth reading before you commit to a setup, because the examples only cover the common paths.

The GitHub Actions provisioner needs a GitHub App with Self-hosted runners Read & Write permission at the organization level, plus the private key file on disk:

source: oci://ghcr.io/cirruslabs/macos-runner:sequoia provisioner: type: github config: appId: <APP_ID> organization: <ORGANIZATION_SLUG> privateKeyPath: ~/github.pem

The GitLab provisioner is more configurable than the others. Beyond gitlabURL and runnerToken it accepts executor (defaults to shell), maxNumberOfBuilds (defaults to 1), downloadLatest (defaults to true, and when false it expects the binary in the user's home directory), downloadURL (defaults to GitLab's official S3 bucket), and tomlPath, which if set causes Cilicon to ignore the other runner variables and pass the path straight to the runner executable. That last key is the escape hatch for anyone whose runner configuration does not fit the four convenience fields.

Buildkite needs only agentToken. The script provisioner takes a run block and executes it in the guest, which is how you would wire up a CI system Cilicon does not support natively. There is also a top-level consoleDevices list, used for compatibility with tools like tart-guest-agent that expect specific console devices to be present, and sshConnectMaxRetries, which defaults to 10.

Two image rules are easy to get wrong. The oci:// scheme must be prepended to a hosted image URL, otherwise Cilicon treats the string as a local filesystem path. And the README advises against the latest tag, recommending a specific Xcode version such as 14.3 instead.

Where Cilicon stops being the right tool

The most concrete limitation is stated as a warning at the top of the README: there appears to be an issue with swift-nio based SSH on macOS 15.0 through 15.3.X, and users should be on macOS 15.4 or later for a more reliable experience. Since SSH is the channel through which Cilicon provisions every VM in 2.0, a host in that version range is a host where the core mechanism is known to be flaky. That is a hard floor on which machines you can deploy to, and it is not something the configuration file can work around.

Image lifecycle is the second gap. The README states that OCI images reside in ~/.tart and that the folder should be cleared of unused images periodically. There is no documented automatic pruning, and no documented rollback path if a new image breaks your jobs. The README also notes that images with newer macOS versions may be published with the same Xcode version installed, and that upgrading may require manually deleting the outdated image and restarting Cilicon. So the tool gives you ephemeral compute but not ephemeral storage or self-managing image versions.

The third limitation is scope. Cilicon runs on Apple Silicon and produces macOS guests. If most of your pipeline is Linux, you are adding a macOS-only appliance to your infrastructure to serve a minority of jobs. There is also no indication in the README of multi-host scheduling or capacity management across several machines; the model is one app on one Mac, cycling VMs on that Mac. A team that needs a fleet with a queue in front of it is looking at a different class of tool.

How it compares to running tart or Anka directly

The closest alternative in the same ecosystem is tart, the Cirrus Labs virtualisation tool whose image format Cilicon has partly adopted and whose images it recommends pulling. The difference is where the orchestration lives. With tart alone you get commands to clone and run VM images; you then write your own glue to decide when a VM starts, how the runner inside it is configured, and when the VM is torn down. Cilicon packages that glue as a long-running macOS app with a declarative provisioner block, so the runner registration for GitHub, GitLab, Buildkite or a custom script is configuration rather than code.

That trade is real in both directions. Cilicon is faster to stand up for the four supported provisioners, and it is harder to adapt when your requirement does not match one of them. The script provisioner softens that, but you are still running inside Cilicon's cycle rather than owning it. If you need scheduling across hosts, custom networking, or a provisioning step that has to happen before the guest boots, tart plus your own automation gives you the room. Cilicon's value is the removal of that automation for the common cases, not extra capability over the underlying framework.

Maintenance, releases and what the MIT licence leaves to you

The repository is not archived and the last push was on 2026-09-14. Releases are tagged and versioned: v2.4.2 on 2025-12-12, v2.4.1 on 2025-12-10 and v2.4.0 on 2025-12-10. The 2.0 release notes describe a schema change, which is the upgrade cost you should budget for if you are on 1.x: the config schema changed slightly, and in most cases renaming vmBundlePath to source is enough. Cilicon detects a 1.0 image and offers to convert it. Anyone still on 1.0 should also expect to enable SSH and set credentials as part of that conversion, or accept the default admin:admin, which is worth changing.

The licence is MIT. That is permissive: you can use, modify and redistribute the app, including commercially, provided the copyright notice and permission notice are preserved. It also means no warranty and no support obligation from Trade Republic. The practical consequence for a CI dependency is that you are responsible for your own upgrade testing. Nothing in the README documents a rollback procedure for a bad image or a bad release, so keeping a known-good image tag pinned and a copy of the previous Cilicon build is a decision you make, not one the project makes for you. Note also that the app itself is MIT while the images you pull are separate artefacts under their own terms, and the GitLab runner binary is downloaded from GitLab's official bucket by default, which is a third-party distribution you are choosing to trust.

Editorial conclusion

Adopt Cilicon if you have spare Apple Silicon Macs, need macOS-only CI jobs, and can accept that image lifecycle management (pulling, pruning ~/.tart, deleting outdated images by hand) is your responsibility rather than the tool's. Do not adopt it if your pipeline is Linux-first, if you need orchestration across many hosts, or if you want a runner pool that manages its own capacity. Before committing, verify three things on your own hardware: that your macOS version is 15.4 or later, since the README warns about swift-nio SSH problems on 15.0 through 15.3.x; that your chosen image tag resolves to a specific Xcode version rather than latest; and that your GitHub App has Self-hosted runners Read & Write at the organization level, because the provisioner cannot register a runner without it.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. traderepublic/Cilicon on GitHub
Community notes

Community notes