https://github.com/pixelkarma/smolvm
Run containerized alpine VMs who each have their own local web-based agent. Because if the world needs anything, it's more artificial intelligence...
https://github.com/pixelkarma/smolvm
Last synced: about 1 month ago
JSON representation
Run containerized alpine VMs who each have their own local web-based agent. Because if the world needs anything, it's more artificial intelligence...
- Host: GitHub
- URL: https://github.com/pixelkarma/smolvm
- Owner: pixelkarma
- License: unlicense
- Created: 2026-05-02T16:48:46.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-11T01:37:18.000Z (2 months ago)
- Last Synced: 2026-05-11T02:16:56.411Z (2 months ago)
- Language: Go
- Homepage:
- Size: 303 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# smolvm
`smolvm` is a lightweight admin for running multiple isolated coding-agent instances as Alpine QEMU VMs. It is for development only: the guest runs as `root` on purpose, and the whole system is designed to be disposable.
Each instance gets:
- its own QEMU VM
- its own writable disk image cloned from a golden Alpine image
- a private agent UI behind the admin
- a browser terminal into the VM
- a public web port for the app running inside the guest
## Requirements
`build.sh` does not install host packages for you anymore. Have these available first:
```sh
curl
expect
go
qemu-img
qemu-system-x86_64
ssh-keygen
```
On Linux, KVM is optional but preferred. Without it, QEMU falls back to emulation.
## Install
```sh
git clone https://github.com/pixelkarma/smolvm
cd smolvm
./build.sh
```
`build.sh`:
- builds `smolvm-admin` for the host
- cross-builds `smolagent` for Linux `amd64`
- downloads Alpine installer assets if they are missing
- creates or reuses `~/.smolvm/assets/alpine-golden.qcow2`
- writes `~/.smolvm/smolvm.config.json`
If a golden image already exists, the script asks whether to reuse it or rebuild it.
During `./build.sh`, the script prompts for the admin password. If you just press Enter, it uses:
```text
smolvm
```
## Run the admin
After the build finishes, start the admin manually:
```sh
~/.smolvm/bin/smolvm-admin --config ~/.smolvm/smolvm.config.json
```
Default URL:
```text
http://SERVER_IP:8090/login
```
Default password if you accept the build default:
```text
smolvm
```
## Config
Host config lives at:
```sh
~/.smolvm/smolvm.config.json
```
Typical host config:
```json
{
"listen_addr": ":8090",
"data_dir": "/home/user/.smolvm/data",
"default_openai_api_key": "sk-...",
"admin_password": "smolvm",
"qemu_binary_path": "/usr/bin/qemu-system-x86_64",
"template_image_path": "/home/user/.smolvm/assets/alpine-golden.qcow2",
"guest_ssh_key_path": "/home/user/.smolvm/keys/guest-admin"
}
```
`default_openai_api_key` is still written to the host config, but the guest now refreshes its runtime config from the admin on every launch instead of relying on a baked guest config file.
## How it works
- The admin runs on port `8090`.
- Each VM gets a copied QCOW2 disk from the golden image.
- The admin launches QEMU daemonized and only tracks whether the VM process is present.
- App links are generated from the current admin request host; there is no separate public-host setting anymore.
- The private agent UI is proxied through the admin after login.
- Public app ports start at `8100` and increment.
- Guest SSH is forwarded to a host port starting at `10001`.
Current networking model:
- guest `9000` = private `smolagent` UI
- guest `80` = public web app port
- host `127.0.0.1:` -> guest `9000`
- host `:` -> guest `80`
- host `:` -> guest `22`
The guest learns its instance id from QEMU SMBIOS:
```text
/sys/class/dmi/id/product_serial
```
`smolagent` refreshes its config from the admin on every launch by requesting:
```text
http://10.0.2.2:8090/internal/instance-config?instance_id=
```
That is how the guest picks up:
- the effective OpenAI API key
- the global prompt
- the instance prompt
## UI behavior
Each instance row in the admin includes:
- `Agent`
- `Terminal`
- `Web App`
- a `...` menu for `Start`, `Stop`, and `Delete`
The `Active` column is browser-side only:
- `checking` while the browser probes the proxied agent URL
- `active` if the probe succeeds
- `inactive` if it times out or fails
The browser terminal uses local vendored `xterm.js` assets and connects through an admin websocket. It SSHes into the guest over the forwarded guest SSH port.
## Capabilities
- create, start, stop, and delete VM instances
- set per-instance RAM, CPU, disk, web port, API key override, and prompt
- private agent UI behind admin auth
- browser terminal into the guest
- direct public web port exposure for the guest app
- per-instance persistent disk images
## Limitations
- Admin auth is intentionally simple: one shared password
- VM state is tracked by process inspection, not a full orchestrator
- The admin does not validate guest readiness before returning from `Start`
- The system assumes trusted local administration and disposable guests
- The guest app is expected to listen on port `80`
## Important paths
- config: `~/.smolvm/smolvm.config.json`
- binaries: `~/.smolvm/bin`
- guest image assets: `~/.smolvm/assets`
- VM disks and database: `~/.smolvm/data`
- vendored terminal assets: `static/xterm/`