DEV Community

Sam Yang
Sam Yang

Posted on

A Snapshot Is Not a Rebuild: Five Myths About Free Dev Boxes

The script passes on the machine where you wrote it and fails on the machine you borrowed, and the traceback rarely names the real difference. Most teams respond by pinning one more version in requirements.txt, rerunning, and calling it fixed when the color turns green. That habit works often enough to feel like engineering, which is exactly why the underlying myths survive contact with production. This piece takes five claims developers repeat about free or borrowed development boxes and tests each one against a concrete artifact you can run this week.

Myth 1: The same base image means the same box

Two instances built from the same image can still disagree about the C library, the locale, the timezone database, and the global package tree. A wheel compiled against a newer glibc will install on one host and silently resolve to a source build on another, and sorting behavior changes the moment LC_ALL moves from C to en_US.UTF-8. Neither difference announces itself in a stack trace, because both are environmental facts rather than code paths. The image is a starting point, not a fingerprint.

Myth 2: Pinning your direct dependencies is enough

Pinning top-level versions constrains the graph you declared and says nothing about the graph your package manager chose. Transitive resolution depends on the resolver version, the index state at install time, and sometimes on the CPU architecture of the box doing the install. You can prove this cheaply by hashing pip freeze output on two supposedly identical hosts and comparing the digests. If the digests differ while the pins match, the pins were never the contract you thought they were.

Myth 3: A snapshot is a backup of the environment

A snapshot restores a disk, which is useful, and it does not restore the reasoning that produced the disk. Six weeks later nobody remembers why libssl was downgraded by hand at 2 a.m., and the snapshot faithfully reproduces an accident along with the fix. The rebuildable artifact is the script, not the image, because the script is reviewable in a diff and the image is not. Free boxes make this worse by disappearing on someone else's schedule, so the only durable asset you own is the recipe.

The artifact: a manifest, a noise filter, and a diff

The drill is small enough to fit in a repository and boring enough to run on every dependency change. Start by emitting a sorted fingerprint of the box, then strip the fields that legitimately vary between hosts, then diff one fingerprint against another.

#!/usr/bin/env bash
# manifest.sh — emit a stable fingerprint of a dev box
set -euo pipefail
out="${1:-manifest.txt}"
{
  echo "## kernel";  uname -srm
  echo "## distro";  grep -E '^(ID|VERSION_ID)=' /etc/os-release | sort
  echo "## libc";    (ldd --version 2>/dev/null || true) | head -n1
  echo "## python";  command -v python3 >/dev/null && python3 -VV
  echo "## node";    command -v node   >/dev/null && node --version
  echo "## pip-digest"; command -v pip3 >/dev/null && pip3 freeze 2>/dev/null | sort | sha256sum
  echo "## npm-global"; command -v npm >/dev/null && npm ls -g --depth=0 --parseable 2>/dev/null | sort
  echo "## locale";  locale 2>/dev/null | sort
  echo "## tz";      cat /etc/timezone 2>/dev/null || date +%Z
  echo "## limits";  ulimit -a | sort
  echo "## cpu";     nproc; grep -m1 'model name' /proc/cpuinfo 2>/dev/null || true
  echo "## env";     env | grep -E '^(LANG|LC_|TZ|PYTHONHASHSEED|NODE_OPTIONS)=' | sort
} > "$out"
echo "wrote $out ($(wc -l < "$out") lines)"
Enter fullscreen mode Exit fullscreen mode

The interesting part is what you throw away before comparing, because a noisy diff trains people to ignore diffs. Hostnames, inode counts, load averages, and core counts all vary legitimately between a laptop and a small remote box, and none of them explain a test failure. Filter them explicitly rather than eyeballing the output.

# strip sections whose values are expected to vary across hosts
strip_volatile() {
  awk '/^## (kernel|cpu|limits)$/ {skip=1; next} /^## / {skip=0} !skip' "$1"
}

strip_volatile a.txt > a.norm
strip_volatile b.txt > b.norm
diff -u a.norm b.norm | tee drift.diff || true
test ! -s drift.diff && echo "OK: no drift outside filtered sections"
Enter fullscreen mode Exit fullscreen mode

Run manifest.sh on the box that works, provision a clean free box, replay your bootstrap.sh, and run manifest.sh again there. The diff is the entire deliverable: every line in it is either a fact your bootstrap forgot to pin or a field your filter should have dropped. Commit the diff alongside the fix, because the diff is the bug report you will want next quarter.

Myth 4: If it ran twice, it is reproducible

Running twice on the same host tests almost nothing, since the second run inherits every mutation the first one made. Reproducibility is a property of the pair (recipe, clean host), and the clean host is the hard half of that pair. This is where a free server option earns its place, not as a deployment target but as disposable verification capacity you are willing to destroy. A box you can throw away is more scientifically useful than a box you are afraid to touch.

Myth 5: The model can repair the environment for you

Free model access is genuinely useful for the tedious half of this loop, which is turning a drift diff into a first-draft bootstrap.sh and explaining why a package was pulled in. It is not useful as an authority on what your box should contain, because it cannot see the host and will happily invent a plausible apt-get line. I use a three-strike rule for generated setup scripts: each line must name the package it satisfies, map to a diff hunk or a manifest entry, and survive one clean replay. Lines that fail the rule get deleted rather than debugged.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. In this workflow I use its free model access to draft and repair the bootstrap script described above, and its free server option as the throwaway clean host that the drill rebuilds into; both are operator-supplied availability claims, so check the project page for current limits before you plan around them. The article stands on its own without either, since the manifest and the diff are plain shell.

When the drill is worth the hour

Situation Run the drill? Why
Pure-Python service, lockfile committed Once per quarter Catches resolver drift cheaply
Native extensions or CUDA wheels Every dependency bump glibc and driver drift dominate
Data or state lives on the box No — fix that first Rebuild cannot restore data
Regulated audit requirements No Use signed, attested images instead
Teaching juniors about environments Yes, once The diff teaches faster than prose

Limitations and who should skip this

The drill verifies environment shape, and it says nothing about data durability, secret handling, or whether the free box will exist tomorrow. Free tiers change their limits and availability windows without notice, so treat any borrowed host as a cache and never as a system of record. If your team already ships pinned container images through an attested pipeline, you have solved this problem with better tooling and should not add shell scripts for symmetry. Individuals writing one-off analysis scripts with no dependencies should skip it too, because there is nothing to rebuild.

The corrected mental model is unglamorous: a box is a claim, and a rebuild is the evidence. Keep the manifest in the repository, keep the diff in the pull request, and let the second host be the reviewer that never gets tired. If you want to try the loop without provisioning anything, run the two scripts on the machine in front of you and read what the filter removes — that list is usually longer than expected.

Top comments (0)