# GitHub Actions runners

Run GitHub Actions jobs on Composal. Choose VM or sandbox runners, use the included tools, or preload your own Docker image.

## Use the included tools

Composal runners include Node 24, AWS CLI v2 and GitHub CLI (`gh`).
Use them directly in your workflow steps.
`actions/setup-node` or your project's tool manager can select another Node
version.

## Set up runner access

Install Vex 1.17.90 or newer to manage runners. Sign in and check the pools and
images available to your organization:

```sh
curl -fsSL https://dl.composal.ai/install-vex -o /tmp/install-vex.sh
sh /tmp/install-vex.sh
vex login
vex --org acme runner github list
vex --org acme runner images list
vex agent-help
```

Your organization needs GitHub Actions runner access enabled and a GitHub App
connection with access to the repository. Organization owners and administrators
can configure images and pools. A pool serves one exact repository/label pair.

## Choose a VM or sandbox

| Workflow label | Runtime | Choose for |
| --- | --- | --- |
| `composal-vm-x64-4x` | VM | Docker builds, container actions and service containers |
| `composal-sandbox-x64-4x` | Sandbox | Shell and JavaScript actions, lint and tests without Docker |
| `composal-x64-4x` | VM | Existing workflows; keeps VM behavior |

Each label family offers 2x, 4x, 8x and 16x sizes, with 2/4/8/16 CPU cores
and 4/16/16/32 GiB RAM, respectively. Sandboxes do not run a Docker daemon. Use a VM for
container actions, service containers and Docker builds.

## Configure a runner pool

Configure a pool to use the standard tools:

```sh
vex --org acme runner github configure acme/monorepo \
  --label composal-sandbox-x64-4x --default-image --max-concurrency 8
```

Then select that exact label in GitHub Actions:

```yaml
jobs:
  check:
    runs-on: composal-sandbox-x64-4x
    steps:
      - uses: actions/checkout@v7
      - run: node --version && aws --version && gh --version
```

## Mount dependency caches

Mount persistent storage at a package cache directory with a stable key:

```yaml
- uses: composalai/disk-volume@v1.2.0
  with:
    key: npm-store-v1
    path: ~/.npm
- run: npm ci
```

Package managers use the mounted directory in place. The post step flushes
writes; there is no archive restore, copy-back or automatic cache cleanup.
The checkout, workspace and `.git` stay on local runner disk.

Keys are scoped to the GitHub installation and repository. Jobs with the same
key share a writable cache across branches and runner sizes, including pull
requests and failed jobs. Use download stores with package integrity checks,
such as npm, pnpm and Cargo caches. Keep credentials and build outputs out of
these directories. Choose separate keys for tools that require exclusive access.

The action accepts an empty absolute, workspace-relative or `~/` path, with up
to five mounts per job. `cache-hit: true` means the directory exists; the package
manager still installs missing dependencies. The first run after this storage
update starts cold.

## Preload your own tools

For additional tools or pinned dependencies, write a linux/amd64 Ubuntu 24.04
Dockerfile with Node 22 or newer on PATH. Custom images keep their own Node
version. Keep credentials out of image layers. For example:

```dockerfile
FROM node:24-bookworm-slim AS node
FROM ubuntu:24.04
COPY --from=node /usr/local/ /usr/local/
RUN apt-get update && apt-get install -y ca-certificates curl git gh jq
```

Build and push it to your registry. Register the immutable digest printed by the
push, replacing the placeholder with its real 64-character SHA-256:

```sh
docker buildx build --platform linux/amd64 \
  --tag ghcr.io/acme/actions-tools:v1 --push .
vex --org acme runner images create tools-v1 \
  --image ghcr.io/acme/actions-tools@sha256:<64-hex-digest> --wait
vex --org acme runner github configure acme/monorepo \
  --label composal-sandbox-x64-4x --image tools-v1
```

Composal prepares the image once, adding the Actions runner and its supporting
tools. Preparation moves through `pending` and `building`, then ends at `ready`
or `failed`. New runners reuse the prepared image. Composal replaces `CMD` and
`ENTRYPOINT`, so put permanent setup in Dockerfile `RUN` instructions.

## Pull from a private registry

Private registries use `--registry-secret RUNNER_REGISTRY`. This names an active
organization secret exposed to CI whose JSON value contains exactly
`REGISTRY_USERNAME` and `REGISTRY_PASSWORD`, both nonempty strings. Store the
value using secret management with stdin or a private file. Composal uses these credentials
to pull the image; it does not add them to workflow environments. Registration does not grant
registry access.

## Update tools or restore the defaults

Inspect preparation and retry a failed build:

```sh
vex --org acme runner images get tools-v1 --json
vex --org acme runner images prepare tools-v1
vex --org acme runner images wait tools-v1 --json
```

Ready images are immutable. Publish a new digest and image name to update tools.
Images are scoped to their organization. Switching a pool affects new runners;
running jobs finish on their original image. A pool waits for its selected image
to become ready. Verify a real GitHub job receives a `composal-gh-*` runner,
prints the expected tool versions, and reaches a terminal result.

Use `--default-image` to restore the standard toolchain. Omit both image flags
to retain the current selection. Configuring a label does not rewrite workflows
or disable other pools; each pool has its own concurrency limit.
