SheetBuilding in public

Remocal: a staging slice per developer


Preview environments rot because they are shared and expensive to keep honest. A per-developer staging slice - remote truth, local compute - gives everyone a private, high-fidelity environment without the maintenance tax.

Every team eventually builds a shared preview environment, and every team eventually resents it. It drifts from production, one bad deploy blocks ten people, and keeping it honest becomes somebody’s unpaid second job. The problem isn’t the idea of a preview - it’s the word shared.

A remocal environment - remote truth, local compute - flips the model. Each developer gets their own slice of staging on their own machine, pulling shared state from a control plane but running the service under test locally. Private blast radius, near-production fidelity, almost no standing infrastructure to babysit.

Why shared previews rot

A shared preview node fails for structural reasons, not because anyone was careless:

  • Contention. One broken migration or crashed service blocks everyone pointed at that node.
  • Drift. The preview accretes manual fixes until it resembles neither production nor a fresh checkout.
  • Cost. Keeping a full always-on stack per team is expensive, so teams under-provision and fidelity suffers.

The pattern is the same one concurrency control has fought for decades: shared mutable state with no isolation forces you to serialize. The fix is also the same - give each reader a consistent snapshot instead of a lock on the one real thing.

The slice

The core move is to split state from compute. Shared, slow-changing truth (service registry, a seeded dataset, secrets) stays remote in a control plane. The fast-changing thing you’re actually editing runs on your laptop, wired into that truth.

Fig. 1A remocal slice reads shared truth from the control plane but runs the service under test locally, so one developer's changes never touch another's slice.

Because the slice is per-developer, there is no contention: your broken migration breaks only your slice. Because truth is pulled fresh from the control plane, drift has nowhere to accumulate.

What a slice looks like

At its simplest, a slice is a small compose file that runs the service under test plus a thin proxy that resolves everything else to the shared control plane.

remocal.compose.yaml
services:
service-under-test:
build: .
environment:
# Unresolved dependencies fall through to the shared control plane
REGISTRY_URL: ${CONTROL_PLANE_URL}/registry
DATASET_URL: ${CONTROL_PLANE_URL}/seed
SECRETS_URL: ${CONTROL_PLANE_URL}/secrets
ports:
- "8080:8080"

Standing a slice up is then a single command per developer - no ticket, no shared node to reserve:

Start your slice
export CONTROL_PLANE_URL="https://staging-control.internal"
docker compose -f remocal.compose.yaml up

The one line that does the real work is the fall-through: any dependency the slice doesn’t run locally is resolved against shared truth instead of a hand-maintained mock.

The trade-offs worth naming

Remocal is not free - it just moves the cost somewhere healthier:

  • Read isolation, not write isolation. If slices share one dataset, writes can still collide. A per-slice copy-on-write dataset (the same instinct as MVCC snapshots) removes the last shared-state hazard.
  • The control plane is now load-bearing. You’ve traded many fragile preview nodes for one control plane that must stay healthy. That’s a better problem, but it’s still a problem.
  • Local resource ceiling. A laptop can run a slice, not the whole company. The split only works if most dependencies stay remote.

The through-line

Strip away the tooling and remocal is an isolation argument, not a DevOps one. Shared mutable state forces serialization; snapshots buy you concurrency. It’s the same shape whether the “readers” are database transactions, CPU cores reading a cache line, or engineers who each want a staging environment that is theirs.

That’s the kind of connection this site is for - and in a later lab, we’ll stand up a real slice with a copy-on-write dataset and watch two developers work without ever touching each other’s state.