
マイクロVM「smolvm」でLLMが生成したコードをOCIイメージごと安全に動かしてみる
はじめに
データ事業本部のkobayashiです。
LLMエージェントにコードを生成・実行させるワークフローが当たり前になってきましたが、「生成されたコードをどこで安全に動かすか」という問題が常につきまといます。Dockerコンテナは軽量ですがホストカーネルを共有するため、カーネル脆弱性経由のコンテナ脱出リスクがゼロではありません。かといってFirecrackerやQEMUで毎回マイクロVMを立てるのは構築コストが高いです。
今回はそのちょうど中間を埋める smolvm というツールを試してみました。200ms未満で起動するマイクロVMをCLIから直接扱え、Docker デーモン不要で OCI イメージをそのまま動かせます。
smolvmとは
smolvmはsmol-machinesプロジェクトが開発しているRust製のマイクロVM実行エンジンです。macOS(Hypervisor.framework)および Linux(KVM)上で、ハードウェアレベルの隔離環境を200ms未満で起動できます。内部的にはlibkrunをVMMとして使用しています。
主な特徴としては以下になります。
- HWレベル隔離: 各ワークロードが独立したカーネル上で動作し、Dockerコンテナとは異なるハイパーバイザ境界で分離される
- 200ms未満起動: VM としては最速クラスの起動時間。Linux native の Docker コンテナ(約100ms)には及ばないものの、Firecracker(<125ms)と並ぶVM起動性能
- Dockerデーモン不要: OCI互換イメージをそのまま Docker Hub / ghcr.io 等から pull して起動できる
- デフォルトで隔離:
--netを明示しない限りネットワークはOFF。信頼できないコードの実行を前提とした設計 .smolmachine形式で配布: OCIイメージとVM起動環境を.smolmachine形式にパッケージ化でき、OCIレジストリ不要で配布可能- macOSネイティブ: Docker Desktop のような常駐VM不要で、Apple Silicon 上で直接動作
Dockerとの違い
| smolvm | Docker | |
|---|---|---|
| 分離レベル | HWレベル(VM) | OSレベル(namespace/cgroup) |
| 起動時間 | 200ms未満(VMとしては最速クラス) | 約100ms(Linux native) |
| macOSでの動作 | Hypervisor.framework を直接利用 | Docker Desktop(内部でLinux VM) |
| デーモン | なし | dockerd 常駐 |
| ネットワーク | デフォルトOFF | デフォルトON |
| 配布形式 | OCI互換 + .smolmachine アーティファクト |
OCIイメージ |
| 主な用途 | サンドボックス・使い捨て実行 | アプリ配布・本番運用 |
起動時間は smolvm 公式 README の比較表に準拠した値です。Linux 上で Docker を動かす場合は分離が OS レベルなので原理的にコンテナの方が高速ですが、macOSでは Docker Desktop が内部でLinux VMを起動するため、その分のオーバーヘッドが追加で発生します。一方の smolvm は macOS 上で常駐 VM を持たず Hypervisor.framework を直接叩くので、macOS 環境では実用上の起動コストはむしろ小さくなりやすいです。
つまり smolvm は Docker の代替ではなく、**「信頼できないコードを安全に動かすサンドボックス」**としての色が強いツールです。
では早速試してみます。
smolvmを使ってみる
環境
今回使用した環境は以下の通りです。
macOS 15.7.3 (Apple Silicon)
smolvm 0.9.0
対応プラットフォームは公式ドキュメントに記載があります。
| Host | Guest | Requirements |
|---|---|---|
| macOS Apple Silicon | arm64 Linux | macOS 11+ |
| macOS Intel | x86_64 Linux | macOS 11+ |
| Linux x86_64 | x86_64 Linux | KVM (/dev/kvm) |
| Linux aarch64 | aarch64 Linux | KVM (/dev/kvm) |
インストール
公式のインストールスクリプトを実行します。
$ curl -sSL https://smolmachines.com/install.sh | bash
バイナリは ~/.smolvm/ に配置され、~/.local/bin/smolvm にシンボリックリンクが張られます。
$ smolvm --version
smolvm 0.9.0
基本的な使い方
ephemeralな実行
smolvm machine run で一時的なマイクロVMを起動し、コマンド実行後に自動的に破棄できます。
$ smolvm machine run --net --image alpine -- sh -c "echo 'Hello from smolvm' && uname -a && cat /etc/os-release | head -3"
Starting ephemeral machine (vm-8e87fe6b)...
Pulling image alpine...Pulling image alpine... done.
Hello from smolvm
Linux container 6.12.87 #1 SMP Fri May 8 14:25:15 CEST 2026 aarch64 Linux
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.23.4
VM内ではLinuxカーネル 6.12.87 が動作しており、ホストのmacOSカーネルとは完全に分離されていることが確認できます。
なお、machine run --image <NAME> は ephemeral 用途のため、time を付けて連続実行すると分かるとおり、呼び出すたびに image の pull/展開フェーズが走ります。1GB 規模の python:3.12 で3回連続実行して計測した結果が以下です。
$ time smolvm machine run --net --image python:3.12 -- python3 --version
Starting ephemeral machine (vm-400d1506)...
Pulling image python:3.12...Pulling image python:3.12... [==> ] 14%Pulling image python:3.12... ... [====================] 100% — syncing...Pulling image python:3.12... done.
Python 3.12.13
smolvm machine run --net --image python:3.12 -- python3 --version 0.01s user 0.06s system 0% cpu 39.776 total
$ time smolvm machine run --net --image python:3.12 -- python3 --version
...
smolvm machine run --net --image python:3.12 -- python3 --version 0.01s user 0.03s system 0% cpu 38.281 total
$ time smolvm machine run --net --image python:3.12 -- python3 --version
...
smolvm machine run --net --image python:3.12 -- python3 --version 0.01s user 0.04s system 0% cpu 36.758 total
3回とも 37〜40 秒、プログレスバーも毎回 0% から表示されています。公式が謳う「200ms未満」というのは VM 自体のカーネル起動部分のみで、レジストリ問い合わせとイメージ展開の時間は別途上乗せされる点に注意が必要です。なお time の user/system がほぼ 0 秒なのは、pull や VM 起動の実処理がバックグラウンドの VM プロセス側で行われ、フォアグラウンドのCLIプロセス自体はほとんどCPUを使わないためです。machine run を素早く繰り返したいユースケースでは、後述の smolvm pack で .smolmachine 形式に固めるか、永続マシンを使って machine exec する方法が向いています。
インタラクティブシェル
-it オプションで対話的に操作できます。
$ smolvm machine run --net -it --image alpine -- /bin/sh
/ # apk add -q curl
/ # curl -sSL https://example.com | head -c 120
<!doctype html><html lang="en"><head><title>Example Domain</title><meta name="viewport" content="width=device-width
/ # exit
サンドボックス機能を試す
smolvmの真価はサンドボックスとしての使い勝手にあります。実際にネットワーク制御の挙動を確認してみます。
デフォルトではネットワークOFF
--net を指定しない場合、外部への通信は完全に遮断されます。VM内からは Docker Hub にも到達できないため、イメージのpull自体も成立しません。
$ smolvm machine run --image alpine -- sh -c "wget -q -O- -T 5 https://example.com || echo 'NETWORK BLOCKED'"
Starting ephemeral machine (vm-a422e1a3)...
Pulling image alpine...Pulling image alpine... done.
Error: agent operation failed: pull image: agent operation failed: pull image: crane manifest failed: 2026/06/05 06:29:20 retrying dial tcp: lookup index.docker.io on 8.8.8.8:53: dial udp 8.8.8.8:53: connect: network is unreachable
Error: fetching manifest docker.io/library/alpine:latest: Get "https://index.docker.io/v2/": dial tcp: lookup index.docker.io on 8.8.8.8:53: dial udp 8.8.8.8:53: connect: network is unreachable
Hint: networking is disabled. Add --net to enable image pulls:
smolvm machine run --net --image alpine ...
VM 内のプロセスから外部への通信が一切できないため、レジストリへの問い合わせもブロックされる、という挙動です。「信頼できないコードを動かす際に、本当に外部通信ができないことを保証したい」というユースケースで素直な設計になっています。
なお、「イメージは手元にあるが完全にオフラインで起動したい」という場合は、後述する smolvm pack で .smolmachine 形式に固めてしまえば、--net なしのまま起動できます。
--net で明示的にネットワーク許可
外部通信が必要な場合は --net を明示します。
$ smolvm machine run --net --image alpine -- sh -c "wget -q -O- https://example.com | head -3"
Starting ephemeral machine (vm-518ab753)...
Pulling image alpine...Pulling image alpine... done.
<!doctype html><html lang="en"><head><title>Example Domain</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{background:#eee;width:60vw;margin:15vh auto;font-family:system-ui,sans-serif}h1{font-size:1.5em}div{opacity:0.8}a:link,a:visited{color:#348}</style></head><body><div><h1>Example Domain</h1><p>This domain is for use in documentation examples without needing permission. Avoid use in operations.</p><p><a href="https://iana.org/domains/example">Learn more</a></p></div></body></html>
リソースと隔離
smolvmはデフォルトで 4 vCPU / 8 GiB RAM を割り当てますが、VM は virtio balloon を使って 実際に使っているぶんしかホストメモリを消費しません。つまり「大きめに確保しておくが、遊んでいる時は解放される」という挙動です。必要なら --cpus と --mem で明示的に上書きできます。
$ smolvm machine run --net --image alpine --cpus 2 --mem 512 -- free -m
Starting ephemeral machine (vm-584e457b)...
Pulling image alpine...Pulling image alpine... done.
total used free shared buff/cache available
Mem: 490 20 441 0 28 461
Swap: 0 0 0
--mem 512 を指定したとおり、約 490 MiB のメモリでVMが起動していることが確認できます。
smolvm pack でVM実行バイナリをパッケージ化
smolvmの特徴的な機能に pack があります。OCIイメージをVM実行バイナリと一緒にパッケージ化して、Dockerレジストリなしで配布・起動できます。
$ smolvm pack create --image python:3.12-alpine -o /tmp/python312
Starting agent VM...
Pulling image python:3.12-alpine...Pulling image python:3.12-alpine... done.
Image: python:3.12-alpine (4 layers, 356568173 bytes)
Collecting runtime libraries...
Collecting agent rootfs...
Creating storage template...
Merging 4 layers in VM (one-time cost)...
Exporting merged layer... 52 MB done
Assembling packed binary done
Packed: /tmp/python312 (stub: 22317KB, total: 50563KB)
Assets: /tmp/python312.smolmachine (28243KB compressed)
Signing binary with hypervisor entitlements...
Signed successfully
Run with: /tmp/python312
Note: Keep the .smolmachine file alongside the binary
Options: --help for usage
成果物は 実行用 stub バイナリ(/tmp/python312) と アセット側の .smolmachine ファイル(/tmp/python312.smolmachine) の2ファイル構成です。配布するときは両方をセットで持ち運ぶ必要があります。
実行は smolvm pack run --sidecar 経由で行います。
$ smolvm pack run --sidecar /tmp/python312.smolmachine -- python3 --version
Python 3.12.13
Python 3.12 の環境が完全に独立したマイクロVMとして手元に用意でき.smolmachine ファイル一式あれば、Docker Desktop も pyenv も venv もなしにPython環境を起動できます。
なお --net を付けずに smolvm pack run で起動すれば、.smolmachine 内のイメージで完全オフラインのVMが立ち上がります。先ほどの machine run --image と違ってレジストリ問い合わせが発生しないため、ネットワーク遮断下でもそのまま動作します。
また毎回の pull が発生しない分、起動時間も大幅に短くなります。先ほど machine run --image python:3.12 で毎回37〜40秒かかっていたのと同じイメージを pack run で連続実行すると、初回は sidecar 展開に時間がかかりますが、2回目以降は約3秒で安定します。
$ time smolvm pack run --sidecar /tmp/python312-fat.smolmachine -- python3 --version
Python 3.12.13
smolvm pack run --sidecar /tmp/python312-fat.smolmachine -- python3 --version 2.05s user 9.15s system 13% cpu 1:26.02 total
$ time smolvm pack run --sidecar /tmp/python312-fat.smolmachine -- python3 --version
Python 3.12.13
smolvm pack run --sidecar /tmp/python312-fat.smolmachine -- python3 --version 0.34s user 0.23s system 20% cpu 2.732 total
$ time smolvm pack run --sidecar /tmp/python312-fat.smolmachine -- python3 --version
Python 3.12.13
smolvm pack run --sidecar /tmp/python312-fat.smolmachine -- python3 --version 0.36s user 0.25s system 20% cpu 3.010 total
初回の展開コストはイメージサイズに依存しますが、machine run の約37秒 と pack run 2回目以降の約3秒で10倍以上の差になります。同じイメージで繰り返し使うなら pack 化した方が圧倒的に速いです。
pack済みアーティファクトから永続マシンを作る(machine create --from)
pack run は実行のたびにイメージから新鮮な状態で起動する(=書き込みは破棄される)ephemeral な使い方ですが、.smolmachine を永続マシンのベースとして使うこともできます。machine create --from を使うと、レジストリへの pull なしに pack 済みアーティファクトから永続マシンを作成でき、machine exec での変更が stop/start を通して残ります。
$ smolvm machine create fastvm --from /tmp/python312.smolmachine --net
Extracting .smolmachine assets...
Created machine: fastvm
CPUs: 4, Memory: 8192 MiB
$ smolvm machine start --name fastvm
Machine 'fastvm' running (PID: 66492)
$ smolvm machine exec --name fastvm -- pip install -q requests
$ smolvm machine exec --name fastvm -- python3 -c "import requests; print('requests OK', requests.__version__)"
requests OK 2.34.2
stop してから再度 start しても、インストールした requests はそのまま残ります。
$ smolvm machine stop --name fastvm
$ smolvm machine start --name fastvm
$ smolvm machine exec --name fastvm -- python3 -c "import requests; print('再起動後も requests OK', requests.__version__)"
再起動後も requests OK 2.34.2
pull が走らないぶん start は約1秒と速く、配布した .smolmachine をそのまま「育てて使い続ける開発環境」に転用できるのが利点です。なお start 直後すぐに exec すると、ゲスト側のコンテナ起動が間に合わず the container ... is not running と返ることがあります。その場合は数秒待ってから exec し直してください。
LiteLLMをsmolvmで動かしてみる
サンドボックス機能とパック機能を組み合わせて、LLM を呼び出す Python スクリプトを smolvm で実行してみます。以前の記事で紹介した LiteLLM を使って OpenAI の API を叩く例です。
まずはホスト側に実行するスクリプトを用意します。
import os
from litellm import completion
response = completion(
model="openai/gpt-4o-mini",
messages=[
{
"role": "user",
"content": "smolvmマイクロVMで動いていることを確認するため、短い挨拶を日本語で1文だけ返して",
},
],
)
print("LLM回答:", response.choices[0].message.content)
print("model:", response.model)
print("usage:", response.usage.total_tokens, "tokens")
先ほど作成した python312.smolmachine を使って、このスクリプトをマイクロVM内で実行します。
$ smolvm pack run --sidecar /tmp/python312.smolmachine --net \
--env OPENAI_API_KEY="$OPENAI_API_KEY" \
--volume /tmp/smolvm-litellm-demo:/app \
--workdir /app \
-- sh -c "pip install -q litellm && python3 app.py"
LLM回答: こんにちは、SmolVMでの動作確認ができました!
model: gpt-4o-mini-2024-07-18
usage: 53 tokens
ポイントは以下の3点です。
--envで APIキーを VM に渡している。ホストの環境変数は VM に継承されないため、明示的に渡す必要があります--volumeでホストのスクリプトディレクトリを VM 内/appにマウント。コードを VM イメージに焼き込まなくても実行できる--netを付けないとpip installも OpenAI API 呼び出しもできない。逆に言えばこのフラグなしなら 完全にオフラインの実行環境 を作れる
シークレットストアでAPIキーを安全に渡す
上記の --env OPENAI_API_KEY="$OPENAI_API_KEY" 方式は手軽ですが、APIキーがコマンドラインに現れるためシェル履歴や ps に残るという難点があります。その場合、ホスト側シークレットストアを使うと、暗号化したキーをコマンドラインに出さずに VM のプロセス環境へ注入できます。
まずキーをストアに登録します。--from-env を使うと、値そのものではなく「ホストのこの環境変数を起動時に参照する」という参照情報だけが永続化されます。
$ smolvm secret set OPENAI_API_KEY --from-env OPENAI_API_KEY
Stored secret 'OPENAI_API_KEY'.
$ smolvm secret list
NAME SOURCE
OPENAI_API_KEY env
smolvm secret list は名前とソース(env / file / 暗号化ストア)だけを表示し、値は決して出力しません。値を直接入力して暗号化保存したい場合は smolvm secret set OPENAI_API_KEY(プロンプトでエコーオフ入力)を使います。
登録したシークレットは Smolfile の [secrets] セクションから参照します。
image = "python:3.12-alpine"
net = true
workdir = "/app"
[dev]
volumes = ["/tmp/smolvm-litellm-demo:/app"]
[secrets]
OPENAI_API_KEY = { from_store = "OPENAI_API_KEY" }
-s で Smolfile を渡して実行すると、--env でキーを書かなくても VM 内にだけ OPENAI_API_KEY が注入されます。
$ smolvm machine run -s Smolfile -- sh -c "pip install -q litellm && python3 app.py"
Starting ephemeral machine (vm-f572cf1a)...
Pulling image python:3.12-alpine...Pulling image python:3.12-alpine... done.
LLM回答: こんにちは!SmolVMマイクロVMが正常に動作しています。
model: gpt-4o-mini-2024-07-18
usage: 55 tokens
[secrets] では from_store(暗号化ストア)のほかに from_env(ホスト環境変数)、from_file(ホストのファイル)も指定でき、いずれも VM 内のプロセス環境にだけ注入されてコマンドラインやディスク(暗号化ストア外)には残りません。LLM生成コードのような信頼できないワークロードに認証情報を渡すときに向いた仕組みです。
LLMエージェントに生成コードを実行させるユースケース
このパターンの応用として、LLMに生成させたコードをsmolvm内で実行し、ホストを汚さずに結果だけ回収する、という使い方が考えられます。ポイントは以下です。
- ホストFSは見えない: VM から見えるのは明示的に
--volumeしたディレクトリのみ - 認証情報が漏れない:
~/.aws/credentialsや~/.sshなどは明示しない限り VM から一切見えない - ネットワーク制御:
--netなしなら外部への情報流出もブロックできる - 使い捨て:
machine runは実行後にVMごと破棄されるので、マルウェアが仕込まれても次回起動時には消えている
Dockerで同じことをやろうとすると、カーネル脆弱性・マウント設定ミス・seccompプロファイルなど気にすべきポイントが多いですが、smolvmなら「デフォルトで安全」側に倒れているのが楽なところです。
永続マシンとSmolfile
永続マシン
開発用途では、VMを立ち上げっぱなしにしたい場合もあります。machine create / start / exec / stop で永続マシンを扱えます。
--image を省略した場合は smolvm が用意した agent rootfs(Alpine Linux v3.19)の素のVMが起動します。apk でパッケージを足していくスタイルです。任意のベースイメージを使いたい場合は --image ubuntu や --image python:3.12-alpine のように指定します(「Image-based persistent」モード)。
$ smolvm machine create --net myvm
Created machine: myvm
CPUs: 4, Memory: 8192 MiB
Use 'smolvm machine start --name myvm' to start the machine
Then use 'smolvm machine exec --name myvm -- <command>' to run commands
$ smolvm machine start --name myvm
Starting machine 'myvm'...
Machine 'myvm' running (PID: 12377)
$ smolvm machine exec --name myvm -- apk add -q python3
$ smolvm machine exec --name myvm -- python3 --version
Python 3.11.14
$ smolvm machine stop --name myvm
Stopping machine 'myvm'...
Stopped machine: myvm
exec でインストールしたパッケージは停止・再起動の影響を受けずに残ります。また、exec は起動済みのVMへの接続のみで完結するため time で測ると 0.1 秒前後で返ってきます。machine run --image の約37秒と比べると劇的に速いので、開発中に同じVMで繰り返しコマンドを叩くユースケースに向いています。
Smolfileで宣言的に定義
TOMLファイルでVM構成を宣言できます。チームで同じ開発環境を共有したい時に便利です。
image = "python:3.12-alpine"
net = true
[dev]
init = ["pip install -r requirements.txt"]
volumes = ["./src:/app"]
[auth]
ssh_agent = true
$ smolvm machine create myvm -s Smolfile
$ smolvm machine start --name myvm
ssh_agent = true を指定すればホストのSSHエージェントをVMにフォワードできます。秘密鍵自体は VM に渡らず、認証だけが委譲される仕組みです。
このほか Smolfile には、egress を絞る [network](allow_hosts / allow_cidrs)、machine monitor 用の [health]、pack create 用の [artifact]、そして前述の [secrets] といったセクションが用意されています。smolvm --help の "Smolfile Reference" に全項目がまとまっているので、用途に応じて参照してください。
実行方式の使い分け
ここまでで4種類の実行方式(machine run --image / pack + pack run / 永続マシン / machine create --from)が出てきたので、リソース消費とユースケースで整理しておきます。
| 方式 | 起動時間 | ディスク | メモリ | 向いている用途 |
|---|---|---|---|---|
machine run --image |
毎回 約37〜40秒(pull走る) | ホスト側キャッシュのみ | 動作中のみ | 完全な使い捨て、毎回新鮮な環境がほしい時 |
pack + pack run |
2回目以降 約3秒 | イメージ丸ごと(python:3.12 で約400MB) | 動作中のみ | 同じ環境を繰り返し使う、別マシンに配布したい |
| 永続マシン | start 後の exec は 約0.1秒 |
最大10〜20GiB割当(実消費は書き込んだ分だけ) | start 中ずっとVMサイズ分を占有 |
同じVMを育てて使い続ける、対話的に作業する |
machine create --from |
start は約1秒(pullなし) |
.smolmachine 展開分 + 書き込み分 |
start 中ずっとVMサイズ分を占有 |
pack済みアーティファクトを永続マシンとして育てる |
永続マシンのディスクは「最大10〜20GiB を確保するが、実際にホストの空き容量を食うのはVM内で書き込んだ分だけ」というスパース割当です。作っただけで何百GiB持っていかれるわけではなく、apt install などで書き込んだ分が積み上がっていく挙動になります。
使い分けとしては「pack はディスク前払い、永続マシンはメモリ常駐」と覚えると選びやすいです。配布したいなら pack、対話的にガリガリ作業するなら永続マシン、本当に使い捨てでよければ machine run --image、という選択になります。
その他の便利な機能
ここまでに紹介していない、smolvmの便利な機能をいくつか紹介します。
machine update で停止中VMの設定変更
machine create 時に指定したボリュームやポート、リソース割り当ては、machine update で後から変更できます。stop 状態のマシンに対して実行でき、start し直すと新しい設定が反映されます。
$ smolvm machine update myvm -v /tmp/smolvm-update-test:/app -p 8080:8080
Updated machine 'myvm':
added volume: /private/tmp/smolvm-update-test:/app
added port: 8080:8080
Start with: smolvm machine start --name myvm
machine shell ショートカット
machine exec --name myvm -- /bin/sh の代わりに machine shell --name myvm でインタラクティブシェルに入れます。マシンが停止していれば自動で起動してくれるため、開発中の「ちょっとVMの中を覗きたい」用途にちょうどよい挙動です。
$ smolvm machine shell --name myvm
TCPポートマッピング(-p フラグ)
virtio-net 経由のTCPポートマッピングがサポートされており、-p HOST:GUEST でVM内のサービスをホストに公開できます。VM内で開発用Webサーバを立てて、ホストのブラウザからアクセスするといった使い方ができます。
$ smolvm machine run --net -p 8000:8000 --image python:3.12-alpine -- python3 -m http.server 8000
Starting ephemeral machine (vm-ffccb908)...
Pulling image python:3.12-alpine...Pulling image python:3.12-alpine... done.
別ターミナルからホスト側へリクエストを投げると、VM内の http.server に届いていることが確認できます。
$ curl -s http://127.0.0.1:8000/ | head -5
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Directory listing for /</title>
GPUサポート(virtio-gpu)
--gpu フラグを付けると、macOS Apple Silicon と Linux 双方で virtio-gpu/Venus を介したGPUアクセラレーションが有効になります。共有メモリ量は --gpu-vram <MiB>(デフォルト4096)で調整できます。リポジトリの examples/ には local-llm(ローカルLLM)や gpu-chrome・doom-web といったGPU活用サンプルが含まれており、ローカルLLMやブラウザワークロードをサンドボックスで動かすユースケースにも対応できるようになっています。
プライベートレジストリ認証
smolvm config registries edit で認証情報を設定するか、既存の Docker 認証をそのまま使いたい場合は smolvm machine run --docker-config ... でホストの ~/.docker/ をVMにマウントして認証できます。またmachine create 時にレジストリの identity token を渡すことで認証情報がエージェントのリクエストログにマスキングされるようになっています。
現時点で遭遇した注意点
最後に、動作確認中に気づいたクセのある挙動を共有しておきます。
--allow-host 指定時の初回pullに注意
--allow-host api.openai.com のように特定ホストだけ egress を許可する機能があります。これを使うとVM内からそのホスト以外への通信がブロックされるため、信頼できないコードを動かす際の egress 制御として強力です。
ただし、--allow-host で許可していないホストはレジストリ(index.docker.io など)も例外なくブロック対象になるため、イメージのpullが失敗します。
$ smolvm machine run --allow-host example.com --image alpine -- sh -c "wget -q -O- https://example.com"
Starting ephemeral machine (vm-f698e1a9)...
Pulling image alpine...Pulling image alpine... done.
Error: agent operation failed: pull image: crane manifest failed: Error: fetching manifest docker.io/library/alpine:latest: Get "https://index.docker.io/v2/": dial tcp 54.235.153.191:443: connect: permission denied
これは「指定ホストしか許可しない」というセキュリティ設計どおりの挙動ですが、初回pullがブロックされる点はハマりやすいので注意が必要です。--allow-host を使う場合は、以下のどちらかの運用にする必要があります。
- 事前に
smolvm pack createで.smolmachineにパッケージ化してから--allow-host付きでrunする - もしくは
--allow-host api.openai.com --allow-host index.docker.io --allow-host ...のようにレジストリ含めて明示的に許可する
まとめ
smolvmを使ってマイクロVMの起動からLiteLLMの実行までを試してみました。VM レベルの隔離を持ちつつコンテナに迫る起動速度で扱える点や、--net デフォルトOFFなど「デフォルトで安全」寄りに振った設計が特徴的でした。Apple Silicon の Mac で Docker Desktop を常駐させるのが重いと感じている方や、LLMエージェントが生成したコードを安全に実行する仕組みを探している方は、ぜひ試してみてください。
最後まで読んでいただきありがとうございました。







