Dev.to AI πŸ€– Ai πŸ‘ 0 πŸ“– 6 min read

What a Free Server Should Never Be Asked to Remember

A free host is not dangerous because it goes down. It is dangerous because it forgets, quietly, and the application keeps serving responses that look correct right up until someone asks where last Tuesday's record went.

A free host is not dangerous because it goes down. It is dangerous because it forgets, quietly, and the application keeps serving responses that look correct right up until someone asks where last Tuesday's record went. Downtime announces itself. Data loss mails you a support ticket three weeks later.

So the useful question about a free server option is not uptime. It is this: if the process disappears and comes back, which of your assumptions die with it? That question can be answered in about fifteen minutes with a script, and the answer is usually more interesting than any benchmark.

Restart is the only test that matters

Deploys, evictions, host migrations, and operator maintenance all present to your application as the same event β€” the process is gone, then it is back. A free host that advertises persistence still has to survive that event, and a host that advertises nothing may survive it anyway. Marketing copy is not evidence. A round trip is.

The drill below uses the smallest application that can be embarrassed by a restart: a counter written to disk.

# app.py β€” smallest thing a restart can embarrass
import json, os, pathlib
from fastapi import FastAPI

STATE = pathlib.Path(os.environ.get("STATE_DIR", "./state")) / "counter.json"
app = FastAPI()

def read():
    if not STATE.exists():
        return {"n": 0}
    return json.loads(STATE.read_text())

@app.post("/bump")
def bump():
    s = read()
    s["n"] += 1
    STATE.parent.mkdir(parents=True, exist_ok=True)
    STATE.write_text(json.dumps(s))
    return s

@app.get("/state")
def state():
    return read()

Run it, bump once, restart the process by hand, and read the counter. If it comes back as one, the write path survives a cold start on that host. If it comes back as zero, the file lives somewhere that does not outlive the process β€” a container layer, a tmpfs, a per-instance scratch directory. That single fact determines whether the host can hold any state at all.

The harness

The manual version proves a point once. A harness proves it every time you change something, which is the version you actually need, because the property you are testing is a property of the host, and hosts are not stable facts.

#!/usr/bin/env bash
# restart_drill.sh β€” falsifies "this host keeps state", never proves it
set -euo pipefail
BASE_URL="${1:?usage: restart_drill.sh BASE_URL}"
RESTART_CMD="${RESTART_CMD:?set RESTART_CMD, e.g. 'ssh host systemctl restart demo'}"

probe() { curl -fsS "$BASE_URL/state" | python3 -c 'import json,sys; print(json.load(sys.stdin)["n"])'; }

before=$(probe)
curl -fsS -X POST "$BASE_URL/bump" >/dev/null
after=$(probe)

eval "$RESTART_CMD"
sleep 3
recovered=$(probe)

echo "before=$before after=$after recovered=$recovered"
if [ "$recovered" -ge "$after" ]; then
  echo "PASS: state survived one restart"
else
  echo "FAIL: state lost on restart ($after -> $recovered)"
  exit 1
fi

Three runs of that script tell you more than an hour of reading documentation. Run it on day one. Run it again after the provider changes the runtime. Run it after you change where the file goes.

The harness has one honest limit: it falsifies durability, it never proves it. A pass means the write survived the restarts you happened to perform, not that the disk is replicated. Treat it as a filter that removes bad hosts, not a certificate for good ones.

The torn write

The interesting failure is not a clean restart. It is a kill during the write itself β€” the case where the process dies between truncate and flush, and the application comes back to a half-written file that parses ninety percent of the time and then does not.

# kill mid-write, repeatedly
for i in $(seq 1 20); do
  curl -fsS -X POST "$BASE_URL/bump" >/dev/null &
  sleep 0.0$((RANDOM % 9))
  eval "$RESTART_CMD"
  wait || true
done
probe || echo "FAIL: state unreadable after torn write"

The fix is not a better host. It is a write that lands atomically: write to a temporary file in the same directory, then rename. Rename is atomic on a POSIX filesystem, and the recovery path becomes "the old file or the new file", never a mixture. A host you do not control makes atomic writes mandatory rather than optional.

Time-keeping is state

Files are the obvious case and the easy one. The subtler state is time: session rows, scheduled jobs, lock files, "last checked" timestamps. A free host that drops to zero between requests will replay those timestamps, and a job that guards on "not run in the last hour" will run zero times because the record of the last run evaporated. That is not a bug in the scheduler. It is a bug in assuming the host remembers having done something.

This is where the free model access has a narrow, honest use. When a drill fails, the output is a pile of probe lines and restart timestamps, and asking a model to classify those lines into "clean restart", "torn write", and "host recreated" is a decent use of a free tier β€” it runs after the incident, off the request path, on data the application already produced. It should not be the thing deciding whether to write, and it should not sit between the user and the response.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. The free server option and free model access referenced here are operator-supplied availability facts; the drill above is the part that produces evidence.

What goes where

| Workload | Free host verdict | Why |
| --- | --- |
| Static docs, build artifacts | Fine | Regenerable from source |
| Cron that must fire exactly once | No | No durable record of the last run |
| Session store | No, unless external | Restart wipes the identity mapping |
| Cache with a cold-start path | Fine | Loss is slow, not wrong |
| Queue buffer | No | A dropped message has no owner |
| Log shipper | Fine, if the sink is elsewhere | Local copy is disposable |
| Anything with a retention obligation | No | Compliance is not a restart property |

Exit criteria

Three triggers mean the host is the wrong place, and none of them are negotiable. First, a drill failure that you cannot fix within an hour by moving state off the host β€” if the fix needs write access you cannot obtain, you have already lost. Second, a second unexpected restart within a day that you did not cause, because that pattern predicts the third one landing inside a write. Third, any moment when a lost write would be a billing event or a customer-visible inconsistency rather than a slightly stale page.

Who should not use this

Teams with retention requirements should not put anything on a free host, and no drill changes that β€” durability you can test is not the same as durability you can attest. Teams that need sub-second cold starts should not use a free option either, because a process that has to boot is a process that has to boot. And teams that cannot obtain a restart hook on the host cannot run the harness at all, which means they are trusting a claim instead of a measurement.

For everyone else, the sequence is boring and short. Deploy the counter. Bump it. Restart. Read it. Then keep the harness in the repository next to the deploy script, so the next person who changes the runtime inherits the test rather than the assumption.

If you try MonkeyCode's free server option for the stateless half of your stack, run this drill first and let the result decide where the boundary sits.

πŸ“° Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.