Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions Tutorials/GitHub-actions-runner.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
---
title: "Run GitHub Actions on Blaxel"
description: "Run GitHub Actions self-hosted runners on Blaxel ephemeral micro-VMs using the Blaxel GitHub App."
sidebarTitle: "GitHub Actions runner"
---

Blaxel can act as a self-hosted GitHub Actions runner. Each workflow job runs inside an ephemeral micro-VM that is spun up on demand and discarded when the job finishes. The Blaxel GitHub App receives `workflow_job` events from GitHub and automatically launches your job.

<Note>
This tutorial assumes that you have a Python project containing unit tests and a GitHub Actions workflow that runs those unit tests using `pytest`.
</Note>

## Prerequisites

- A Blaxel account and workspace
- A GitHub repository containing a Python project with tests
- The Blaxel CLI (`bl`) installed and authenticated

## 1. Create the Dockerfile

The Dockerfile defines the micro-VM filesystem. A good starting point is the `catthehacker` Ubuntu image, which includes most tools found on GitHub-hosted runners. Here is an example for a Python test runner:

```dockerfile
FROM ghcr.io/catthehacker/ubuntu:full-24.04

USER root

# Install Python
RUN apt-get update -qq \
&& apt-get install -y -qq --no-install-recommends \
python3 python3-pip python3-venv \
&& rm -rf /var/lib/apt/lists/*

# Install pytest
RUN pip3 install pytest pytest-cov pytest-xdist

# Install GitHub Actions runner
ARG RUNNER_VERSION=2.333.0
RUN curl -fSL --retry 3 --retry-delay 5 \
-o /tmp/runner.tar.gz \
"https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" \
&& mkdir -p /actions-runner \
&& tar xzf /tmp/runner.tar.gz -C /actions-runner \
&& rm /tmp/runner.tar.gz

COPY start.sh /start.sh
RUN chmod +x /start.sh

ENV RUNNER_ALLOW_RUNASROOT=1

ENTRYPOINT ["bash", "/start.sh"]
```

You can install additional tools or set environment variables using standard `RUN`, `COPY`, and `ENV` instructions. When you are ready to use a different image, edit the Dockerfile and redeploy.

## 2. Create the entrypoint script

The Dockerfile references an entrypoint script (`start.sh`) that starts Docker, fetches the JIT config from Blaxel and starts the GitHub Actions runner:

```bash
#!/bin/bash
set -euo pipefail

export RUNNER_ALLOW_RUNASROOT=1
RUNNER_DIR="/actions-runner"
export HOME="${RUNNER_DIR}"
TASK_INDEX=${TASK_INDEX:-0}

start_docker() {
command -v dockerd &>/dev/null || return 0

local fs_type="unknown"
local fs_size="unknown"
if mountpoint -q /var/lib/docker 2>/dev/null; then
read -r fs_type fs_size < <(df -hT /var/lib/docker | awk 'NR == 2 {print $2, $3}')
fi

# Docker overlay2 cannot use an overlay filesystem as its backing store.
# That happens when Blaxel overlays a non-empty image path with the volume.
if [ "${fs_type}" = "ext4" ] || [ "${fs_type}" = "xfs" ]; then
STORAGE_DRIVER="overlay2"
echo "Docker storage: ${fs_type} ${fs_size} -> overlay2"
else
STORAGE_DRIVER="vfs"
echo "Docker storage: ${fs_type} ${fs_size} -> vfs"
fi

dockerd --storage-driver="$STORAGE_DRIVER" &>/var/log/dockerd.log &
DOCKERD_PID=$!

for i in $(seq 1 30); do
docker info &>/dev/null && break
if ! kill -0 "$DOCKERD_PID" 2>/dev/null; then
echo "ERROR: dockerd died unexpectedly"
tail -20 /var/log/dockerd.log 2>/dev/null
return 1
fi
sleep 1
done

if docker info &>/dev/null; then
echo "Docker ready ($STORAGE_DRIVER)"
else
echo "ERROR: dockerd failed to start"
tail -20 /var/log/dockerd.log 2>/dev/null
return 1
fi
}

start_docker

# Fetch task arguments from Blaxel execution data
if [ -n "${BL_EXECUTION_DATA_URL:-}" ]; then
echo "Fetching task data from Blaxel..."
TASK_DATA=$(curl -sf "${BL_EXECUTION_DATA_URL}")
JIT_CONFIG=$(echo "${TASK_DATA}" | jq -r ".tasks[${TASK_INDEX}].JIT_CONFIG")
fi

if [ -z "${JIT_CONFIG:-}" ] || [ "${JIT_CONFIG}" = "null" ]; then
echo "Error: JIT_CONFIG not found in task data"
exit 1
fi

cd "${RUNNER_DIR}"
echo "Starting GitHub Actions runner in JIT mode..."
exec ./run.sh --jitconfig "${JIT_CONFIG}"
```

## 3. Create `blaxel.toml`

The `blaxel.toml` file defines the job configuration. This example uses ephemeral volumes and targets the GitHub repositories this runner is allowed to serve:

```toml
type = "job"
name = "pytest-runner"

[runtime]
memory = 16384 # 16 GB of RAM allocated to the micro-VM
timeout = 3600 # maximum job duration: 1 hour (in seconds)
maxRetries = 0 # no automatic retries on failure
diskPercent = 5 # base root disk allocation (percentage)

[githubRunner]
repositories = ["owner/repo"]

[[volumes]]
# Dedicated storage for the Docker daemon
name = "docker"
mountPath = "/var/lib/docker"
type = "ephemeral"
sizeMb = 10240

[[volumes]]
# General-purpose scratch space
name = "tmp"
mountPath = "/tmp"
type = "ephemeral"
sizeMb = 102400
```

The `repositories` field lists the GitHub repositories this runner is allowed to pick up jobs from. The format is `owner/repo`.

## 4. Deploy the job

Deploy the job to Blaxel:

```bash
bl deploy
```

<Note>
Due to the large size of the image, the build process can take up to 40 minutes in some cases.
</Note>

## 5. Install the Blaxel GitHub App

1. Log in to the Blaxel Console.
2. Navigate to **Hosting** > **Jobs** > `<your-job-name>` > **Settings**.
3. Scroll to the **GitHub Runner** section. It should show as **Active**.
4. Click **Edit**, then click **+**.
5. Follow the instructions to authorize and install the Blaxel GitHub App on the repository.
6. Select the repository and click **Save**.

## 6. Redeploy the job

```bash
bl deploy --skip-build
```

## 7. Use the runner in your GitHub Actions workflow

In any GitHub Actions workflow, set `runs-on` to `<workspace>/<job-name>` to target your Blaxel runner:

```yaml
name: test

on:
push:
branches:
- main

Comment thread
mendral-app[bot] marked this conversation as resolved.
permissions:
contents: read


jobs:
test:
runs-on: my-blaxel-workspace/pytest-runner

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dependencies
run: pip install -r requirements.txt

- name: Run tests
id: run-tests
run: |
pytest --tb=short -q
```

Each time this workflow runs, Blaxel spins up a fresh ephemeral micro-VM, executes the job, and discards the VM when it finishes.

## Resources

<CardGroup cols={2}>
<Card title="Jobs reference" href="/Jobs/Overview">
Learn more about Blaxel Jobs and configuration options.
</Card>
<Card title="blaxel.toml reference" href="/deployment-reference">
Full reference for all `blaxel.toml` fields.
</Card>
</CardGroup>
8 changes: 8 additions & 0 deletions deployment-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ The format of Blaxel's configuration file is described below.
# maximum number of retries
# maxRetries = 0

# base root disk allocation as a percentage of available disk (only valid for resource type="job")
# diskPercent = 5

# volumes (optional, for resource type="agent", "sandbox", "volume-template", "job")
# attaches persistent storage to the resource
# the resource must be pinned to the same region as the volume
Expand All @@ -95,6 +98,11 @@ The format of Blaxel's configuration file is described below.
# volume default size in MB (only valid for resource type="volume-template")
# defaultSize = 1024

# GitHub Runner integration (optional, only valid for resource type="job")
# [githubRunner]
# list of GitHub repositories this runner can pick up jobs from
# repositories = ["owner/repo"]

# job trigger (optional)
# [[triggers]]
# trigger identifier
Expand Down
6 changes: 6 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@
"Tutorials/Custom-Agents"
]
},
{
"group": "Jobs",
"pages": [
"Tutorials/GitHub-actions-runner"
]
},
{
"group": "Complete examples",
"pages": [
Expand Down