Deploy Runner
Run GitHub Actions jobs on Skippr-managed runners. Keep workflow YAML in GitHub; point runs-on at a Skippr label. When GitHub queues a matching job, Skippr starts a runner for that session.
Runner is part of Deploy.
Status: Production. Connect the GitHub App, run jobs, and read billed usage. Catalog sizes are Linux x64/ARM _{2,4,8,16,32,64}, Windows windows_x64_*, and Darwin darwin_arm64_8 (skippr-darwin-arm64). Linux ARM and Windows fail closed until a live RunnerHost exists. See the API action reference for the full command list.
Before you start
- Sign in with
skippr-cloud login. See Cloud User Directory. - Ensure your policy allows the
deployactions you need. Owner and Admin already permit Deploy actions. Developer includes create, update, and list; ReadOnly includes get and list.
1. Connect GitHub
skippr-cloud deploy runner connect starts the GitHub App install. After GitHub redirects back, Skippr records the installation and creates a default org-scoped pool for each runner family that currently has a live RunnerHost. Linux x64 becomes linux_x64_2; Darwin becomes darwin_arm64_8 when a Darwin host is live. Families without a live host get no default pool. Connecting again keeps an existing default pool. Terraform and CDKTF still import an installation id for ACME and existing orgs.
skippr-cloud deploy runner connect --input - <<JSON
{
"organization": "acme"
}
JSONUsage: skippr-cloud deploy runner get-usage --input '{"period":"2026-09"}'. Jobs run as soon as a RunnerHost has capacity — you do not need a paid plan to build. Subscribe with skippr-cloud deploy runner create-checkout-session for included minutes and invoices.
2. Record the GitHub App installation (import)
If you already have an installation id, import it instead of connect.
Steps below show the same import flow as CLI, Terraform, CDKTF TypeScript, or CDKTF Python. For provider credentials, see Terraform and CDKTF.
skippr-cloud deploy runner create-github-installation --input - <<JSON
{
"installationId": "12345678",
"accountLogin": "acme",
"accountType": "Organization"
}
JSONresource "cloud_deploy_runner_github_installation" "app" {
installation_id = "12345678"
account_login = "acme"
account_type = "Organization"
}import { CloudProvider, CloudDeployRunnerGithubInstallation } from "@skippr/provider-cloud";
const cloud = new CloudProvider(this, "cloud", { region: "eu-central-1" });
const installation = new CloudDeployRunnerGithubInstallation(this, "app", {
installationId: "12345678",
accountLogin: "acme",
accountType: "Organization",
provider: cloud,
});from skippr_cdktf import CloudProvider, CloudDeployRunnerGithubInstallation
cloud = CloudProvider(self, "cloud", region="eu-central-1")
CloudDeployRunnerGithubInstallation(
self,
"app",
installation_id="12345678",
account_login="acme",
account_type="Organization",
provider=cloud,
)2. Bind a repository
Use the numeric GitHub repository id as repoId:
skippr-cloud deploy runner create-repository-binding --input - <<JSON
{
"repoId": "987654321",
"installationId": "12345678",
"owner": "acme",
"name": "app",
"defaultBranch": "main"
}
JSONresource "cloud_deploy_runner_repository_binding" "app" {
repo_id = "987654321"
installation_id = cloud_deploy_runner_github_installation.app.installation_id
owner = "acme"
name = "app"
default_branch = "main"
}import { CloudDeployRunnerRepositoryBinding } from "@skippr/provider-cloud";
const binding = new CloudDeployRunnerRepositoryBinding(this, "app", {
repoId: "987654321",
installationId: installation.installationId,
owner: "acme",
name: "app",
defaultBranch: "main",
provider: cloud,
});from skippr_cdktf import CloudDeployRunnerRepositoryBinding
CloudDeployRunnerRepositoryBinding(
self,
"app",
repo_id="987654321",
installation_id=installation.installation_id,
owner="acme",
name="app",
default_branch="main",
provider=cloud,
)Optional siteId attaches Site preview URLs for that repository. Leave it unset unless the repo publishes a Site.
3. Create a runner pool
skippr-cloud deploy runner create-runner-pool --input - <<JSON
{
"poolId": "ci",
"installationId": "12345678",
"repositoryId": "987654321",
"size": "linux_x64_2",
"labels": ["self-hosted", "linux", "x64", "skippr-linux-x64-2"]
}
JSONresource "cloud_deploy_runner_pool" "ci" {
pool_id = "ci"
installation_id = cloud_deploy_runner_github_installation.app.installation_id
repository_id = cloud_deploy_runner_repository_binding.app.repo_id
size = "linux_x64_2"
labels = ["self-hosted", "linux", "x64", "skippr-linux-x64-2"]
}import { CloudDeployRunnerPool } from "@skippr/provider-cloud";
new CloudDeployRunnerPool(this, "ci", {
poolId: "ci",
installationId: installation.installationId,
repositoryId: binding.repoId,
size: "linux_x64_2",
labels: ["self-hosted", "linux", "x64", "skippr-linux-x64-2"],
provider: cloud,
});from skippr_cdktf import CloudDeployRunnerPool
CloudDeployRunnerPool(
self,
"ci",
pool_id="ci",
installation_id=installation.installation_id,
repository_id=binding.repoId,
size="linux_x64_2",
labels=["self-hosted", "linux", "x64", "skippr-linux-x64-2"],
provider=cloud,
)Linux x64 and Linux ARM share the Depot ladder _{2,4,8,16,32,64} (4 GiB RAM per vCPU). Public labels are skippr-linux-x64-{n} and skippr-linux-arm64-{n}; the unsuffixed aliases map to _2. Windows is windows_x64_{2,4,8,16,32,64} with no RAM accelerator. Darwin is darwin_arm64_8 only (skippr-darwin-arm64, 24 GB RAM). Disk and RAM accelerator follow the size label; you do not pick a disk SKU. linux_x64_1 and other Darwin vCPU ladders are not offered.
4. Target the pool from GitHub Actions
Keep using GitHub Actions YAML. Point runs-on at the pool labels:
jobs:
test:
runs-on: [self-hosted, skippr-linux-x64-16]
steps:
- uses: actions/checkout@v4
- run: cargo test
darwin:
runs-on: [self-hosted, skippr-darwin-arm64]
steps:
- uses: actions/checkout@v4
- run: cargo testWhen GitHub queues a matching workflow_job, Skippr starts a new runner for that session when capacity is free. Otherwise the session stays queued. The previous job's disk is not reused. List sessions with skippr-cloud deploy runner list-runner-sessions. Cancel an in-flight session with skippr-cloud deploy runner cancel-runner-session. Deleting a pool stops in-flight sessions, then removes the pool.
Linux jobs run skippr-ubuntu-24.04-x64. That image includes Ubuntu 24.04's default python3 with venv, pip, and headers, plus rustup, Node.js, and Docker. python3 -m venv works without an apt install in the workflow. Pin a different CPython with actions/setup-python; do not bake extra interpreters into the runner image.
Plans, cache, and invoices
Developer $20 / month includes 2,000 billed minutes and 25 GB cache. Startup $200 / month includes 20,000 billed minutes and 250 GB cache. Linux 2-vCPU / 8 GB is 1× billed minutes at $0.004 / billed minute overage. Darwin is $0.08 / elapsed minute. Cache overage is $0.20 / GB-month on Skippr object storage {tenant}--deploy-cache. Linux and Darwin IsolatedGuest jobs use stock actions/cache@v4 against Skippr Cache Service v2. Unused entries expire after 7 days. Windows stays on GitHub's cache.
The clock starts when GitHub marks the job in progress. Usage for a UTC month includes billed minutes, elapsed minutes, cache GB, and breakdowns by runner size and repository: skippr-cloud deploy runner get-usage --input '{"period":"2026-09"}'. The current month's invoice is an unsealed preview. Closed months return a sealed overage invoice: skippr-cloud deploy runner get-invoice --input '{"period":"2026-09"}'. Checkout: skippr-cloud deploy runner create-checkout-session then skippr-cloud deploy runner complete-checkout-session. Until you subscribe, jobs still run; usage meters emit, and Skippr does not create a Stripe invoice for that usage.
Limits and errors
- Size is a closed catalog enum (Linux x64/ARM, Windows x64, Darwin
darwin_arm64_8). Jobs stay queued when no RunnerHost of that family has enough free slots. - Fork pull requests are denied unless the pool fork policy allows them.
- If no capacity is free for the job size, Skippr accepts the GitHub webhook and keeps the session queued. There is no per-pool concurrency cap.
- Duplicate webhook deliveries are ignored.
- Long-running machines are not a product you call. Do not create fleets to host runners.
