{
  "manifest_version": "1",
  "slug": "ci",
  "title": "CI on exe.dev",
  "headline": "Run CI on exe.dev, a fresh VM for every job",
  "description": "Run CI jobs on exe.dev: each job on a fresh sandbox copied from a warm base VM, billed by the second. A vendor-free recipe, and GitHub, GitLab, Buildkite.",
  "category": "dev-tools",
  "tags": [
    "ci",
    "continuous integration",
    "self-hosted ci",
    "ci runners",
    "ephemeral runners",
    "sandbox per job",
    "build cache",
    "commit status",
    "webhooks",
    "hosted ci alternative"
  ],
  "url": "https://exe.dev/deploy/ci",
  "markdown_url": "https://exe.dev/deploy/ci.md",
  "upstream": {
    "website": "https://exe.dev/docs/standalone-vms",
    "docs": "https://exe.dev/docs/https-api"
  },
  "example_vm_name": "my-ci",
  "time": "20 minutes",
  "stack": [
    {
      "name": "Dispatcher",
      "detail": "ci-dispatcher.py (Python 3, standard library), the ci-dispatcher systemd service on my-ci",
      "port": 8000,
      "data": "~/ci (env, jobs, logs)"
    },
    {
      "name": "Job logs",
      "detail": "served by the same program, private behind exe.dev login",
      "port": 8080,
      "data": "~/ci/logs"
    },
    {
      "name": "Base VM",
      "detail": "my-ci-base, a regular VM with your toolchains and warm caches",
      "data": "~/go, ~/.cache, ~/.npm, Docker images"
    },
    {
      "name": "Job sandboxes",
      "detail": "one cp --sandbox copy of the base per push, 2 vCPU / 4 GiB, deleted when the job ends"
    }
  ],
  "access": [
    {
      "url": "https://my-ci.exe.xyz/",
      "who": "public",
      "what": "signed push webhooks and one-time source downloads, nothing else"
    },
    {
      "url": "https://my-ci.exe.xyz:8080/",
      "who": "private",
      "what": "the job logs, linked from each commit status"
    },
    {
      "url": "ssh my-ci-base.exe.xyz",
      "who": "private",
      "what": "the base VM, to install toolchains and warm caches"
    }
  ],
  "delivers": [
    "A base VM with your toolchains and warm caches, ready to be copied",
    "The ci-dispatcher service, which tests every push in a fresh sandbox and deletes it afterwards",
    "The exe.dev API key and the forge token held by integrations, on no VM",
    "A commit status on every push, linking to the job's log"
  ],
  "deployable": false,
  "updated": "2026-11-04",
  "verified": "2026-11-04",
  "related": [
    "github-actions",
    "gitlab-runner",
    "buildkite",
    "docker-builder",
    "nix-builder",
    "sandbox"
  ],
  "alternatives": [
    "GitHub-hosted runners",
    "GitLab-hosted runners",
    "CircleCI"
  ],
  "files": [
    "https://exe.dev/deploy/ci/ci-dispatcher.py"
  ]
}
