microvms-core 1.3.0

The MicroVMs control-plane and in-VM client: every trap closure, the cost engine, the sizing model
Documentation

microvms-core

Run agents, shell commands, builds, and tests inside AWS Lambda MicroVMs from Rust. Your application controls a remote sandbox through Sandbox and Session: launch a VM, copy a workspace, execute tools, collect results, and terminate it. The agents module also provisions Claude Code or Codex with Amazon Bedrock access.

Run your first command

You need Rust, AWS credentials with MicroVM access, a VM execution role, and an image containing agentd. Complete the one-time AWS setup if this is your first use. AWS usage is billed; a first image build takes several minutes. Once an image exists, the example below is the full launch-to-cleanup path.

Use the CLI to prepare a reusable image, then copy data.imageIdentifier from its JSON output:

microvm build --name agent-tools --region us-east-1 --json
export MICROVM_IMAGE='arn:aws:lambda:us-east-1:123456789012:microvm-image:REPLACE_ME'
export MICROVM_EXECUTION_ROLE_ARN='arn:aws:iam::123456789012:role/REPLACE_ME'
cargo new sandbox-demo
cd sandbox-demo

The image and this example use us-east-1. Set MICROVM_IMAGE to the actual ARN; the SDK can also resolve an image name in that region. Set MICROVM_EXECUTION_ROLE_ARN to the actual execution role ARN from setup. AWS credentials come from the standard credential chain, including AWS_PROFILE and environment variables.

Replace the empty [dependencies] section in Cargo.toml with:

[dependencies]
microvms-core = "1.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

Replace src/main.rs with:

use std::time::Duration;
use microvms_core::prelude::*;
use microvms_core::{Region, protocol::exec::StartRequest};
use microvms_core::sandbox::{RunRequest, Sandbox, TeardownOpts};
use microvms_core::session::mint_exec_id;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let image = std::env::var("MICROVM_IMAGE")?;
    let role = std::env::var("MICROVM_EXECUTION_ROLE_ARN")?;
    let mut sandbox = Sandbox::new(Region::US_EAST_1).await?;
    let work = async {
        let mut request = RunRequest::new().with_image(&image);
        request.execution_role_arn = Some(role);
        // Returns once the VM is RUNNING and its daemon answers.
        let session = sandbox.run(request).await?;
        let command = vec!["printf 'hello from a sandbox\\n'".into()];
        let hello = StartRequest::new(mint_exec_id(), command)
            .with_shell(true)
            .with_timeout_sec(Some(30.0))
            .with_reap_group_on_exit(true);
        session.run_sync(hello, Duration::from_secs(35)).await
    }.await;

    // Runs even when launch, readiness, or execution returns an error.
    let cleanup = sandbox.terminate(TeardownOpts::default()).await;
    for failure in &cleanup.failures {
        eprintln!("cleanup: {failure}");
    }
    for resource in &cleanup.undeleted {
        eprintln!("not deleted: {resource}");
    }
    let result = work?;
    print!("{}", result.stdout());
    eprint!("{}", result.stderr());
    println!("exit: {:?}", result.exit_code());
    if !result.succeeded() {
        return Err("guest command failed".into());
    }
    if !cleanup.failures.is_empty() || !cleanup.undeleted.is_empty() {
        return Err("VM cleanup failed; see errors above".into());
    }
    Ok(())
}
cargo run

Expected output includes hello from a sandbox and exit: Some(0). use microvms_core::prelude::*; brings the constructors that wire in AWS, such as Sandbox::new, into scope. A request with no with_cwd uses the image's working directory. Set a different directory only after creating it or uploading files there; a generic image need not contain /workspace. Cleanup requests VM termination and retains the image for reuse. Dropping a Sandbox does not terminate it; always call terminate and inspect its report. Use TeardownOpts::default().waiting_for_terminated() when you must wait for AWS to report termination complete.

Give an agent its own sandbox

Use a separate VM for each task or workspace, upload only the files it needs, run tools through the session, and download the results before cleanup. Session::upload_file, upload_tar, download_file, and download_tar handle transfers; Session::run returns an ExecHandle for streaming and cancellation.

For built-in coding agents, start with running Claude Code or Codex. Rust's agents::AgentVm composes image preparation, launch, Bedrock credential installation, and prompts. Agent prompts run as uid/gid 1000. The generic command example above runs as the image's AGENTD_DEFAULT_USER when the image sets one, and as the daemon's user, normally root, when it doesn't.

The VM separates workload execution from your host. Processes within one guest share that VM; uid demotion is not a separate security boundary. Guest workloads can access the VM execution role through metadata, so scope that role accordingly. Agent helpers request internet access. Omitting egress does not block the default network, and proxy-deny variables are advisory. Enforced internet isolation needs a VPC connector and verified VPC routing without internet access. See trust boundaries and networking.

Next steps

Apache-2.0