{
  "manifest_version": "1",
  "slug": "github-actions",
  "title": "GitHub Actions",
  "headline": "Run GitHub Actions jobs on exe.dev, a fresh VM per job",
  "description": "Move GitHub Actions off GitHub-hosted runners: each job gets its own exe.dev sandbox, copied from a warm base VM and deleted when the job ends.",
  "category": "dev-tools",
  "tags": [
    "github actions",
    "self-hosted runners",
    "ephemeral runners",
    "jit runners",
    "ci",
    "continuous integration",
    "github-hosted runners alternative",
    "sandbox per job"
  ],
  "url": "https://exe.dev/deploy/github-actions",
  "markdown_url": "https://exe.dev/deploy/github-actions.md",
  "upstream": {
    "website": "https://docs.github.com/en/actions/concepts/runners/self-hosted-runners",
    "source": "https://github.com/actions/runner",
    "docs": "https://docs.github.com/en/rest/actions/self-hosted-runners#create-configuration-for-a-just-in-time-runner-for-a-repository",
    "license": "MIT (the runner)"
  },
  "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"
    },
    {
      "name": "Base VM",
      "detail": "my-ci-base, a regular VM with the runner, your toolchains, and warm caches",
      "data": "~/actions-runner, ~/.cache"
    },
    {
      "name": "Job sandboxes",
      "detail": "one cp --sandbox copy of the base per job, 2 vCPU / 4 GiB, deleted when the job ends"
    }
  ],
  "access": [
    {
      "url": "https://my-ci.exe.xyz/",
      "who": "public",
      "what": "GitHub's signed workflow_job webhooks, nothing else"
    },
    {
      "url": "ssh my-ci-base.exe.xyz",
      "who": "private",
      "what": "the base VM, to install toolchains and warm caches"
    }
  ],
  "delivers": [
    "A base VM with the GitHub Actions runner, ready to be copied",
    "The ci-dispatcher service, which gives each queued job a fresh sandbox and deletes it afterwards",
    "The exe.dev API key and the GitHub token held by integrations, on no VM",
    "A workflow running `runs-on [self-hosted, exe]` jobs, with `started` and `deleted` lines in `journalctl -u ci-dispatcher`"
  ],
  "deployable": false,
  "updated": "2026-11-04",
  "verified": "2026-11-04",
  "related": [
    "sandbox",
    "forgejo",
    "gitea",
    "docker-host",
    "code-server",
    "linux-desktop"
  ],
  "alternatives": [
    "GitHub-hosted runners",
    "Actions Runner Controller"
  ],
  "files": [
    "https://exe.dev/deploy/github-actions/base-setup.sh",
    "https://exe.dev/deploy/github-actions/ci-dispatcher.py"
  ]
}
