Skip to content

Self-hosting

A spindle is Tangled’s CI runner. It watches the knots of the repositories it serves, runs their .tangled/workflows/*.yml in microVMs or containers, and serves status and logs over XRPC. Run a spindle is the short version of this page.

The spindle signs with an EC P-256 key that it publishes in its DID document. It never generates one, so make it once and point SPINDLE_SERVER_KEY_PATH (NixOS: keyPath) at it:

openssl ecparam -name prime256v1 -genkey -noout -out /var/lib/secrets/spindle-key.pem
  • Linux with KVM for the microvm engine, or Docker for the nixery engine.
  • A recent git.
  • A public hostname with TLS.

Import nixosModules.spindle from the Tangled flake:

{
imports = [ tangled.nixosModules.spindle ];
services.tangled.spindle = {
enable = true;
server = {
hostname = "spindle.example.com";
owner = "did:plc:yourdid";
};
keyPath = "/var/lib/secrets/spindle-key.pem";
contact.email = "admin@spindle.example.com";
artifactStores.s3.bucket = "";
};
systemd.tmpfiles.rules = [
"L+ /var/lib/spindle/images/nixos - - - - ${tangled.packages.x86_64-linux.spindle-nixos-image}"
];
}

The module enables the vhost, vsock and vhost_vsock kernel modules and puts qemu, e2fsprogs, slirp4netns, iproute2 and util-linux on the service’s PATH.

Put it behind an HTTPS reverse proxy to port 6555 that passes websocket upgrades and sends CORS headers. State lives in /var/lib/spindle, and secrets such as AWS credentials go in environmentFile. Run a spindle walks through a first install.

Elsewhere. Build with nix build .#spindle, set the environment, and run spindle. There is no container image.

Workflows pick an image by name, for example image: nixos. Spindle looks for it in SPINDLE_MICROVM_PIPELINES_IMAGE_DIR. The flake builds spindle-nixos-image, spindle-alpine-image and spindle-almalinux10-image.

Admins are the owner plus SPINDLE_SERVER_ADMINS. With SPINDLE_SERVER_ADMISSION=closed (the default) the spindle serves only admins and its allow-list; with open it serves anyone who isn’t banned.

spindle allowlist add friend.example.com
spindle invite

A repository is served when its manifest names this spindle and its owner is admitted.

Repository secrets live in spindle.db (the default) or in OpenBao (SPINDLE_SERVER_SECRETS_PROVIDER=openbao). They are masked in logs and withheld from fork runs.

Logs are written to SPINDLE_SERVER_LOG_DIR, then archived to a disk directory or an S3 bucket. The NixOS module defaults to the tangled-logs bucket; set artifactStores.s3.bucket = "" to keep logs on disk.

Run a cilogs server (nixosModules.cilogs) and set SPINDLE_SERVER_LOGS_ADDR to its host:port. Pushes then print an ssh command that follows the run.

  • Nix binary cache: microVM guests read from SPINDLE_NIX_CACHE_READ_URLS and upload to SPINDLE_NIX_CACHE_UPLOAD_URL.
  • CI cache: backs the workflow cache field. Set SPINDLE_CACHE_BACKEND to disk or s3.

SPINDLE_QUOTA_USER_* and SPINDLE_QUOTA_REPO_* cap workflows, CPU, memory, disk, cache and CI minutes. Override them per user:

spindle quota set friend.example.com --resource workflows --limit 4
spindle quota usage
spindle ban did:plc:spammer

Set SPINDLE_OIDC_ISSUER and SPINDLE_OIDC_KEY_PATH (an EC P-256 key) to give trusted workflows an OIDC token for their audience:.

A spindle can split into a mill, which places jobs, and executors, which run them. Set SPINDLE_ROLE to mill or executor, and register each executor on the mill:

spindle mill executor token generate --db /var/lib/spindle/spindle.db > exec-a.token
spindle mill executor add --db /var/lib/spindle/spindle.db --token-file exec-a.token --label kvm exec-a

The mill and executors must share one artifact store.

  • Pushes never trigger anything: check that the owner is admitted and not banned.
  • A workflow fails with a missing tool or device: install what the error names, see Requirements.
  • Runs are slow to start: check SPINDLE_SERVER_MAX_JOB_COUNT and spindle quota usage.

Spindle Configuration lists every setting.