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
12 changes: 12 additions & 0 deletions cmd/aether-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/3xDevOps/Aether/internal/guest/helper"
"github.com/3xDevOps/Aether/internal/harness"
"github.com/3xDevOps/Aether/internal/runtime"
"github.com/3xDevOps/Aether/internal/runtime/vm"
"github.com/3xDevOps/Aether/internal/scheduler"
"github.com/3xDevOps/Aether/internal/server"
"github.com/3xDevOps/Aether/internal/serversetup"
Expand All @@ -50,6 +51,9 @@ func main() {
if len(os.Args) > 1 && os.Args[1] == "guest" {
os.Exit(helper.Main())
}
if len(os.Args) > 1 && os.Args[1] == vm.MonitorCommand {
exitOn(vm.Monitor(os.Args[2:]))
}
if filepath.Base(os.Args[0]) == "aether-internal" {
os.Exit(coordcli.Main(os.Args[1:]))
}
Expand Down Expand Up @@ -251,6 +255,8 @@ type serveOptions struct {
runMemory *runCountValue
runPids *runCountValue
containerRuntime *string
sessionRuntime *string
vmKernel *string
}

// serveFlags declares the server options on fs. It is the single definition
Expand Down Expand Up @@ -312,6 +318,10 @@ func serveFlags(fs *flag.FlagSet) *serveOptions {
o.containerRuntime = fs.String("container-runtime", server.DefaultContainerRuntime,
"OCI runtime for run and member environment containers: auto uses sysbox-runc when Docker has it registered "+
"(Docker inside sessions without --privileged), otherwise the daemon default; any other value is used as given, e.g. runc to opt out")
o.sessionRuntime = fs.String("session-runtime", server.SessionRuntimeDocker,
"where sessions run: docker, or vm for one Cloud Hypervisor VM per session (needs /dev/kvm, cloud-hypervisor and virtiofsd; "+
"every other container stays on Docker)")
o.vmKernel = fs.String("vm-kernel", server.DefaultVMKernel, "the guest kernel session VMs boot with -session-runtime vm")
return o
}

Expand Down Expand Up @@ -393,6 +403,8 @@ func serve(args []string) error {
RunPidsLimit: int64(*o.runPids),

ContainerRuntime: *o.containerRuntime,
SessionRuntime: *o.sessionRuntime,
VMKernel: *o.vmKernel,
SysboxInstaller: true,
})
if err != nil {
Expand Down
40 changes: 40 additions & 0 deletions docs/environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,46 @@ pressure](failure-handling.md#recover-from-disk-pressure) and [Recover from
memory pressure](failure-handling.md#recover-from-memory-pressure) have the
thresholds and what to do.

## Sessions in virtual machines

`--session-runtime` (config key `session-runtime`) defaults to `docker`. With
`vm`, each new session runs in its own [Cloud
Hypervisor](https://github.com/cloud-hypervisor/cloud-hypervisor) virtual
machine instead of a container. Environment terminals, the assistant, the
agent updater, the development browser and **Save environment** stay on
Docker.
This is work in progress; [the plan](plans/2026-11-13-microvm-sessions.md)
lists what is still to come.

```sh
sudo aether-server config set session-runtime vm
sudo systemctl restart aether-server
```

The host needs `/dev/kvm`, `cloud-hypervisor`, `/usr/libexec/virtiofsd`,
`qemu-img`, `mkfs.ext4`, `iptables` and systemd, besides Docker, and the
release's guest kernel `aether-vmlinux-x86_64` at `/usr/local/lib/aether/vmlinux`
(`--vm-kernel`, config key `vm-kernel`, names another path).

What changes:

- Each VM runs in the systemd unit `aether-vm-<run id>.service` in
`aether.slice`, so it keeps running through a server restart or update.
Its disks and logs are in `<data dir>/vm/<run id>/`: `monitor.log` holds the
VM monitor's output and `console.log` the guest kernel's.
- The VM gets the CPU limit rounded up to whole vCPUs, held to the exact
limit by the unit's `CPUQuota`, and the memory limit as its memory. The
process limit applies inside the guest.
- The checkout, the home and the other mounts reach the VM over one virtio-fs
share; read-only mounts stay read-only. Each VM has its own address in
`10.200.0.0/16` and reaches the internet through the host.
- Starting a stopped session builds a fresh root disk from its image and runs
the setup script again: packages installed outside the mounts are gone.
Docker images inside the session stay on its data disk.
- Not yet supported, refused at launch with an error naming the work item:
the Isolated tier and egress control, and the development browser beside a
session.

## After a session closes

Reporting success or failure leaves an interactive session open for follow-up, and
Expand Down
Loading
Loading