Rackspace Spot sells surplus data-center capacity as managed Kubernetes clusters, priced by open auction with a floor of $0.001 per hour — about $0.73 a month for a node if you win at the floor, which in the less-contested regions you usually do. That makes it the cheapest way I know of to run a real, persistent Kubernetes cluster — and a remote dev environment on top of it.
This guide gets you from no account to a working devcontainer dev environment served by DevPod, including the handful of sharp edges that aren’t in Rackspace’s docs. Everything here comes from running several Spot clusters in production for over a year; the pitfalls are ones I hit for real.
What you’ll need
kubectlinstalled locally- A Docker Hub account (free) — you’ll need a personal access token to dodge a rate-limit trap explained below
- Optionally, a repo with a
devcontainer.jsonyou want to develop in
Part 1 — A working Spot cluster
1. Sign up and create a cloudspace
Create an account at spot.rackspace.com, then create a cloudspace — Rackspace’s name for a managed Kubernetes cluster. The control plane is free on the standard tier; you pay only for the nodes you bid on.
Region choice matters more than anything else on your bill. Auction prices vary a lot between regions: the US regions around Ashburn (IAD) and Chicago (ORD) have historically had classes sitting at or near the $0.001/hr floor for long stretches, while others clear meaningfully higher. Check the live pricing table before you pick — it reads Rackspace’s public pricing feed and shows the current market price plus percentiles for every server class in every region.
2. Add a spot node pool — and bid with the percentiles
Add a node pool to your cloudspace. Two decisions here:
- Server class. A general-purpose class (e.g.
gp.vs1.mediumin your chosen region — 4 vCPU / 8 GB) is plenty for a dev environment. - Bid price. Your bid is the maximum you’ll pay; you’re charged the current market price as long as it’s at or below your bid. When the market rises above your bid, your node is preempted. The pricing table shows p20/p50/p80 percentiles for each class: bidding at the p80 level would have kept your node through roughly 80% of the recent window. Bidding exactly the current market price wins now and gets preempted at the next uptick.
Stick to spot pools. On-demand pools exist but cost many times more, which defeats the reason to be here.
3. Download the kubeconfig — then immediately make a better one
The kubeconfig you download from the console authenticates with an OIDC token that expires after about three days. It’s fine for bootstrap work, but anything unattended (CI, a dev-environment launcher, monitoring) will mysteriously lose access mid-week.
Mint a long-lived ServiceAccount credential right away:
export KUBECONFIG=~/Downloads/your-cloudspace.yaml
kubectl create serviceaccount admin-sa -n kube-system
kubectl create clusterrolebinding admin-sa \
--clusterrole=cluster-admin \
--serviceaccount=kube-system:admin-sa
kubectl create token admin-sa -n kube-system --duration=8760h
Build a kubeconfig around that token (same cluster/server block, swap the user
for token: <output>), keep it somewhere safe, and use it for everything from
here on. Scope it down from cluster-admin later if you keep the cluster around.
4. Two first-boot fixes every new cloudspace needs
Calico picks the wrong node IP. Spot’s Calico ships with firstFound
autodetection, which on these nodes grabs an unreachable interface — symptoms
range from flaky pod networking to nodes that never go Ready. Point it at the
node’s real InternalIP:
kubectl patch installation default --type=merge \
-p '{"spec":{"calicoNetwork":{"nodeAddressAutodetectionV4":{"kubernetes":"NodeInternalIP"}}}}'
Anonymous Docker Hub pulls get throttled. Spot nodes share NAT egress
addresses with other tenants, so Docker Hub’s anonymous rate limit
(HTTP 429) is effectively always exhausted. This can break anything pulling from
docker.io — I’ve seen it take down CNI and CSI images after a node was
recreated, leaving the node NotReady. Create an authenticated pull secret in each
namespace you deploy to, using a Docker Hub personal access token:
kubectl create namespace devpod
kubectl create secret docker-registry docker-hub-registry -n devpod \
--docker-username=<your-dockerhub-user> \
--docker-password=<your-dockerhub-PAT>
Reference it from your pods’ imagePullSecrets — or, for DevPod below, it gets
picked up automatically.
5. Storage: decide sizes up front
Spot’s storage is OpenStack Cinder. Two rules:
- Always set
storageClassNameexplicitly on every PVC. The default class varies by cluster, and on Spot thesataclass (orsata-largefor volumes over ~100 GB) is the one you want. - Volumes cannot be expanded. The API returns a 403 on resize — a PVC’s size is a lifetime decision. Size generously; at these prices a too-big volume costs cents.
Part 2 — The dev environment, with DevPod
DevPod is a client-side, open-source tool that creates dev
environments from devcontainer.json on any backend — including a bare
Kubernetes cluster. Nothing to install in the cluster ahead of time: each
workspace is a pod plus a PVC, created on demand, and DevPod’s dockerless mode
builds the devcontainer image inside the cluster, so you don’t need Docker
locally or a registry.
6. Install the CLI
curl -L -o devpod "https://github.com/loft-sh/devpod/releases/latest/download/devpod-linux-amd64" \
&& sudo install -c -m 0755 devpod /usr/local/bin && rm -f devpod
(There’s also a desktop app, and builds for macOS/Windows — see devpod.sh.)
7. Configure the Kubernetes provider once
This is where every Spot lesson from Part 1 gets encoded as a default:
devpod provider add kubernetes
devpod provider set-options kubernetes \
-o KUBERNETES_CONFIG=$HOME/.kube/spot.kubeconfig \
-o KUBERNETES_NAMESPACE=devpod \
-o STORAGE_CLASS=sata \
-o DISK_SIZE=30Gi \
-o PVC_ACCESS_MODE=RWO \
-o INACTIVITY_TIMEOUT=2h
Why each option matters here:
KUBERNETES_CONFIG— the long-lived kubeconfig from step 3, not the 3-day one.STORAGE_CLASS=sata— otherwise DevPod uses the cluster default, which may not be the class you want (step 5).DISK_SIZE=30Gi— this PVC is your workspace’s persistent home and it can never be expanded. Pick a size you won’t outgrow.INACTIVITY_TIMEOUT=2h— auto-stops idle workspace pods. The PVC (your work) stays; the pod (the compute) goes away until the nextdevpod up.- Pull secrets: the provider’s
KUBERNETES_PULL_SECRETS_ENABLEDdefaults totrue, so thedocker-hub-registrysecret from step 4 protects workspace image pulls from the 429 trap automatically.
8. Launch
devpod up github.com/your-org/your-repo --provider kubernetes --ide vscode
DevPod creates the PVC and workspace pod, builds the devcontainer in-cluster, and opens VS Code connected through a tunnel — no ingress, no load balancer, no public exposure of the cluster. Prefer a terminal?
devpod up github.com/your-org/your-repo --provider kubernetes --ide none
devpod ssh your-repo
9. Living with preemption
This is spot capacity: when the market price rises above your bid, the node goes away. What that actually means for a DevPod workspace:
- The pod dies, the PVC survives — your checked-out code, build caches, and home directory are all on the volume.
devpod up <workspace>re-provisions onto whatever node your bid wins next, usually within a couple of minutes of capacity returning.- Anything running unattended (long builds, servers) dies with the pod. Treat the workspace like a laptop that can run out of battery: commit and push often.
- Bidding at the p80 percentile (step 2) makes this rare — at the floor-priced classes, preemptions can be weeks apart.
Caveats worth knowing
- The 429 trap recurs. Every time Spot recreates a node, that node pulls its
system images through the shared NAT. If a fresh node sits NotReady, check for
ImagePullBackOffon CNI/CSI pods before suspecting anything else. - Prices move. The pricing table is a snapshot of Rackspace’s published feed with the fetch time shown — always confirm in the console before changing a bid.
- DevPod’s Kubernetes provider needs write access to one namespace. The
ServiceAccount options (
SERVICE_ACCOUNT,CLUSTER_ROLE) let you tighten this once you’re past the bootstrap stage. - Check your node pool type occasionally. Only spot pools; if an on-demand pool ever appears (e.g. from console experimentation), delete it — it bills at a completely different rate.