Remote dev with code-server over port-forward
# from the repo root, with the first-run prerequisites in place:bash examples/code-server-remote-dev/run.sh# then open http://127.0.0.1:8080The script builds an image carrying code-server on first use, launches a named VM, starts the IDE inside it, and forwards local port 8080. Re-running it reattaches to the same VM instead of launching a second one, and Ctrl-C stops the forward and nothing else. At the end of this page you will have a disposable, suspendable dev box whose idle time costs roughly snapshot storage instead of compute.
1. The image
Section titled “1. The image”Dockerfile starts from the platform’s al2023-1 base pair, pinned by digest, carries the daemon exactly as the client’s default Dockerfile does, then installs code-server from its release RPM. The RPM bundles its own Node, so the image adds only git and the shell basics beside it. The version is pinned by an ARG default; edit it to bump, and the content-hash-keyed image name makes the edited file build fresh under a new name.
2. The launch: named, kept, suspendable
Section titled “2. The launch: named, kept, suspendable”microvm run --keep --vm-name code-server-dev --image "$IMAGE_ARN" --config microvm.toml--vm-name registers the name in the local registry, so everything after the launch addresses the VM as --name code-server-dev. microvm.toml carries the choices that make this a dev box rather than a batch runner:
memory = 2048egress = trueshell = trueauto-resume = truemax-idle-sec = 900
[env]HOME = "/workspace"PATH = "/usr/local/bin:/usr/bin:/bin"memory = 2048 keeps the default baseline and its 8 GiB ceiling, because an IDE with language servers is a steadier workload than a peaky agent session. egress = true because git, package registries, and the extension marketplace all need outbound network. shell = true launches shell-capable, so microvm shell --name code-server-dev can open a real PTY beside the IDE. auto-resume and max-idle-sec wire the suspend loop below. The [env] table sets HOME somewhere uid 1000 can write settings and extensions, and an explicit PATH, because the daemon spawns execs with a minimal environment.
3. Start the IDE, detached and demoted
Section titled “3. Start the IDE, detached and demoted”microvm exec "code-server --bind-addr 127.0.0.1:8080 --auth none /workspace" \ --detach --user 1000 --group 1000 --name "$VM_NAME" --region "$REGION" \ --json >/dev/null
microvm exec "timeout 60 bash -c 'until echo > /dev/tcp/127.0.0.1/8080; do sleep 1; done' 2>/dev/null" \ --name "$VM_NAME" --region "$REGION" --json >/dev/null--detach starts code-server and returns; the second exec waits for the listener with bash’s /dev/tcp, so nothing has to be installed for the wait. --user 1000 because an IDE hands a terminal to whoever reaches it, and that terminal should not be root’s.
--auth none is sound here because three layers already gate the path. The local listener binds 127.0.0.1, so nothing off your machine reaches the forward. Every request the forward relays crosses the endpoint proxy, which requires an auth token scoped to this MicroVM and port set, and port-forward mints and attaches it for you. Inside the guest, code-server binds loopback only. A password prompt on top would authenticate nothing the proxy token has not already authenticated.
4. The forward
Section titled “4. The forward”microvm port-forward 8080:8080 --name code-server-devport-forward serves a guest port on localhost so a browser here reaches a server in the VM. The ports are LOCAL[:GUEST], and a single number uses it on both sides. --bind defaults to 127.0.0.1, deliberately; --max-connections stops after serving that many connections instead of running until Ctrl-C.
5. The suspend/resume loop is what makes this cheap
Section titled “5. The suspend/resume loop is what makes this cheap”Suspend is a freeze and restore. The filesystem, running processes, and the endpoint URL all survive a suspend/resume cycle, so unsaved buffers and the terminal you left open come back exactly as you left them.
The loop wires itself. An open editor tab holds a live WebSocket, which is inbound traffic, and inbound traffic resets the platform’s idle timer. So the VM stays RUNNING while you work, suspends about fifteen minutes after you close the tab, and, because the config sets auto-resume, resumes on the next request through the endpoint. Reload the tab with the forward still up, or re-run the script, and you are back. A suspended VM bills roughly snapshot storage alone.
One ceiling to plan around: the platform caps any single VM’s life at eight hours (--max-duration-sec, refused above 28800 before any call). This is a work-session dev box, so get work out before the ceiling with git from inside the IDE, or from outside:
microvm cp --tar vm:/workspace ./workspace-backup.tar --name code-server-dev6. A shell beside it
Section titled “6. A shell beside it”microvm shell --name code-server-devA real PTY with job control, signals, and resize, for anything the editor’s own terminal is the wrong tool for. It works while the browser tab is closed.
7. Teardown
Section titled “7. Teardown”microvm suspend code-server-dev # pause it nowmicrovm terminate code-server-dev # tear it downsuspend, resume, and terminate take the MicroVM id as their positional argument, and the registered name stands in for it. The image persists deliberately, because its snapshot has a one-week minimum retention; delete it with aws lambda-microvms delete-microvm-image when you are done with the recipe. The measurements behind the loop, the eight-hour ceiling, and the proxy’s port-scoped tokens are in Platform.