Case study · Prepare.sh · 2025–2026
Rebuilding the developer environment stack
Interactive developer environments exposed the limitations of the existing Azure AKS and vCluster approach. I contributed to the move to bare-metal Hetzner infrastructure running K3s, alongside a new GitHub Actions-based delivery workflow.
At a glance
- Isolated environment provisioning
- 2–3 min → <1 min
- Monthly infrastructure cost, as reported by the company after the migration
- ~$7,900 → ~$330
- Users on the platform
- ~10,000
Role · Founding Engineer, Cloud & DevOps
What I did
- Worked alongside the founder across the product: web development, practice content and labs, client conversations and deployment.
- Evaluated Docker-in-Docker limits on Azure AKS / vCluster.
- Contributed to the migration to bare-metal Hetzner K3s with GitHub Actions CI/CD.
- Contributed to the move of CDN, object storage and databases from Azure to Cloudflare R2, D1 and Workers.
- Built reusable Terraform modules and Helm deployments.
Team context
What the team did
- The learning platform, its content and the product itself were built by the wider team.
- The company’s cost figures reflect the migration as a whole, not my individual work.
- Later scaling of the platform was handled by others. I was not directly involved in it.
01
Context
Prepare.sh is a developer-education platform: learners work inside interactive, hands-on technical environments rather than reading about them. Roughly 10,000 users were served.
The first infrastructure ran on Azure: AKS for compute, Azure DevOps YAML pipelines for CI/CD, Azure Container Registry, Functions, Event Hub, Blob Storage and Azure CDN for media.
02
Problem
Each learner needs an isolated environment that behaves like a real machine, including the ability to run containers inside it. Docker-in-Docker on AKS with vCluster isolation ran into limitations, and an environment took about 2–3 minutes to provision, which is slow for an interactive experience.
03
Constraints
- Environments had to be isolated from one another.
- Learners needed to run containers inside their environment.
- Provisioning latency shapes an interactive experience.
- A small engineering team, so the stack had to be operable without a dedicated platform group.
- Infrastructure spend: the platform was moving off hyperscaler cloud credits.
04
Architecture
Target architecture after the migration.
- GitHub Actions builds and ships to a bare-metal K3s cluster on Hetzner.
- Each learner request creates an isolated environment on that cluster.
- Static assets, object storage and databases sit on Cloudflare R2, D1 and Workers.
05
Alternatives
What happened
Azure AKS with vCluster isolation was the starting point, and was evaluated against Docker-in-Docker requirements before the move to bare metal.
Retrospective analysis
- Stay on AKS with a different isolation model
- Plausible, but retained the cost profile and the managed-cloud constraints that were already pushing the team toward a different stack.
- Managed Kubernetes elsewhere
- Would change the vendor, not the underlying constraint between container isolation and nested containers.
- Per-learner VMs
- Strong isolation, but typically slower to start and heavier per environment than containers.
06
Decision
Move to bare-metal Hetzner running K3s, with GitHub Actions for CI/CD. K3s is a lightweight Kubernetes distribution. The documented drivers were the Docker-in-Docker limitations on AKS/vCluster and the wish to move off hyperscaler cloud credits and cut recurring spend.
Alongside that move, CDN, object storage and databases went from Azure to Cloudflare R2, D1 and Workers.
07
Implementation
- 01
Investigate
Evaluated Docker-in-Docker behavior on AKS and vCluster to understand where the limitations came from before choosing a replacement.
- 02
Cluster
Stood up the bare-metal Hetzner K3s cluster that isolated environments run on.
- 03
Delivery
Replaced Azure DevOps YAML pipelines with GitHub Actions CI/CD for the new stack.
- 04
Platform services
Moved CDN, object storage and databases to Cloudflare R2, D1 and Workers, removing the dependency on Azure services.
- 05
Repeatability
Wrote reusable Terraform modules and Helm deployments so environments and services could be provisioned without manual configuration.
08
Results
- Isolated developer environment provisioning dropped from approximately 2–3 minutes to under 1 minute.
- Company-reported: infrastructure cost fell from roughly $7,900 per month on Azure to roughly $330 per month after the migration. The figure comes from the company’s founder and reflects the migration as a whole, not my work alone.
- Reusable Terraform modules and Helm deployments replaced manual configuration for environment setup.
Measurable outcomes
- 2–3 min → <1 min
- Isolated environment provisioning
- From my work
- > 50% faster
- Provisioning time reduction, comparing the fast end of the old range (2 min) with the new ceiling (1 min)
- Calculated
- > 1 min saved
- Saved per environment created, using the same comparison
- Calculated
- ~$7,900 → ~$330 / month
- Reported infrastructure cost, before and after the migration
- Company-reported
- ≈96% lower
- Monthly cost reduction: ($7,900 − $330) ÷ $7,900
- Calculated
- ~$90,800 / year
- Annualized difference ($7,570 × 12), assuming the monthly figures hold
- Calculated
- GitHub Actions
- CI/CD for the new stack, replacing Azure DevOps pipelines
- From my work
- Terraform + Helm
- Reusable modules replaced manual configuration for environment setup
- From my work
09
Trade-offs & Failure Modes
From the project
- Moving to bare metal and a self-managed stack means the team now operates the cluster itself, in exchange for control over isolation, latency and cost.
General analysis
- Node failure
- On bare metal there is no managed control plane to absorb hardware faults; recovery depends on spare capacity and how quickly workloads can reschedule.
- Noisy neighbors
- Many isolated environments sharing nodes need explicit CPU, memory and storage limits to stay fair.
- Security boundary
- Nested containers need careful privilege and runtime choices; isolation is only as strong as those choices.
- Upgrades
- A self-managed K3s fleet makes version upgrades and certificate rotation the team's job.
10
Lessons
- Measure the thing the user feels. Provisioning time was the metric that justified the move.
- Understand the constraint before replacing the platform: evaluating the Docker-in-Docker limits first made the migration a decision rather than a reaction.
- Match the architecture to the actual workload. Interactive, short-lived, isolated environments have different needs from a typical web service.
11
Questions People Ask
The follow-ups this work tends to raise, answered directly.
What was wrong with Docker-in-Docker on AKS and vCluster?
Interactive environments need to run containers of their own. On AKS with vCluster isolation, that ran into limitations, which I evaluated before choosing a replacement. Those limits, plus the push to move off hyperscaler credits, led to the move to bare-metal K3s.
Why is nested Docker hard on managed Kubernetes in general?
Running containers inside a container typically needs elevated privileges or special runtimes, and managed Kubernetes and virtual clusters tend to restrict both. Bare metal gives full control over those choices.
General context
How much faster did environments get?
Provisioning an isolated environment went from approximately 2–3 minutes to under 1 minute.
What did the migration cost and save?
The company’s founder reported infrastructure costs of roughly $7,900 per month on Azure versus roughly $330 per month after the migration. That is the company’s result for the migration as a whole, not something to attribute to me alone.
Why K3s?
K3s is a lightweight Kubernetes distribution, which suits a small team running its own bare-metal cluster.
General context
Did you build all of this yourself?
I evaluated the Docker-in-Docker limits, contributed to the migration to bare-metal K3s and built the Terraform and Helm modules. The product and application code were built by the wider team, and later scaling of the platform was handled by others.