GitHub Actionsのセルフホステッド ランナーをLambda MicroVMs上で実行してみた
リテールアプリ共創部@大阪の岩田です。
先日公開されていた以下の記事を読み、Lambda MicroVMs上にGitHub Actionsのセルフホステッドランナーを構築する構成を試してみたくなりました。
ECSでRunnerを都度起動するアーキテクチャのデメリットとして挙げられている起動時間やスケールアウト速度の問題をLambda MicroVMsを利用することで解消できないか?と考えてのことです。
さっそく検証用の環境を作ってみようと思ったところ...
すでに先人による成果物がConstruct Hubで公開されていました。今回はこちらのConstructを使ってLambda MicroVMs上にセルフホステッドランナーを構築して検証していきます。
環境
今回利用した環境は以下の通りです。
- cdk-github-microvm-runners: 0.1.7
- aws-cdk-lib: 2.266.0
構成概要
詳細はcdk-github-microvm-runnersのGitHubリポジトリにて解説されています。
ざっくりした流れは以下の通りです。
- GitHub Actionsのワークフローが起動するとWebHookでLambdaを呼び出す
- Lambdaは呼び出し元を検証し、問題なければSQSにメッセージを投入
- SQSからLambaが起動、LambdaがMicroVMを起動してGitHubにJust-in-time runnerとしてセルフホステッドランナーを登録
- セルフホステッドランナーがワークフローを実行する
起動したMicroVMの情報はDynamoDBで管理されており、ジョブの重複実行などが発生しないように制御されているとのことです。
やってみる
それでは早速セルフホステッドランナー on Lambda MicroVMsの環境を作っていきましょう。
CDKで必要なリソース一式をデプロイ
まず npx cdk init --language typescript を実行してCDKコードを準備、続いてnpm install cdk-github-microvm-runnersでcdk-github-microvm-runnersをインストールします。
準備できたらGetting startedにならってCDKのコードを書きます。
import { App, CfnOutput, Stack } from 'aws-cdk-lib';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
import {
GithubAppId,
GithubAppKey,
GithubAuth,
GithubMicrovmRunners,
MicrovmSize,
RunnerScope,
} from 'cdk-github-microvm-runners';
const app = new App();
const stack = new Stack(app, 'Runners', { env: { region: 'us-east-1' } });
const appId = Secret.fromSecretNameV2(
stack,
'AppId',
'microvm-runner/dev/app-id',
);
const privateKey = Secret.fromSecretNameV2(
stack,
'AppKey',
'microvm-runner/dev/app-private-key',
);
const webhookSecret = Secret.fromSecretNameV2(
stack,
'WebhookSecret',
'microvm-runner/dev/webhook-secret',
);
const runners = new GithubMicrovmRunners(stack, 'Runners', {
github: GithubAuth.app({
appId: GithubAppId.fromSecret(appId),
privateKey: GithubAppKey.fromSecret(privateKey),
webhookSecret,
}),
scope: RunnerScope.org('<ここにセルフホステッドランナーを使いたいOrgの名前を設定>'),
});
// A runner class: the `microvm` label, on 4 GB VMs.
runners.addRunnerClass('microvm', { size: MicrovmSize.GB4 });
new CfnOutput(stack, 'WebhookUrl', { value: runners.webhookUrl });
new CfnOutput(stack, 'SetupCommand', { value: runners.setupCommand });
RunnerScope.orgにセルフホステッドランナーを登録したいGitHubのOrg名を設定するのがポイントです。
コードの準備ができたので、npm cdk deployでスタックをデプロイします。しばらく待つと各種のリソースが作成されます。

MicroVMのイメージも登録されています。

ちなみに上記のMicroVMのイメージをビルドするのに利用されたDockerfileをS3からDLすると、中身は以下の通りでした。
FROM public.ecr.aws/lambda/microvms:al2023-minimal
RUN dnf install -y libicu bash git docker jq tar zip unzip nodejs22 sudo shadow-utils && dnf clean all
RUN dnf install -y gh --repofrompath gh-cli,https://cli.github.com/packages/rpm || true
RUN curl -fsSL https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip -o /tmp/awscliv2.zip && unzip -q /tmp/awscliv2.zip -d /tmp && /tmp/aws/install && rm -rf /tmp/aws /tmp/awscliv2.zip
RUN useradd -m runner && usermod -aG docker runner || groupadd docker && usermod -aG docker runner
RUN mkdir -p /opt/runner && cd /opt/runner && curl -fsSLo r.tgz https://github.com/actions/runner/releases/download/v2.335.1/actions-runner-linux-arm64-2.335.1.tar.gz && tar xzf r.tgz && rm r.tgz && chown -R runner:runner /opt/runner
COPY microvm-runner/agent.mjs /opt/microvm-runner/agent.mjs
COPY microvm-runner/entrypoint.sh /opt/microvm-runner/entrypoint.sh
RUN chmod +x /opt/microvm-runner/entrypoint.sh
ENTRYPOINT ["/opt/microvm-runner/entrypoint.sh"]
イメージをカスタマイズしたい場合はCDKのコードで調整も可能です。例えばRunnerImage.fromInlineを利用した以下のような書き方が用意されています。
runners.addRunnerClass('custom', {
size: MicrovmSize.GB4,
image: RunnerImage.fromInline(`
FROM public.ecr.aws/lambda/microvms:al2023-minimal
...
略
`),
});
詳細については以下のリンクで解説されています。
GitHub Appのセットアップ
スタックがデプロイできたのでGitHub Appを作成します。セットアップ用のコマンドは先程デプロイされたCFnスタックのOutputにも出力されていますが、以下のようなコマンドになります。
npx cdk-github-microvm-runners@0.1 setup --org <対象のGitHub Org名> --stack Runners --region us-east-1
--stackオプションはCFnのスタック名と、--regionはスタックがデプロイされたリージョンに合わせてください。
上記のコマンドを実行するとGitHub Appを作成する画面が開くので、ユニークになるように名前を指定します。

作成したGitHub AppをOrgにインストールして良いか確認されるので、インストールをクリックします。
インストールが完了したらブラウザは閉じておきましょう。
これで必要な準備は完了です。一連の処理の裏側でCDKのコードで指定したmicrovm-runner/dev/app-id等のシークレット値も登録されています。

セルフホステッドランナーを使うワークフローを動かしてみる
ここまででセルフホステッドランナーを使う準備ができたので、適当にリポジトリを作成してGitHub Actionsのワークフローを起動してみます。まず以下のようなシンプルなワークフローを試してみました。
on:
workflow_dispatch
jobs:
test:
runs-on: [self-hosted, microvm]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: test
run: |
echo test
sleep 120
ワークフローを起動したあと、Orgの設定からRunnersを確認すると、セルフホステッドランナーが登録されたことが分かります。

さらに数秒待つと、登録されたセルフホステッドランナー上でワークフローが実行開始されます。
ログでは以下の通りです。ちゃんと実行できていることが分かります。

ログの詳細を確認すると、ランナーにジョブがピックされるまで約20秒程度かかっているようです。
2026-08-27T02:56:56.9760000Z Waiting for a runner to pick up this job...
2026-08-27T02:57:15.4380000Z Job is about to start running on the runner: microvm-runner-ba2f8119-bc1605de
cdk-github-microvm-runnersのドキュメントによると、各フェーズの所要時間は以下の通りになるそうです。ワークフローがトリガーされてから実際に処理が始まるまで、全体で約25秒程度の待ち時間になります。
| Segment | Time | What happens |
|---|---|---|
| Queued → the VM is running | 6.8 s | webhook delivery, the queue, the launcher, VM boot |
| VM running → the runner's first log line | 8.2 s | run.sh, the .NET host starting, assemblies loading |
| Runner starting → connected to GitHub | 9.0 s | reading its configuration, registering |
| Connected → the job begins | 1.0 s | GitHub assigns the queued job to it |
| Total | 25 s |
※ 表は https://github.com/schuettc/cdk-github-microvm-runners/blob/main/docs/architecture.md より引用
せっかくなのでDynamoDBのテーブルの中身も確認しておきましょう。テーブルをScanすると、以下のようなアイテムが登録されていました。

実際にデータを見ると、裏側で動いている処理がよりイメージしやすいですね。
せっかくなので別のワークフローを作ってDockerが使えることを確認してみましょう。ワークフローの定義は以下です。
on:
workflow_dispatch
jobs:
test:
runs-on: [self-hosted, microvm]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: test
run: |
docker run --rm hello-world
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: postgres
runの中でdockerコマンドを実行しているのと、servicesの指定でpostgresのコンテナを起動しています。
このワークフローを実行した際のログは以下の通りでした。


ちゃんとLambda MicroVMs上でDockerが利用できていますね。CodeBuildの実行環境にLambdaを選択した場合はDockerが利用できませんが、セルフホステッドランナー on Lambda MicroVMsであればDockerも問題なく利用可能です。やったぜ!
考察
とりあえずLambda MicroVMs上でGitHub Actionsのセルフホステッドランナーを稼働させてワークフローが実行できましたが、果たしてこの構成は有効な構成なのか考察してみます。
処理時間が同じという前提で料金を比較
まずセルフホステッドランナーを利用した場合もワークフローの実行時間が変わらないという前提のもと単純な料金だけで比較してみます。
GitHub-hosted runnersの場合、SKU linux_2_core_armの料金は$0.005/分となります。linux_2_core_arm相当のインスタンスをLambda MicroVMsで起動するとバージニアリージョンの場合でも以下の試算になります。
- vCPU price per second: 2vCPU × $0.0000276944
- Memory price per second (per GB): 8GB × $0.0000036667
1分あたりの料金は(2×0.0000276944 + 8 * 0.0000036667) × 60で$0.00508334となります。簡略化のために割愛していますが、実際にはさらにスナップショットを保存するためのストレージ料金なども発生します。そのため、単純な試算ではGitHub-hosted runnersの方が安いという試算になりますが、Saving Plan等を活用することでコストメリットが出せる可能性はあります。
あとはLambda MicorVMsの場合は課金が分単位ではなく秒単位という点もメリットと言えます。GitHub-hosted runnersの場合は課金が分単位に丸められるため、Lambda MicroVMsなら1分31秒の課金で済むところがGitHub-hosted runnersの場合は2分の課金になるというケースも考えられます。
処理時間が同じになるのか?
先程の試算はワークフローの実行時間が変わらないという前提のもとでの試算でしたが、実際はどうなんでしょうか?
今回利用したcdk-github-microvm-runnersはJust-in-time runnerとしてセルフホステッドランナーを登録し、MicroVMを毎回Terminateする設計になっていますが、Lambda MicroVMsは一時停止と再開に対応しています。ワークフローの実行完了後にMicroVMを一時停止し、次回同じワークフローが起動した時は一時停止中のMicroVMを再開するようなアーキテクチャで実装しなおせば、ステートを保持できるというLambda MicroVMsの特徴を最大限に活かせそうです。
例えばワークフロー内でapt installを実行する処理があったとします。MicroVMの初回起動時はパッケージのインストール処理がフルに実行されるため処理時間が長くなりますが、2回目以後のワークフロー実行時はパッケージインストール済のMicroVMを再開するようにすればインストール処理はスキップされてコマンドの実行は高速に完了するはずです。
さらに進んで、自身のワークロードに最適化したMicroVMのイメージをビルドするというアプローチも考えられます。ワークフローの中でapt installを実行するのではなく、MicroVMのイメージビルド時にapt installするようにすればワークフローの処理時間をさらに削減できます。
特定のプロジェクトだけでセルフホステッドランナーを利用したいのか、それとも組織の基盤として複数のチームにセルフホステッドランナーを提供したいのか?要件次第でMicroVMのイメージにどこまで手を加えられるのかの考え方は変わってきそうですが、セルフホステッドランナーという特性を活かしたワークフローの処理時間削減は一考の価値がありそうです。
まとめ
Lambda MicroVMs上にGitHub Actionsのセルフホステッドランナーを構築して試してみました。作り込めばなかなか面白いことができそうじゃないでしょうか?
GitHub Actionsの処理時間やコストに悩みを抱えている方は一度セルフホステッドランナー on Lambda MicroVMsという構成を検討してみてはいかがでしょうか?




