cd /news/ai-agents/building-bivack-a-cloud-dev-sandbox-… · home topics ai-agents article
[ARTICLE · art-135771] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Building Bivack: A Cloud Dev Sandbox for Coding Agents on AWS Lambda MicroVMs

Developer Gunnar Grosch released Bivack, a cloud development sandbox that gives each user a dedicated AWS Lambda MicroVM running coding agents with a persistent home directory backed by Amazon S3 Files, accessible from a browser via terminal or a full VS Code workbench. The tool is designed so agents and files stay on the MicroVM while users switch devices, with VMs suspending after two hours of inactivity and a maximum lifetime of eight hours. Grosch built and shipped the release while traveling, using a phone and laptop interchangeably against the same remote session.

by read19 min views5 publishedSep 21, 2026

I released a new version of Bivack this morning, and I built it from a car, a bus, an airport and a flight to Amsterdam. Laptop when there was a table, phone when there was not. The agent doing the work never moved: it sat on a MicroVM in us-east-1 the whole time, with my home directory in Amazon S3 where I had left it the night before.

That is the whole premise. Most of what I have written about agentic coding assumes the agent runs on your machine. Claude Code with Amazon Bedrock, hooks, MCP servers, plugins: all of it assumes a terminal on your laptop and a project on your local disk. That setup is excellent until you want to close the laptop, hand a task to an agent and walk away, or pick the work back up from a phone in a departure hall.

Bivack gives each person their own AWS Lambda MicroVM, with their coding agents on it and a persistent home on Amazon S3 Files, reached from a browser: a terminal or a full VS Code workbench. One machine per user, not one per agent, so the agents you selected all sit on the same box and share the same files. The name is a nod to a bivouac, a shelter you set up where you need it. One limit up front: the home is network storage, so it is slower than a local disk. This post covers how it works, the decisions that shaped it, and the parts that are harder than they look.

On the trip, I started work in the browser on my phone, then picked up the exact same session on my laptop when I had a table. The terminal made the phone leg workable; on the laptop, the workbench let me inspect the files and changes the agent had made rather than reconnecting only to a shell. I moved back and forth between the two several times before I shipped the release. There was no handoff to manage or context to recreate: the agent, the terminal and the files stayed put on the MicroVM while I changed devices.

The friction with a laptop-bound agent is mostly the laptop. The session is tied to that one machine, the provider logins live on it, and a long task means keeping the terminal open and the machine awake. You also cannot hand the same environment to a teammate without them repeating your setup.

What I wanted was narrow and specific:

That last one is why the workbench exists. Handing a task to an agent and reading a summary of what it did is not how I work. I want to open the diff, disagree with a function name, move a file, run the tests myself and then hand it back. A terminal covers the agent side of that fine, and it is thin for the rest: reviewing a multi-file change through a CLI pager on a phone is not reviewing. So the editor is not a nicety bolted onto the terminal, it is half the point, and both halves had to be the same machine for it to mean anything.

AWS Lambda MicroVMs matched those requirements closely. A Firecracker snapshot boots in a fraction of a second, each MicroVM is isolated, and you are billed while one is running. For an interactive sandbox that should feel instant and cost nothing while idle, that is the right primitive.

Sign in, pick a workspace, and you are in. The terminal and the editor are the same machine, so they share one home, one set of files and one set of logins. Because it is one machine, you can run an agent in a terminal pane and a second one beside it, both working the same repo. Bringing work in is the usual git clone; git and gh are already installed.

The numbers behind the walk-away part: a VM suspends after two hours with no inbound traffic, stays resumable for another thirty minutes, and no single VM lives longer than eight hours. So you have roughly two and a half hours of not touching it before the session is gone, and the running agent process goes with it. The home survives either way, which is the part that matters: files, shell history and every login are still there on the next sign-in. All three timers are deploy.env settings (IDLE_MAX_SECONDS, IDLE_SUSPEND_SECONDS, MAX_LIFETIME_SECONDS), so a longer leash costs awake time and nothing else. A travel day fits inside the defaults, which is how I picked them.

The entry point: two buttons, one for the terminal and one for the editor.

An xterm.js terminal that installs as a PWA and adds a touch key row on devices without a keyboard. It is where the agent CLIs run, each signed in with that person's own account. This is the half I used from a phone, and the touch key row is the reason it was usable at all.

The workbench is the real browser build of VS Code, backed by the same home and the same authenticated WebSocket mechanism. Same files, same logins, same machine as the terminal, so an agent can be working in one tab while I read its output in the other. Click a file path in its terminal and the file opens in the editor.

Two alternatives are worth naming. The hosted agents (Claude Code and Codex in the cloud, plus background agents from Cursor, Copilot and Devin) are less work: point one at a repo and it runs on the vendor's infrastructure with the vendor's model, usually per seat, with your code in their accounts. What you give up is the environment. A small VPS running tmux and code-server is the other shape, and it costs about five dollars a month, but everyone shares one host and one kernel, you keep the box running, and the home dies with the box. Isolation, fast starts and a home that outlives the machine are exactly what Lambda MicroVMs and S3 Files are for.

The starting point was Remote Developer (rDev) by Eric Johnson, which proved the core idea: per-user AWS Lambda MicroVMs behind Cognito, a browser terminal over a WebSocket, and a per-user S3 Files home mounted by a lifecycle hook. Bivack keeps that foundation and takes it somewhere else. rDev's sandbox carried credentials for Amazon Bedrock and the AWS account hosting it; Bivack removes that hosting-account access. The browser VS Code workbench is new, as is the chooser and login frontend around it, the configurable MicroVM image, the one-command deploy and teardown, the NAT modes, the budget wiring, and the break-glass tooling. The attribution is in NOTICE, and it is genuine: the template shape, the image bones, the lifecycle hooks and the WebSocket terminal all trace back to Eric's work.

One constraint drives most of the design, so the request path is the place to start.

Browser
  |-- HTTPS ------> CloudFront (static frontend)
  |-- sign in ----> Cognito
  |-- GET /token -> API Gateway -> token Lambda function
  |                                   |-- find or create this user's access point
  |                                   '-- launch or resume the MicroVM
  |                                       -> { authToken, endpoint }
  '-- wss:// -----> MicroVM agent (file system, PTY)
                         '-- mount /home/coder (the /run lifecycle hook)
                                 -> S3 Files access point for this user

Cognito holds the users, and they are admin-created: adding a teammate means creating their login by hand, and each one gets their own access point and MicroVM. API Gateway validates the JWT before the Lambda function runs, so the function only ever sees a verified sub. That sub maps to one S3 Files access point rooted at /users/<sub> and one MicroVM. On the first request it creates the access point, launches a MicroVM, and returns a short-lived token and the endpoint. The MicroVM mounts its own home during the /run lifecycle hook, so the mount is per-user and the image stays shared.

Here is the constraint. A Lambda MicroVM endpoint accepts the auth token only in the X-aws-proxy-auth header. A browser can set that header on a fetch or a WebSocket handshake, but it cannot set a header on a page navigation, or on any of the subresource loads a page makes after it. So you cannot run a web IDE inside the MicroVM and point the browser at it. Not with a redirect, not with a cookie, not with a query string.

That single fact decides the shape of the entire frontend. The terminal is an xterm.js client rather than a served page. The IDE is a self-hosted workbench that talks to the VM over a WebSocket rather than an editor served from the VM. Both run over wss://, and the endpoint is useless without the short-lived token. Every design decision downstream of the ingress follows from a header a browser is not allowed to set.

The sandbox is not a machine you install things on. It is an image you declare, and deploy.env is where you declare it:

CLAUDE=latest           # install the current release
KIRO=                   # empty also disables it
TOFU=1.12.6             # install this pinned release

Four coding agents are available (CLAUDE, CODEX, OPENCODE, KIRO) and four infrastructure CLIs ( CDK, SAM, TOFU, TERRAFORM). Claude Code is the only one enabled in the generated deploy.env; the rest are opt-in, because every tool is weight in an image that every user boots. Setting a tool to latest takes its current release and a version string pins it. Kiro CLI is latest only, and pinning OpenTofu and Terraform is worth doing if you want the same image twice.

Changing any of those settings changes the packaged MicroVM source hash, so the next ./scripts/deploy.sh rebuilds the image on its own. The trade-off is worth stating plainly: adding a tool is a five to ten minute image build and a VM recycle, not npm install -g. In exchange, nobody hand-installs anything into a sandbox that gets thrown away, and every user gets the same machine.

Each selected CLI signs in once and keeps its session under /home/coder:

CLI Command First-run login
Claude Code claude sign in with your Claude plan
Codex codex sign in with your OpenAI account
OpenCode opencode /connect to add a provider
Kiro CLI kiro-cli kiro-cli login (device flow)

They share workspace files while keeping their own configuration and history, and each is configured for unattended work: Claude Code uses bypassPermissions, Codex bypasses approvals and its local sandbox, and Kiro CLI carries a persistent allow-all policy. The MicroVM is the isolation boundary, which is the only reason that is a reasonable default.

The home has to outlive the machine. MicroVMs suspend when idle and terminate after a maximum lifetime, so anything stored only on the VM's local disk is gone when it recycles. I considered EBS, EFS and S3 Files. EBS is tied to one instance and one Availability Zone, which fights the recycle-and-come-back model. EFS would work, and its access points can root at a directory with an enforced UID and GID, so isolation is not the hard part. What made S3 Files the better fit is that the home is the same bytes as an S3 prefix: an access point rooted at /users/<sub> is all that user can see, and everything they write is also readable as ordinary objects in the bucket with the tools I already have.

The trade-off: file operations go over NFS to a network-backed filesystem, so metadata-heavy work is slower than local disk. You notice it when a CLI scans its config or a large cache. It is the right call for durability, and it is not free.

S3 Files requires versioning on the bucket (prerequisites). That is fine until you realize how much a home directory churns: package caches, session history, plugin state, logs. Every write becomes a new object version and every delete becomes a delete marker, so the bucket grows even when it looks empty. A lifecycle rule that expires noncurrent versions after a week, drops expired delete markers and aborts stalled multipart uploads keeps the growth bounded. Without it you are paying to store every intermediate state of a file nobody will look at again.

I expected the IDE to be configuration on top of an existing package. It is closer to building an application. The FileSystemProvider and the terminal backend both run over the same authenticated WebSocket to a small agent in the VM. There is no local file system to read, and the ingress constraint rules out serving the editor from the VM, so the workbench stays a client-side app that talks to the agent. The provider turns that socket into stat, readDirectory, readFile, writeFile, createDirectory, delete, rename and a recursive watch, and the agent pushes change events back so the workbench sees an external edit. Making VS Code treat a remote-over-socket tree as a normal workspace, with all of that, takes more wiring than the documentation suggests.

One thing that surprised me: a browser workbench keeps its user settings in the browser, in IndexedDB, not on the machine it is editing. Workspace settings (.vscode/settings.json) live in the VM, but the global ones do not follow you between browsers. And if you set defaults by writing the user settings file, you only ever apply them once, because the file already exists on the next load. Registering configuration defaults instead lets people override the ones they care about and have it stick.

Two sets of AWS accounts are in play here, and they are separate. One hosts the stack: the MicroVMs, the bucket, the API. The others are whatever accounts you use for your own work. The sandbox gets no credentials for the hosting account. Its execution role can mount S3 Files and look up its own endpoint, and nothing more. When you need AWS inside the VM, you bring your own account in.

The profiles and token cache live in ~/.aws in the home, so you set them up once rather than once per VM.

rDev took a different path: its MicroVM execution role gives the sandbox Amazon Bedrock access and broad, boundary-limited permissions in the AWS account that runs the stack. You can still add your own provider login there. I moved away from giving the sandbox hosting-account credentials at all: that avoids coupling it to one provider and keeps a compromised agent out of the account that runs everything.

The reach of a compromised agent is the sandbox and everything signed into it. Provider logins persist in the home, so a malicious file that the agent reads can reach ~/.claude, ~/.codex, ~/.kiro, the gh config, an aws sso cache and git credentials. The agent cannot touch the AWS account the stack runs in, which is the line I care about, and it can touch every other account the person signed into inside the sandbox. That is the boundary.

On a shared deployment there is one more reader to account for: the home lives in the bucket owned by whoever operates the stack, so the operator can read every teammate's provider logins. On a personal deployment that is the same person. On a company deployment, it is a decision to make on purpose.

Each MicroVM reaches model providers and package registries through the private subnets, so those subnets need a way out to the internet. The default is a small NAT instance running masquerade: a t4g.nano at a few dollars a month, patched weekly by an SSM association. Set NAT_MODE=gateway and it becomes a managed NAT Gateway at roughly ten times the monthly cost, with the patching handled for you. For a small team the instance is the sensible default; pick the gateway if you would rather not own the patch.

You will need:

aws lambda-microvms. AWS::Serverless::MicrovmImage. zip and python3 (packaging and small helpers in the scripts)us-east-1) Docker is not required. The MicroVM image is built server-side by the Lambda MicroVMs build service. The deploy checks the AWS CLI for MicroVM support before it applies a stack update, because an older CLI is the first thing most people hit.

git clone https://github.com/gunnargrosch/bivack
cd bivack
cp deploy.env.example deploy.env
$EDITOR deploy.env
./scripts/deploy.sh

deploy.env starts with three values:

AWS_PROFILE=your-profile
AWS_REGION=us-east-1
LOGIN_EMAIL=you@example.com

Everything else is optional: the tool settings above, the stack name (STACK_NAME), the memory tier ( MEMORY_MIB), the three timers, the first login's temporary password ( INITIAL_PASSWORD, random if unset), egress ( NAT_MODE), and the budget ( BUDGET_EMAIL, BUDGET_USD).

The first run bootstraps the stack, builds the IDE, packages the MicroVM image, uploads the frontend, creates your first login, and smoke-tests a throwaway MicroVM. The image build alone is 5 to 10 minutes, so the first deploy is not a quick one.

FrontendUrl: https://<id>.cloudfront.net
Login:       you@example.com

First sign-in: open the URL, sign in with the temporary password below, and
set a new password when prompted.

  Temporary password: <printed once>

Set BUDGET_EMAIL and the stack also creates a monthly AWS Budget, $25 unless you change BUDGET_USD, that emails that address at 80% and 100%. Leave it unset and no budget is created. It is worth setting: a sandbox left running shows up as an alert rather than a surprise. Cost tracks awake time and storage rather than seats, since the MicroVM bills only while it is running and a suspended VM costs nothing but the home's storage.

Sign in, pick the terminal or the editor, and the CLIs sign in with your own accounts. The first launch of claude on a brand-new VM does its first-run work, so it is slower than every launch after it. The image build runs a /validate hook that exercises that startup path so the platform prefetches it, which brings the cold start back down.

To use AWS from inside the VM, run aws configure sso once to create a session and profile, then sign in by naming one of them:

aws sso login --profile management --use-device-code
aws sso login --sso-session my-org --use-device-code

A bare aws sso login resolves the default profile, which usually has no SSO keys. --use-device-code is required because the default Authorization Code flow redirects to the VM's own 127.0.0.1, which your browser cannot reach. The device-code flow instead prints a URL and code that work from any device.

There is no SSH into a MicroVM. For break-glass access, tools/exec.js opens a shell on a running VM and tools/run-remote.js runs a single command against it. To remove everything the stack created, run ./scripts/teardown.sh: it deletes the stack, the buckets including their object versions, the Cognito user pool, the SSM parameters, running MicroVMs, the MicroVM images and the log groups.

git pull
./scripts/deploy.sh

deploy.sh rebuilds only what changed: the IDE when its sources moved, the MicroVM image when microvm/ or a tool setting moved, the frontend when its assets or generated config moved. A no-change run skips frontend publishing, the CloudFront invalidation and the artifact upload. The buckets and the S3 Files home are never touched, so files, history and logins survive an upgrade. Running VMs keep the old image until they recycle; hit the terminal's power button to move onto a new one immediately.

Two flags are worth knowing before a stack update. --dry-run creates a CloudFormation changeset without executing it, and --review prints the changeset and waits for you to confirm. What you are looking for there is Replace on UserPool, WorkspaceBucket or S3FilesFileSystem, because a replacement on any of those drops users or data. The one time you want it is bumping S3FilesFileSystem's ClientToken, which is the recovery path for a wedged synchronization state. The rest of the flags are smaller: --frontend-only re-uploads the frontend and IDE with no SAM deploy and no image, --no-smoke skips the throwaway test VM, and --build-ide forces an IDE rebuild.

The IDE is the browser build of VS Code, so only extensions with a web entrypoint install, and Microsoft and GitHub sign-in and Settings Sync are unavailable because they are Microsoft-hosted built-ins. Source Control has no provider at all: the browser cannot spawn the git binary and monaco-vscode-api does not ship VS Code's git extension, so git and gh in the terminal are the answer. IDE user settings are per-browser, and auto-save starts off with tooling dotfiles hidden behind files.exclude, both overridable defaults.

The home is network storage, so it is slower than a local disk. Inside the VM, coder has passwordless sudo and the workload has open outbound internet, so the MicroVM is the isolation boundary rather than a hardened jail. Model-provider spend is billed to each user's own provider account with no sandbox-level cap. The stack targets one region.

There is no built-in preview for a dev server. The ingress accepts the auth token only in a header, so there is no port-forward from the browser to a port inside the VM; if you need one, run your own tunnel from inside the VM. The VM memory is a fixed tier (MEMORY_MIB, 4 GB by default, bursting to 4x), and that tier is the ceiling an agent works within.

The travel day worked. I built and released a version of Bivack using the previous version of Bivack, from three modes of transport and two devices, and the only thing I actually needed was a browser and my own logins.

What still stays on my laptop: anything that needs a dev server I can open in a browser tab, anything metadata-heavy enough that NFS latency turns into waiting, and anything I want to leave running for a full workday without touching it. The eight hour ceiling and the two and a half hour idle window are generous for a commute and thin for a weekend batch job.

The parts that took the most work were the plumbing around the agent CLIs: a durable home on S3 Files, a workbench that had to become a real application, and a credentials story that stays deliberately empty. If you deploy it, the S3 versioning bill is the one to watch, and the first launch of any CLI on a fresh machine is the slow one.

What would you run here, and what would you keep on your laptop? Let me know in the comments.

── more in #ai-agents 4 stories · sorted by recency
── more on @bivack 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/building-bivack-a-cl…] indexed:0 read:19min 2026-09-21 ·