← AI Agents Frameworks & Ecosystem
🤖 AI Agents

AWS Bedrock AgentCore: MicroVM Deployment

Source: mortalapps.com
TL;DR
  • Note: AWS Bedrock AgentCore provides managed infrastructure (containers, scaling, tool execution) for deploying AI agents. The Bedrock Agent API (`agentId`, `invoke_agent()`, `agentAliasId`) is used when calling Bedrock-managed agents. This article demonstrates the infrastructure orchestration layer. For Bedrock Agent invocation, use `bedrock_agent_runtime.invoke_agent(agentId=..., agentAliasId=..., sessionId=..., inputText=...)`.
  • AWS Bedrock AgentCore is a runtime framework for executing agentic workflows, requiring strict isolation when running untrusted tool code.
  • It solves multi-tenant security and noisy-neighbor problems by isolating agent execution sessions inside dedicated MicroVMs.
  • In production, this architecture guarantees cryptographic session boundaries and prevents arbitrary code execution from compromising the host.
  • This blueprint details how to deploy AgentCore on MicroVMs, configure secure network boundaries, and integrate external Model Context Protocol (MCP) tool servers.

Why This Matters

Deploying agentic systems in multi-tenant enterprise environments introduces severe security risks, particularly when agents execute arbitrary code or interact with untrusted third-party APIs. Implementing an aws bedrock agentcore microvm architecture solves this fundamental challenge by establishing hardware-level isolation for every agent session. Traditional containerization (e.g., Docker) shares the host kernel, leaving systems vulnerable to container escape exploits (such as dirty COW or gVisor bypasses). By wrapping AWS Bedrock AgentCore execution environments in lightweight MicroVMs, such as AWS Firecracker, organizations achieve the security posture of traditional virtual machines with the startup latency and resource footprint of containers.

This approach was invented to address the tension between rapid agent execution and absolute tenant isolation. Without MicroVM-level isolation, a single compromised tool execution or a recursive loop exploit can compromise the underlying host, leak cross-tenant context, or exhaust shared CPU and memory resources (the "noisy neighbor" problem). In production, ignoring this boundary leads to catastrophic compliance failures under SOC2, HIPAA, or ISO 27001, as well as vulnerability to OWASP Agentic AI threats like system prompt leakage and tool privilege abuse.

Engineers should choose this MicroVM-based blueprint over standard Kubernetes pod isolation when agents are authorized to run dynamic code interpreters, execute complex local binaries, or connect to external Model Context Protocol (MCP) tool servers with varying trust levels. While it introduces slight orchestration complexity, it remains the gold standard for secure, multi-tenant agent execution.

Core Concepts

MicroVM (AWS Firecracker)

Extremely lightweight virtual machines running in user space via KVM, offering sub-second boot times and minimal memory overhead.

AgentCore Session

A logical boundary representing a single user interaction or execution thread, bound to a dedicated MicroVM.

Noisy-Neighbor Protection

Resource throttling (CPU shares, IOPS limits, memory cgroups) applied at the MicroVM boundary to prevent one session from degrading others.

Model Context Protocol (MCP) Server

An external or local service that exposes tools, prompts, and resources to the AgentCore runtime over a secure transport.

TAP Device

A virtual network kernel device that provides bridged Ethernet-like networking to the MicroVM, enabling isolated outbound-only traffic.

How It Works

The MicroVM Lifecycle

  1. Session Initialization: The orchestrator receives an execution request. It calls the Firecracker API to spawn a new MicroVM instance using a pre-baked, read-only root filesystem containing the AgentCore runtime.
  1. Network and Resource Provisioning: The host configures a dedicated TAP device for the MicroVM, applying strict iptables rules to block cross-VM traffic and restrict outbound internet access. CPU and memory allocations are locked via cgroups.
  1. AgentCore Bootstrapping: AgentCore initializes inside the MicroVM, loading the agent's specific system prompts, state, and transient memory from a secure, encrypted metadata service.
  1. Tool Execution and MCP Routing: When the agent calls a tool, AgentCore routes the request. If the tool is local, it executes within the MicroVM's isolated sandbox. If it is an external MCP tool, AgentCore establishes an encrypted, authenticated connection to the designated MCP server.
  1. Session Teardown: Upon task completion or timeout, the orchestrator issues a force-shutdown command to the MicroVM. The VM's memory state is destroyed, and the TAP device is torn down, ensuring zero residual data persistence.

Architecture

The architecture consists of a host bare-metal EC2 instance (typically an i3en or m5zn instance supporting nested virtualization via KVM) running a control plane orchestrator. The orchestrator manages a pool of AWS Firecracker MicroVM processes. Each MicroVM contains a minimal Linux kernel, a read-only root filesystem, and the AWS Bedrock AgentCore runtime.

Execution begins when a client sends a request to the orchestrator API. The orchestrator assigns a unique Tenant ID and Session ID, then provisions a MicroVM. Inside the MicroVM, the AgentCore process communicates with the external AWS Bedrock API via an isolated NAT gateway to process LLM reasoning loops.

For tool execution, the AgentCore process connects to either an internal sandboxed Python executor (inside the same MicroVM) or an external MCP Tool Server. The external MCP Tool Server runs in a separate, isolated network zone, accessible only via an authenticated, mTLS-secured TCP connection routed through the host's virtual switch. The host virtual switch blocks all direct inter-VM communication, forcing all traffic through a central security gateway where audit logs are generated. Execution ends when the orchestrator terminates the MicroVM process, reclaiming all allocated memory and disk blocks.

MicroVM Provisioning and Firecracker Configuration

To deploy AgentCore securely, the host system must run a daemon that manages Firecracker MicroVM lifecycles. Firecracker uses a jailer process to drop privileges, change root directories (chroot), and apply seccomp filters before starting the VM. The configuration is defined via a JSON payload sent to the Firecracker Unix socket.

The rootfs image must be minimized (typically under 100MB) using Alpine or a custom Yocto build, containing only the Python runtime, AgentCore dependencies, and required system libraries. The kernel should be compiled with minimal drivers, disabling PCI and USB support to reduce the attack surface.

Multi-Tenant Session Isolation and Network Boundaries

Multi-tenancy requires absolute network isolation. Each MicroVM is assigned a dedicated TAP interface on the host. We use ip link and tc (traffic control) to enforce bandwidth limits and prevent network denial-of-service attacks.

To prevent cross-tenant sniffing, the host's bridge interface must have split-horizon or private VLAN configurations enabled. Iptables rules on the host must drop any packet where the source IP belongs to a MicroVM TAP interface and the destination IP belongs to another MicroVM's subnet.

Integrating AgentCore with External MCP Tool Servers

Model Context Protocol (MCP) allows AgentCore to offload tool execution to specialized servers. When running inside a MicroVM, AgentCore must establish secure channels to these servers.

We implement a secure proxy pattern. The MicroVM connects to a local port mapped via SSH port forwarding or a secure VSOCK channel to the host. The host then proxies the MCP requests to the actual MCP server (which may run in a separate Kubernetes cluster or AWS Fargate task). This prevents the MicroVM from knowing the physical network address of the MCP server, mitigating SSRF (Server-Side Request Forgery) risks.

Noisy-Neighbor Protection and Resource Quotas

To prevent a compromised or poorly written agent loop from consuming all host resources, we enforce strict cgroup limits. Firecracker allows configuring rate limiters for both block storage and network interfaces directly in the VM configuration.

CPU allocation must use hard limits (cpu.cfs_quota_us) rather than soft shares to guarantee that a single VM cannot spike host CPU usage to 100%, which would increase latency for adjacent tenant VMs.

Code Example

Programmatic configuration and launch of a Firecracker MicroVM with jailer constraints, network TAP setup, and resource limits for an AgentCore session.
Python
import os
import json
import socket
import subprocess
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("FirecrackerOrchestrator")

class MicroVMManager:
    def __init__(self, vm_id: str, socket_path: str):
        self.vm_id = vm_id
        self.socket_path = socket_path

    def _send_put(self, endpoint: str, payload: dict):
        """Sends a PUT request to the Firecracker API socket."""
        client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        try:
            client.connect(self.socket_path)
            body = json.dumps(payload)
            request = (
                f"PUT {endpoint} HTTP/1.1
"
                f"Host: localhost
"
                f"Content-Type: application/json
"
                f"Content-Length: {len(body)}

"
                f"{body}"
            )
            client.sendall(request.encode('utf-8'))
            response = client.recv(4096).decode('utf-8')
            if "204 No Content" not in response and "200 OK" not in response:
                raise RuntimeError(f"Failed API call to {endpoint}: {response}")
        finally:
            client.close()

    def configure_vm(self, kernel_path: str, rootfs_path: str, tap_device: str):
        """Configures the boot source, drives, and network interfaces."""
        logger.info(f"Configuring boot source for VM {self.vm_id}")
        self._send_put("/boot-source", {
            "kernel_image_path": kernel_path,
            "boot_args": "console=ttyS0 reboot=k panic=1 pci=off nomodules"
        })

        logger.info(f"Configuring root drive for VM {self.vm_id}")
        self._send_put("/drives/rootfs", {
            "drive_id": "rootfs",
            "path_on_host": rootfs_path,
            "is_root_device": True,
            "is_read_only": True
        })

        logger.info(f"Configuring network interface {tap_device}")
        self._send_put(f"/network-interfaces/{tap_device}", {
            "iface_id": tap_device,
            "host_dev_name": tap_device
        })

        logger.info(f"Configuring machine resources (cgroups equivalent)")
        self._send_put("/machine-config", {
            "vcpu_count": 1,
            "mem_size_mib": 256,
            "smt": False
        })

    def start_vm(self):
        """Starts the configured MicroVM."""
        logger.info(f"Starting MicroVM {self.vm_id}")
        self._send_put("/actions", {
            "action_type": "InstanceStart"
        })

if __name__ == "__main__":
    # Retrieve paths from environment variables to avoid hardcoding
    SOCKET = os.environ.get("FIRECRACKER_SOCKET_PATH", "/tmp/firecracker.socket")
    KERNEL = os.environ.get("GUEST_KERNEL_PATH", "/var/lib/firecracker/vmlinux")
    ROOTFS = os.environ.get("GUEST_ROOTFS_PATH", "/var/lib/firecracker/rootfs.ext4")
    TAP = os.environ.get("VM_TAP_DEVICE", "tap0")

    manager = MicroVMManager(vm_id="session-abc-123", socket_path=SOCKET)
    try:
        manager.configure_vm(kernel_path=KERNEL, rootfs_path=ROOTFS, tap_device=TAP)
        manager.start_vm()
        logger.info("MicroVM successfully started.")
    except Exception as e:
        logger.error(f"Failed to initialize MicroVM: {e}")
Expected Output
INFO:FirecrackerOrchestrator:Configuring boot source for VM session-abc-123
INFO:FirecrackerOrchestrator:Configuring root drive for VM session-abc-123
INFO:FirecrackerOrchestrator:Configuring network interface tap0
INFO:FirecrackerOrchestrator:Configuring machine resources (cgroups equivalent)
INFO:FirecrackerOrchestrator:Starting MicroVM session-abc-123
INFO:FirecrackerOrchestrator:MicroVM successfully started.
Demonstrates how AgentCore inside the MicroVM communicates with an MCP server on the host using VSOCK (virtual sockets), bypassing the standard TCP/IP stack for enhanced security and lower latency.
Python
import os
import socket
import json
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("VSOCK_MCP_Client")

class VsockMcpClient:
    def __init__(self, cid: int, port: int):
        self.cid = cid
        self.port = port
        self.sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)

    def connect(self):
        """Connects to the host-side VSOCK listener.""" 
        try:
            logger.info(f"Connecting to host VSOCK at CID {self.cid} on port {self.port}")
            self.sock.connect((self.cid, self.port))
        except Exception as e:
            logger.error(f"VSOCK connection failed: {e}")
            raise

    def call_tool(self, tool_name: str, arguments: dict) -> dict:
        """Sends an MCP tool execution request over VSOCK."""
        payload = {
            "jsonrpc": "2.0",
            "method": "tools/call",
            "params": {
                "name": tool_name,
                "arguments": arguments
            },
            "id": 1
        }
        try:
            message = json.dumps(payload) + "
"
            self.sock.sendall(message.encode('utf-8'))
            
            # Read response (assuming newline-delimited JSON)
            response_data = self.sock.recv(65536).decode('utf-8')
            return json.loads(response_data)
        except Exception as e:
            logger.error(f"Failed to execute tool {tool_name} over VSOCK: {e}")
            return {"error": str(e)}

    def close(self):
        self.sock.close()

if __name__ == "__main__":
    # VM_ADDR_CID_HOST is always 2 in Firecracker VSOCK implementation
    HOST_CID = int(os.environ.get("HOST_VSOCK_CID", "2"))
    PORT = int(os.environ.get("MCP_VSOCK_PORT", "5005"))

    client = VsockMcpClient(cid=HOST_CID, port=PORT)
    try:
        client.connect()
        result = client.call_tool(
            tool_name="execute_python",
            arguments={"code": "print('Isolated Execution Successful')"}
        )
        logger.info(f"MCP Tool Result: {result}")
    except Exception as e:
        logger.error(f"Execution failed: {e}")
    finally:
        client.close()
Expected Output
INFO:VSOCK_MCP_Client:Connecting to host VSOCK at CID 2 on port 5005
INFO:VSOCK_MCP_Client:MCP Tool Result: {'jsonrpc': '2.0', 'result': {'content': [{'type': 'text', 'text': 'Isolated Execution Successful'}]}, 'id': 1}

Key Takeaways

MicroVMs provide hardware-level isolation (via KVM) for AgentCore sessions, eliminating container escape risks.
Noisy-neighbor protection must be enforced using hard cgroup limits and traffic shaping on host TAP devices.
VSOCK transport bypasses the TCP/IP stack, offering lower latency and higher security for host-to-guest communication.
Pre-warmed VM pools are essential to reduce cold-start latency from ~150ms to sub-10ms for real-time agent applications.
Session-specific IAM roles should be dynamically assumed via AWS STS and injected into the MicroVM at boot time.
Immutable logging via write-only VSOCK streams ensures compliance with SOC2 and HIPAA auditing requirements.

Frequently Asked Questions

What is the difference between running AgentCore in Docker vs a Firecracker MicroVM? +
Docker containers share the host OS kernel, making them vulnerable to kernel-exploit escapes. Firecracker MicroVMs run a dedicated guest kernel isolated by KVM, providing hardware-level security boundaries.
How do I handle file sharing between the host and the MicroVM? +
Avoid mounting host directories. Instead, use secure VSOCK channels to stream files, or use pre-signed S3 URLs for the agent to download and upload files directly.
Can I run this architecture on standard AWS ECS or EKS? +
Standard ECS/EKS run on shared kernels. To run Firecracker, you must use bare-metal EC2 instances (e.g., metal types) or instances that explicitly support nested virtualization.
What is the cold-start latency of a Firecracker MicroVM? +
A cold start typically takes 100ms to 150ms. This can be reduced to under 10ms by maintaining a pre-warmed pool of paused MicroVMs.
How does AgentCore inside the MicroVM connect to external MCP servers? +
The MicroVM routes MCP requests over a secure VSOCK proxy on the host, which forwards the traffic to the designated MCP server, hiding the server's network location.
How do I prevent an agent from running infinite loops and exhausting CPU? +
Enforce hard cgroup limits (cpu.cfs_quota_us) on the host for each Firecracker process, and implement an orchestrator-level execution timeout.
Is it possible to persist state across MicroVM sessions? +
No. MicroVMs should be treated as completely ephemeral. Any state or memory must be persisted externally (e.g., in a PostgreSQL checkpointer) and reloaded at session boot.
What happens if the guest kernel crashes inside the MicroVM? +
The host orchestrator detects the VM process termination, marks the session as failed, triggers a clean teardown of network resources, and provisions a new VM.