How cube-agent Manages Container Lifecycle Inside MicroVMs as PID 1
cube-agent acts as the init process (PID 1) inside CubeSandbox MicroVMs, mounting filesystems, hosting a ttrpc server over vsock, and translating host-side container lifecycle commands into containerd-shim operations.
CubeSandbox uses cube-agent as the guest-side init system to bridge the gap between host orchestration and in-VM container execution. When a MicroVM boots, the statically-linked cube-agent binary—packaged at /sbin/init during the VM image build—runs as PID 1 to initialize the system and establish a communication channel with the host. This enables full container lifecycle management via ttrpc over vsock, allowing the host-side CubeShim to create, start, and stop containers inside the MicroVM.
Initialization as PID 1 in MicroVMs
When the MicroVM boots, cube-agent determines whether it should run in init mode by checking its process ID. In agent/src/main.rs, lines 58–60, the code verifies if it is PID 1:
let init_mode = unistd::getpid() == Pid::from_raw(1); // agent/src/main.rs:L58-L60
If init_mode is true, cube-agent performs essential system initialization before transitioning into its agent role. This includes mounting the necessary filesystems (proc, sys, dev), setting the hostname, and calling init_agent_as_init to assume the responsibilities of the system init process. The binary is injected into the guest image as /sbin/init by the build script at deploy/one-click/build-vm-assets.sh (line 117), ensuring it launches automatically as the first process.
ttrpc Communication Over Vsock
After initialization, cube-agent establishes the communication channel that allows the host to manage containers inside the VM. It opens a vsock listener on a port defined by the log_vport configuration parameter and starts a ttrpc server. In agent/src/main.rs around lines 96–98, the start_sandbox function invokes rpc::start to begin listening on the VM's vsock interface:
// Start ttrpc server on vsock
rpc::start(vsock_listener).await?;
The ttrpc protocol definitions reside in libs/protocols/protos/, providing the structured communication format between the host and the guest agent. This vsock-based communication bypasses traditional network stacks, offering secure, hypervisor-mediated channels between the host and MicroVM.
Container Lifecycle Forwarding from CubeShim
The host-side CubeShim component acts as the client to cube-agent's ttrpc server. CubeShim implements the standard container shim API—including CreateContainer, StartContainer, Exec, Kill, and Delete—and forwards each request over the vsock connection to the in-VM cube-agent.
According to the CubeShim documentation (CubeShim/README.md, lines 24–48), the forwarding logic operates as follows:
- CubeShim connects to the vsock address
VMADDR_CID_ANYon the configured port - Each container lifecycle operation is serialized as a ttrpc request
- The request travels over vsock into the MicroVM where
cube-agentreceives it
This architecture decouples the orchestration logic on the host from the container runtime inside the MicroVM, allowing CubeSandbox to maintain a minimal guest footprint while supporting standard container operations.
Creating Containers Inside the VM
When cube-agent receives a CreateContainer request via ttrpc, it translates the high-level shim API into actual container operations using the containerd-shim-cube-rs runtime. The handler in agent/src/rpc/mod.rs processes the request and spawns the container's init process, setting up namespaces, cgroups, and mounting the rootfs.
Here is how the host-side CubeShim (Go) creates a container inside the MicroVM:
// Host-side shim (Go) – create a container in the VM
func createContainer(ctx context.Context, vmID string, spec *rspec.Spec) error {
// 1. Connect to the VM's vsock (port is stored in the VM metadata)
conn, err := vsock.Dial(vsock.CidAny, uint32(vmPort))
if err != nil { return err }
// 2. Build a ttrpc client for the cube-agent service
client := ttrpc.NewClient(conn)
defer client.Close()
// 3. Call the CreateContainer RPC (defined in libs/protocols/protos/)
req := &api.CreateContainerRequest{
Id: containerID,
Spec: spec,
}
resp, err := api.NewCubeAgentServiceClient(client).CreateContainer(ctx, req)
if err != nil { return err }
// 4. The agent now runs containerd-shim-cube-rs to launch the container inside the VM
fmt.Printf("Container created, PID %d inside VM\n", resp.Pid)
return nil
}
Inside the VM, cube-agent (Rust) handles the RPC:
// agent/src/rpc/mod.rs – handles CreateContainer RPC
async fn create_container(
&self,
req: CreateContainerRequest,
) -> Result<CreateContainerResponse, RpcError> {
// Translate the request into a containerd-shim call
let shim = containerd_shim_cube_rs::Shim::new();
let pid = shim.create_container(req.spec).await?;
Ok(CreateContainerResponse { pid })
}
This end-to-end flow demonstrates how cube-agent bridges the gap between the host's orchestration commands and the in-VM container runtime.
Graceful VM Shutdown
When containers finish execution or the control plane requests VM termination, cube-agent ensures a clean shutdown. In agent/src/main.rs (lines 1003–1006), the agent calls the Linux reboot system call to power off the MicroVM:
libc::reboot(LINUX_REBOOT_CMD_POWER_OFF);
This ensures that the MicroVM shuts down cleanly after all container processes have terminated, preventing resource leaks and ensuring proper cleanup of the guest environment.
Summary
- PID 1 Detection:
cube-agentchecksunistd::getpid()inagent/src/main.rsto determine if it should run as the init process, mounting filesystems and initializing the system when true. - Vsock Communication: The agent starts a ttrpc server on a vsock listener, enabling secure host-to-VM communication without traditional networking.
- Lifecycle Proxy: CubeShim forwards container operations (Create, Start, Kill, Delete) over ttrpc/vsock to
cube-agent, which translates them into containerd-shim calls. - Runtime Integration: Inside the VM,
cube-agentusescontainerd-shim-cube-rsto create containers, configure namespaces, and manage cgroups. - Clean Shutdown: When work completes,
cube-agentcallsreboot(LINUX_REBOOT_CMD_POWER_OFF)to power off the MicroVM gracefully.
Frequently Asked Questions
What happens if cube-agent is not running as PID 1?
If cube-agent detects it is not PID 1 via the getpid() check in agent/src/main.rs, it skips the init-specific setup routines (filesystem mounting, hostname configuration) and runs in a reduced agent mode. However, in standard CubeSandbox deployments, the build script (build-vm-assets.sh) ensures the binary is placed at /sbin/init, guaranteeing it launches as PID 1.
How does cube-agent communicate with the host?
cube-agent communicates with the host via ttrpc over vsock. It opens a vsock listener on a port configured by log_vport and starts a ttrpc server (rpc::start in agent/src/main.rs). The host-side CubeShim connects to this vsock endpoint using VMADDR_CID_ANY, allowing bidirectional RPC communication for container lifecycle management.
What container runtime does cube-agent use?
cube-agent uses the containerd-shim-cube-rs runtime to actually create and manage containers. When cube-agent receives a ttrpc request like CreateContainer, it translates the request into calls to this internal shim runtime, which handles the low-level details of process spawning, namespace configuration, and cgroup setup.
How does cube-agent handle MicroVM shutdown?
When containers finish or the host requests termination, cube-agent initiates a clean shutdown by calling libc::reboot(LINUX_REBOOT_CMD_POWER_OFF) (see agent/src/main.rs lines 1003–1006). This system call signals the kernel to power off the VM, ensuring all resources are properly released and the MicroVM exits cleanly.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →