A weekend demo ships in one of two states: it works on the author's laptop, or it works from a cold start on the host where reviewers will open it. The gap between those two states is almost never the application code. It is three host properties that most build logs never measure.
Core conclusion: before a side project is called "done", measure cold-start time, disk durability, and idle/restart behavior on the actual host. Treat everything else — UI polish, extra endpoints, model choice — as optional.
The scope cut: three tiers, one weekend
The build log below is written so it can be replayed on any free-tier host. No product benchmark numbers appear in it, because free-tier behavior differs by region and changes without notice. The scripts are the artifact; the numbers are yours to fill in.
Tier assignment used for the build:
-
Tier 1 — must ship. One
/healthzendpoint, one read-only route, the cold-start probe, the durability probe, and a README with copy-pasteable commands. - Tier 2 — ship only if Friday's measurements allow. Seed fixtures, one write path behind an environment flag, a short demo script.
- Tier 3 — explicitly never this weekend. Auth, multi-tenant data, background workers, dashboards, and any migration of real data.
One rule keeps the tiers honest: adding a Tier 2 item requires removing a Tier 1 item or shortening the demo script by the same amount of time. Otherwise the tier list becomes a wish list with deadlines attached.
Why free hosts break demos in three specific ways
- Cold start after idle. A host that sleeps the process turns a 200 ms request into a multi-second one. The first reviewer to open the link pays that cost.
- Ephemeral disk. Files written at runtime disappear on restart. SQLite in a container without a mounted volume is the classic case.
- Unknown restart policy. Whether the supervisor restarts a crashed process, and how quickly, is usually undocumented on free tiers.
None of these are reasons to avoid a free host. They are reasons to measure before the demo depends on them.
Artifact 1: the cold-start probe
The probe clones the repository into a temporary directory, installs it, boots it, and polls until the health endpoint answers.
#!/usr/bin/env bash
# cold-start.sh <git-url>
# Measures: clean checkout -> first successful /healthz response.
set -euo pipefail
REPO="${1:?usage: cold-start.sh <git-url>}"
PORT="${PORT:-8080}"
BUDGET_MS="${BUDGET_MS:-0}" # 0 = measure only, no assertion
WORK="$(mktemp -d)"
git clone --depth 1 "$REPO" "$WORK/app" >/dev/null
cd "$WORK/app"
START=$(date +%s%3N)
if [ -f package.json ]; then
npm ci --silent
(PORT="$PORT" npm start >"$WORK/boot.log" 2>&1 &)
elif [ -f requirements.txt ]; then
python -m venv .venv
./.venv/bin/pip install -q -r requirements.txt
(PORT="$PORT" ./.venv/bin/python -m app >"$WORK/boot.log" 2>&1 &)
else
echo "no known manifest; add a branch for your stack"
exit 2
fi
until curl -sf "http://127.0.0.1:$PORT/healthz" >/dev/null 2>&1; do
sleep 0.2
done
END=$(date +%s%3N)
ELAPSED=$(( END - START ))
echo "cold_start_ms=$ELAPSED budget_ms=$BUDGET_MS"
if [ "$BUDGET_MS" -gt 0 ] && [ "$ELAPSED" -gt "$BUDGET_MS" ]; then
echo "FAIL: cold start over budget"
exit 1
fi
echo "PASS"
Run it three times locally and three times on the host, then set BUDGET_MS to roughly twice the slowest observed value. The assertion exists to catch regressions between Friday and Sunday, not to publish a speed claim.
Artifact 2: the durability probe
Durability is a yes/no question and takes about two minutes to answer. Gate the debug route behind an environment variable, and delete it after the measurement.
# Before a restart
curl -s -X POST "$BASE/debug/token" -d "value=$(date +%s)" -o before.txt
# Restart the service using the host's own control panel or CLI
curl -s "$BASE/debug/token" -o after.txt
if diff -q before.txt after.txt >/dev/null; then
echo "durable: state survived restart"
else
echo "ephemeral: treat the disk as scratch space"
fi
An ephemeral result does not kill the demo. It changes the design: the read path is served from a fixture baked into the image, and any runtime writes are declared in the README as lost-on-restart.
Artifact 3: the triage table
| Property | How to measure | If it fails | Ship the demo anyway? |
|---|---|---|---|
| Cold start |
cold-start.sh against the host |
Show a static warming-up page and ping before review | Yes, with a warm-up step in the runbook |
| Disk durability | Token probe across a restart | Serve fixtures from the image; no runtime writes | Yes, if the demo is read-only |
| Restart on crash | Kill the process via the host CLI | Add a manual restart step to the runbook | Yes, for a short review window |
| Outbound network | One curl to a known endpoint from the app |
Precompute anything fetched at runtime | Only if the demo has no live-data need |
The rule applied to the table: a failed property becomes a documented demo step, never an unstated assumption.
Where free model access fits
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode offers free model access and a free server option; both are operator-supplied availability claims here, and model names, quotas, regions, and terms can change, so verify current details on the project's own pages before planning around them. No quota figures, hardware specs, or benchmark results are stated in this article, because none were measured for it.
Free model access covers exactly three of the weekend's chores:
- Generating fixture variations from a small hand-written example.
- Drafting the README from the final diff, which a human then corrects.
- Triaging boot logs into cause and noise after a failed deploy.
It does not belong on the request path of a live demo. A demo that calls out to a model on every page load adds a network dependency to the one thing the reviewer will click. Keep the call count explicit instead:
MAX_MODEL_CALLS = 3 # chores only: fixtures, README draft, log triage
calls = 0
def ask(prompt: str) -> str:
global calls
if calls >= MAX_MODEL_CALLS:
raise RuntimeError('weekend model budget exhausted; do it by hand')
calls += 1
return client.complete(prompt) # placeholder: wire to any provider
The free server option is the natural host for a demo of this size, with one caveat: the probes above decide whether it can hold the demo, not a product page. Measure on the free tier first, then decide what the README promises.
What got skipped, on purpose
- Authentication. The demo has no users, so it has no user model.
- Persistence for writes. The read path carries the demo; writes stay in memory.
- CI beyond the two probes. The probes are the only tests that protect the review experience.
- A custom domain. A default hostname is one fewer thing to break on Sunday night.
Every item on that list belongs in the README under known gaps. A reviewer who finds an unlisted gap assumes the rest is guesswork too.
Limitations and who should not use this
- Demos that hold real user data need durability guarantees a free tier may not offer.
- Anything with an uptime or latency commitment to a customer needs a paid host with a stated SLA.
- Teams already running infrastructure should reuse it; adding a second platform for one weekend costs more than it saves.
- The durability probe writes and reads a debug route. Remove that route after measuring, and confirm the host's terms allow the traffic the probe generates.
Sunday-night runbook
- Run
cold-start.shagainst the deployed URL and record the number. - Restart the service, open the demo link, and confirm the first request path works cold.
- Walk the demo script out loud, once, end to end.
- Confirm every skipped item is listed in the README.
- Freeze the branch. No commits after step 5.
A weekend side project is not judged by how much it contains. It is judged by whether a stranger can open it on Monday and see the one thing it does. If that trade-off fits your next weekend, the free server option is a reasonable place to start — run the cold-start probe before writing the README, and let the measurement set the scope.
Top comments (0)