Dev VMs instead of laptops: a remote team's first year on hardened AWS VMs

17 min read
A developer at a rooftop café table in Lisbon at sunset, working on a thin laptop whose screen shows a GPU job at full load; in the sky above, a server rack with a padlock, linked to the laptop by a thin line of light.

TL;DR — This is a long post for engineering leads. If you run finance or operations, what it costs, where a laptop still wins and how to set it up are enough. The story: a 20-developer remote team stopped buying laptops and gave everyone a hardened VM in AWS, run by Idlefy on leases, so it is off whenever nobody is working. Onboarding became a config change, GPUs became an hourly line item instead of a $16,000 purchase, and the 2025 npm attacks that hijack AI coding agents walk away with short-lived credentials scoped to one developer instead of long-lived keys to the whole account. Along the way: why a stopped VM still costs about $12 a month, why the same machine costs $51 or $159 depending on one setting, why switching to a GPU is a rebuild and not a resize, what the 2025 npm attacks actually find on a hardened VM, and five cases where the laptop still wins.

What is a cloud developer VM?

A cloud developer VM is a per-person virtual machine in AWS or Google Cloud that holds the developer’s tools, code and credentials, while the laptop in front of them becomes a terminal and a browser. For a remote team it fixes three things a laptop cannot. Onboarding and offboarding turn into a config change: a new hire gets a machine in minutes with nothing shipped, and a leaver loses access as soon as the deletion is applied. AI workloads stop being a hardware purchase: a GPU box is rented by the hour and returned. Security becomes a policy you enforce rather than a laptop you hope is patched: no long-lived cloud keys on disk, a firewall that drops everything inbound, an audit trail that leaves the machine. The catch is cost, and it only holds if the VM is off by default. One of our customers runs 20 developers this way, on an open-source dev VM configuration that grew out of their request.

I run Idlefy. For about a year one of our customers, a fully remote product team, has worked on cloud VMs instead of company laptops. Twenty developers, three AWS regions (us-east-1, eu-central-1, eu-north-1), one Terraform repo. The open-source configuration we publish today grew out of that work. The team is not named here. This post is the reasoning behind their decision, the parts that turned out to matter more than either of us expected, and the parts where a laptop still wins.

Why did they stop buying laptops for developers?

Because they hire remotely, and a laptop is the slowest, most fragile part of a remote hire.

The sequence is familiar to anyone who has run a distributed team. You sign the offer. You order a machine. You ship it across a border, wait for customs, and hope the courier finds the address. Someone in IT sets it up over a video call. Then the person leaves, and the whole thing runs in reverse, except now the incentive to cooperate is gone. In Capterra’s offboarding survey, 71% of HR staff who offboarded someone in the previous year had at least one leaver keep company equipment, and remote and hybrid staff were 17% more likely to do so than office staff.

With a VM, the same sequence is a pull request. Adding a developer is one entry in a Terraform variables file and a terraform apply:

"anna" = {
  instance_type  = "m7i.xlarge"
  volume_size_gb = 100
  fqdn           = "anna.ec2.eu-central-1.example.com"
  key_name       = "anna"
  tags           = { Owner = "anna", Purpose = "Development" }
}

A few minutes later the machine exists in the region closest to the person, with an Elastic IP, a DNS record and the entire base policy applied on first boot: firewall, SSH hardening, rootless Docker, Node.js, kubectl, Claude Code and the rest. No hands touch it. Removing the developer is the same apply with the entry deleted, one make target that cleans up the config-management node, and one click in the Tailscale admin console to drop the node from the tailnet. There is no device to retrieve, because there was never a device.

The laptop the person already owns becomes what it is good at: a screen, a keyboard and a battery.

What does a developer VM cost compared with a laptop?

About $51 a month, all in, for the team’s default machine, if it is off when nobody is working. Around $159 if it runs around the clock. That gap is where most teams get the arithmetic wrong: the instance type is not the variable, the hours are.

At the time of writing, a 14-inch MacBook Pro with the M5 Pro chip starts at $2,499, after Apple raised MacBook Pro prices by $300 in June 2026 on the back of the memory shortage. That is a capital expense on day one, before the person has written a line of code, and it depreciates whether the person stays three years or three weeks. Companies typically write laptops down over three to five years.

Now the VM. On-demand, Linux, us-east-1, from our own pricing snapshot dated 2026-08-31. The last two columns are the same machine run around the clock, then run only during a 45-hour working week, close to the eight-hours-a-day, five-days-a-week pattern that AWS’s Well-Architected guidance assumes for dev and test.

InstanceWhat it is24/7 (730 h/mo)Work hours only (est. 195 h/mo)
t3.large2 vCPU, 8 GiB, light dev box~$61~$16
m7i.xlarge4 vCPU, 16 GiB, the team’s default~$147~$39
g6.xlarge1× NVIDIA L4 (24 GB), small inference~$588~$157
g6e.2xlarge1× NVIDIA L40S (48 GB), serious inference~$1,637~$437

Add the disk and the address. AWS bills EBS storage whether the instance is running or stopped, so a 100 GB gp3 volume costs on the order of $8 a month all year round at published gp3 rates. Each VM also keeps an Elastic IP, and a public IPv4 address costs $0.005 an hour, about $3.65 a month, stopped or not. That is the floor: a developer’s stopped VM costs roughly $12 a month to keep around.

So the m7i.xlarge on a working-week schedule comes to about $51 a month with storage and address, or around $610 a year. Left running around the clock it is closer to $1,900 a year, which is where the “cloud dev boxes are expensive” reputation comes from. Nothing in between is a cheaper instance type. It is whether anything turns the machine off.

The airplane rule. A dev VM only costs like an airplane if you fly it 24 hours a day. Theirs sit stopped until someone books them.

That is the Idlefy part of the setup, and the reason we were in the room at all. The VMs are stopped by default. A developer books a machine for a session from Slack or the web, it starts, and when the lease expires it stops itself. Nobody remembers to shut anything down, because nobody has to. AWS’s Well-Architected cost optimization pillar puts the saving for this pattern at up to around 75%, on a 40-hour week; on this team’s 45-hour week it is an estimated 73% off the compute line. Another customer we have written about came in at up to 67% on dev VM spend; how a team actually works moves it several points either way.

Why do AI workloads make the laptop argument worse?

Because the hardware that AI work wants is exactly the hardware that is hardest to buy sensibly right now.

Two things happened in 2025 and 2026. AI tooling became a default part of the job: in the 2025 Stack Overflow survey, 84% of developers use or plan to use AI tools, and 51% of professionals use them daily. At the same time, the memory shortage made local AI hardware a moving target. NVIDIA’s 96 GB RTX PRO 6000 Blackwell, the card people buy to run large models on a workstation, now lists at $16,000, roughly double its launch price. Apple quietly removed the 512 GB Mac Studio, the configuration the local-LLM crowd had settled on, and raised the price of the 256 GB option.

Buying inference hardware for a developer means guessing, today, how much memory they will need in eighteen months, paying for it up front, and accepting that it depreciates faster than anything else on the balance sheet. For a remote hire, it also means shipping a workstation.

A VM inverts every part of that:

  • The GPU is rented by the hour and returned. A g6e.2xlarge with a 48 GB L40S costs roughly $2.24 an hour in us-east-1. A developer who needs it for ten hours a week spends on the order of $100 a month; in the weeks they don’t touch it, they pay for the disk. Nobody has to justify a five-figure purchase for an experiment.
  • The size is a config field. The same developer’s entry can say m7i.2xlarge this sprint and m7i.xlarge next: stop, change the type, start, and the disk comes along. Moving to a GPU type also means pointing the entry at an image that carries the NVIDIA driver, so that one is a rebuild rather than a resize.
  • The agent lives where the compute is. Claude Code runs on the VM, next to the repo and the build cache, with the tools the configuration installs on every machine. A long agent run keeps going after the laptop lid closes. From the developer’s side it is an SSH session, from any device.

That last point is why the team treats the laptop as a thin client. The state, meaning the checkouts, the containers and the agent sessions, lives on a machine that is patched, rebuilt from code when needed, and reachable from a phone if it has to be.

Is a VM more secure than a laptop, or just different?

More secure, for one specific reason: a VM is a controlled environment you can enforce a policy on, and a laptop is an endpoint you can only hope about. The difference became concrete in 2025.

In August 2025 the Nx build tool was compromised on npm. The malicious post-install script did something new: it looked for AI coding agents on the developer’s machine and launched them with their safety checks disabled (Claude Code with --dangerously-skip-permissions, Gemini CLI with --yolo, Amazon Q with --trust-all-tools) and a prompt asking them to inventory every credential-looking file on disk, as Snyk documented. GitGuardian counted 2,349 credentials from 1,079 developer systems: GitHub tokens, npm tokens, SSH keys, AWS keys, wallet files. Three weeks later the Shai-Hulud worm hit hundreds of npm packages with the same idea and a self-replication loop: harvest GitHub, npm, AWS and GCP credentials from the machine, query the cloud instance metadata endpoint, and use the stolen tokens to publish itself into the next maintainer’s packages.

Both attacks ran with the developer’s own privileges. No exploit, no privilege escalation, just npm install on a machine where the credentials were sitting in the home directory. A laptop with the AWS CLI configured is that machine. So is a laptop with a password manager unlocked and a browser full of SSO sessions.

On these VMs the same script finds a different world. This is what the open-source configuration enforces on every machine, re-applied every 30 minutes so manual changes do not survive:

ConcernWhat the VM does about it
Cloud credentialsNo long-lived keys anywhere on disk. Each VM has its own IAM role with a permissions boundary, an IAM ceiling on what that role can ever be granted; a root-owned broker refreshes short-lived STS credentials into a tmpfs file the developer can read but not replace. The platform never places a static key, and writing one by hand trips a Falco rule.
Blast radiusThe permissions boundary is per VM. A compromised machine can reach exactly what that developer was granted, not the whole account.
Instance metadataThe firewall drops the metadata endpoint for everyone except root and the admin-access daemon. Containers and the developer’s own processes cannot grab role credentials from it, which is precisely what Shai-Hulud tries.
InboundDefault-DROP firewall, SSH hardened, fail2ban, root login disabled.
DockerRootless. The system daemon is disabled and containers run under the developer’s own uid, so root inside a container is the developer outside it, not root on the host.
Admin accessOperators connect through EC2 Instance Connect with SSH keys that expire after 60 seconds. Nobody holds a standing admin key. Every connection request is logged in CloudTrail.
PatchingUnattended updates daily for the OS and the tool repositories; the log shipper and the runtime detector are version-pinned and move only by a deliberate edit. Because the lease model stops and starts the VM, kernel updates actually get applied instead of waiting for a reboot nobody schedules.
Audit trailThe system journal ships off-host to Loki within seconds. The developer cannot read the system journal or stop the shipper, and a sudo attempt cannot be buried under log noise the developer generates. Six alert rules read that stream.
Runtime detectionFalco watches syscalls for four things that have no innocent explanation on this platform: a credentials file being written under a home directory, a non-root process reaching for instance metadata, a denied probe of the credential store, and the developer attempting sudo for anything but the one command they are allowed. All four land in one Grafana alert that is critical, has no threshold, and fires on the first event.

I want to be precise about the claim. This does not make npm install safe; nothing does. What it changes is what a malicious package can walk away with. On the laptop it gets long-lived keys and every session the browser holds. On the VM it gets short-lived credentials scoped to one developer’s bundle, an alert fires when it writes the file it wanted to write, and the whole environment can be destroyed and rebuilt from Terraform in the time it takes to read the incident report.

There is a second, quieter benefit. When an AI agent runs on a hardened VM, the question “should I let it run with permissions skipped?” stops being existential. The agent has the developer’s scope on a disposable machine, not the developer’s life.

What does mobility actually look like day to day?

Like not caring which device you are holding.

Every VM joins the team’s tailnet under the developer’s own identity, not a shared machine key. The only sudo a developer has on the box is the single command that does that. From then on the machine is reachable by name from their laptop, their tablet, or a phone, and any laptop that can run a terminal is good enough. A dead laptop is replaced with whatever is in stock locally, and the environment does not notice: the checkouts, the containers and the agent sessions were never on it.

Publishing something for a colleague is a few labels on the developer’s own container: Traefik runs on every VM and fronts the app on <name>.ec2.<region>.<domain> with an automatic Let’s Encrypt certificate and basic auth, so “look at this branch” is a URL instead of a screen share.

Regions are part of mobility too. A developer in Helsinki gets a VM in eu-north-1 and a developer in New York gets one in us-east-1. Latency is the thing people worry about most before switching, and the fix is boring: put the machine near the person. Three regions cover this team; adding a fourth is a script.

And a lost laptop is a lost laptop. Nothing on it was the source of truth, and the tailnet identity is revoked from a console, not from the device.

Where does a laptop still win?

I would rather say this than have you find out.

  • Anything that needs local hardware. iOS and macOS builds, GUI-heavy design tools, and anything that talks to a physical device on the desk. On this team those people get a Mac, and a VM too for everything else.
  • Working offline. A VM needs connectivity. On a plane you are back to whatever runs on the laptop.
  • Latency-sensitive interactive work. A terminal over a good connection is fine. A remote desktop for pixel-level UI work is not, at least not for everyone. Pick the region carefully and be honest about the workflow.
  • Teams with no lifecycle discipline. This is the big one. If nothing stops the VM, the right-hand column of the cost table disappears and you are paying $147 a month for a machine that is used 45 hours a week. Automate the off switch before you migrate anyone, not after.
  • You now operate a platform. A laptop fails one person at a time; a bad Terraform apply or an unreachable region fails everyone. Someone has to own the configuration server, the alerts and the upgrade path. It is small but not zero, and it is a new on-call surface.
  • Data residency. Sometimes a good constraint: you choose the region. Sometimes a hard one: some data may not leave a particular jurisdiction at all, and the laptop was never the compliant answer either.

How do you set this up yourself?

The whole configuration is on GitHub at idlefy/vm-platform-aws, Apache 2.0. It is a template: Terraform modules for the per-developer instances (VPCs, subnets, Elastic IPs, DNS, IAM roles and boundaries) and a CINC (open-source Chef) cookbook that holds every hardening rule and tool from the table above, plus the Grafana dashboard and the six alert rules. Your own settings live in a small private repository generated from it, pinned to a release of each.

In outline: copy the skeleton into a private repo and fill in a handful of values (AWS profile, DNS zone, state bucket, CINC org name; a placeholder check and a read-only preflight catch what you missed), bootstrap the configuration server once, then add one entry per developer and apply. Every VM converges to the published policy on first boot and every 30 minutes after. The last step is the one that turns the cost column from left to right: tag the machines for Idlefy or wire up whatever schedule you already trust, so the default state is off. The README has the exact commands.

The repository has had one external security review so far. Every finding was fixed and re-measured on a live fleet before the next release, and the review record is checked in next to the code.

If you would rather solve the lifecycle part with cron and a Lambda, the repo does not care. The hardening and the audit trail are the part I would not want anyone to rebuild from scratch, which is why it is public.

Frequently asked questions

How much does a developer VM cost per month? It depends almost entirely on how many hours it runs. In us-east-1 on-demand pricing (snapshot dated 2026-08-31), a 4 vCPU, 16 GiB m7i.xlarge costs about $147 a month running 24/7 and about $39 a month running a 45-hour working week, plus about $12 for the disk and the public IP address, which are billed even while the machine is stopped.

Do AI coding agents work well on a remote VM? Yes, arguably better. Claude Code and similar agents run next to the repo and the build cache, keep going after the laptop lid closes, and get a scoped, disposable environment rather than the developer’s personal machine. GPU capacity for local inference is rented by the hour instead of bought.

Does the VM have to run all the time? No, and it should not. Stopped instances are not billed for compute, only for their disks and their public IP address. Idlefy keeps this team’s VMs stopped by default and starts one when a developer books a session; when the lease ends it stops again.

Disclosure

I’m the founder of Idlefy. The team in this post is a customer: the VM configuration we publish grew out of building theirs, and Idlefy does the lease-based start and stop for their fleet, across AWS and GCP. Our own team is a handful of people, which is why this is their story rather than ours. The VM configuration itself is independent of Idlefy: it is plain Terraform and CINC, and it runs fine with any scheduler, or with none, if you enjoy paying for weekends.

The numbers in this post are the team’s, or linked to their source. The cost table uses our own pricing snapshot, so check the linked instance pages for the current figures before you budget anything.


Related reading: How to audit idle VMs in AWS and GCP for measuring what a fleet actually costs while nobody is using it, and From ‘ship it’ to ‘shut it down’ on why lifecycle, not architecture, is where dev spend leaks. The RSS feed has more posts in this thread.