DEV Community

Jordan Huang
Jordan Huang

Posted on

Idle SSH Is Not a Job Queue: Five Free-Runtime Myths

Can a free remote box replace your night laptop?

The price dropped, and coding agents got loud.
A set of runtime myths rushed into that gap.

This FAQ is about runtime, not model rankings.
I will take five claims I still hear.
Then I will hand you a probe to run.

Treat every snippet as a proposed check.
I am not selling a benchmark. I am selling a receipt.

Why this FAQ exists

Free models changed how we draft patches.
Free servers changed where those drafts execute.
Those two facts do not rewrite operating systems.

A cheap endpoint still needs a contract.
A cheap box still needs a job model.
Skip either one, and you debug ghosts later.

Want the short version before the myths?
Price is not persistence. SSH is not a queue.
A blinking cursor is not a finished job.

How to read each answer

Every question has three parts on purpose.

  • The claim people keep repeating in reviews
  • The evidence shape, without fake vendor numbers
  • The corrected mental model you can steal

Run the probe before you trust a free runtime.
If a claim needs quotas or hardware, I skip it.
Those details go stale. Contracts do not.

Q1. If the model is free, can I retry forever?

Claim people repeat: retries cost nothing, so loop forever.

Evidence shape: you still pay with wall clock.
You also pay with logs, locks, and duplicate side effects.
A hung loop pins the only box you have.

Did your tool call create a file already?
Then a second attempt is not harmless exploration.
It is a second writer on the same tree.

Corrected mental model: free inference is not infinite compute.
Give every agent job a budget envelope.
Count attempts. Cap wall time. Require idempotent tools.

Proposed envelope

# proposed: retry_envelope.py — unexecuted example
from dataclasses import dataclass
import time

@dataclass
class Envelope:
    max_attempts: int = 3
    deadline_s: float = 90.0

def run_with_envelope(job, env: Envelope):
    start = time.monotonic()
    last_err = None
    for attempt in range(1, env.max_attempts + 1):
        if time.monotonic() - start > env.deadline_s:
            raise TimeoutError(f"deadline hit on attempt {attempt}")
        try:
            return job(attempt)
        except Exception as err:
            last_err = err
    raise RuntimeError(f"attempts exhausted: {last_err}")
Enter fullscreen mode Exit fullscreen mode

Three attempts. Ninety seconds. Then fail loud.
That is a contract, not a hopeful vibe.

Q2. If the server is free, can disk keep state?

Claim people repeat: the box stayed up, so files remain.

Evidence shape: free runtimes are often reclaimable workers.
Disk can vanish. Home directories get recycled overnight.
Your still-open SSH session is not a backup policy.

Would you store the only copy of a patch there?
I would not. Git already exists for that job.
The remote home directory is scratch, full stop.

Corrected mental model: treat the free server as a cutting board.
Clone, run, collect receipts, then push artifacts out.
Never make that disk the source of truth.

What I copy out

  • a branch or a patch, never a dirty mystery tree
  • a receipt JSON with exit code and SHA
  • logs under a unique job id, nothing else
  • no secrets, no caches, no "I might need this"

If you cannot rebuild it from git, it is gone.
That rule saves more nights than any uptime myth.

Q3. Does a living SSH session mean health?

Claim people repeat: I can type, so the agent is fine.

Evidence shape: SSH liveness is a tty, not a supervisor.
The agent process can zombie behind a prompt.
A model call can hang on an open socket forever.

Are you watching a cursor, or a child exit code?
Those are different instruments. Do not mix them.
A healthy shell can host a dead job.

Corrected mental model: health is a receipt file.
You want a PID story, an exit code, and a checksum.
If those three are missing, nothing finished.

Receipt shape

{
  "job_id": "2026-09-05T18-00Z-refactor-auth",
  "git_sha": "REPLACE_WITH_REV_PARSE",
  "exit_code": 0,
  "attempts": 2,
  "artifact": "patches/auth.diff"
}
Enter fullscreen mode Exit fullscreen mode

No receipt, no merge. I do not argue with cursors.
Write the file even when the job fails.
Failure without a receipt is just folklore.

Q4. Is a free remote runner a substitute for CI?

Claim people repeat: it built on that box, so ship it.

Evidence shape: that box is one fingerprint, not a fleet.
Python version. Locale. Missing system libraries. Clock skew.
Your laptop and that runner will disagree on purpose.

Did you hash the lockfile, or only the commit?
The commit can match while the resolver drifts.
That is runtime drift, not a taste debate.

Corrected mental model: the free server is a canary worker.
CI remains the merge gate. Always.
The remote agent proposes a patch. CI disposes.

Drift checklist

  1. Print python -V and uname -a into the receipt.
  2. Hash the lockfile, not just git rev-parse HEAD.
  3. Run the same pytest node id you actually care about.
  4. Compare exit codes. Ignore chat summaries entirely.

A remote green build is still one machine talking.
One machine is a sample. CI is the contract.

Q5. Can I skip a queue because SSH is free?

Claim people repeat: I have the box, so jobs can share it.

Evidence shape: two agent runs will stomp the same tree.
They will fight the same port and the same log file.
Idle access is not mutual exclusion. It is a hallway.

What stops the second job from editing your patch?
If you cannot name the lock, you do not have one.
Courtesy between terminals is not a scheduler.

Corrected mental model: idle SSH is not a job queue.
Use one worktree per job id, then lock it.
Record the lease. Expire it. Do not reuse paths.

Cheap lock, proposed only

# proposed: lease a worktree, then run
JOB_ID="$(date -u +%Y%m%dT%H%M%SZ)-$RANDOM"
git worktree add "/tmp/jobs/$JOB_ID" HEAD
(
  flock -n 9 || { echo "lock failed"; exit 75; }
  python retry_envelope.py
  python write_receipt.py --job "$JOB_ID"
) 9>/tmp/jobs/$JOB_ID.lock
Enter fullscreen mode Exit fullscreen mode

Exit 75 means try another worker, not retry blindly.
That is a queue's poorest cousin, and it still helps.
Two agents editing one file is how myths start.

The artifact: a runtime probe

Here is the original artifact for this FAQ.
It is a proposed contract, not a performance study.
Save it as runtime_probe.py and run it twice.

Once on the laptop. Once on the free server.
Diff the fingerprints. Keep both receipts.

#!/usr/bin/env python3
"""Proposed runtime probe. Label: unexecuted example."""
from __future__ import annotations

import hashlib
import json
import os
import platform
import subprocess
import sys
import time
from pathlib import Path

RECEIPT_DIR = Path(os.environ.get("RECEIPT_DIR", "./receipts"))

def sh(cmd: list[str]) -> str:
    p = subprocess.run(cmd, check=False, capture_output=True, text=True)
    return (p.stdout or p.stderr).strip()

def fingerprint() -> dict:
    lock = Path("poetry.lock")
    if not lock.exists():
        lock = Path("package-lock.json")
    digest = ""
    if lock.exists():
        digest = hashlib.sha256(lock.read_bytes()).hexdigest()[:12]
    return {
        "python": sys.version.split()[0],
        "platform": platform.platform(),
        "cwd": str(Path.cwd()),
        "lock_sha": digest or "no-lockfile",
        "user": os.environ.get("USER", "unknown"),
        "scratch_writable": os.access("/tmp", os.W_OK),
    }

def write_receipt(job_id: str, exit_code: int, extra: dict) -> Path:
    RECEIPT_DIR.mkdir(parents=True, exist_ok=True)
    payload = {
        "job_id": job_id,
        "exit_code": exit_code,
        "git_sha": sh(["git", "rev-parse", "HEAD"]),
        "ts_unix": int(time.time()),
        **extra,
    }
    path = RECEIPT_DIR / f"{job_id}.json"
    path.write_text(json.dumps(payload, indent=2) + "\n")
    return path

def main() -> int:
    job_id = os.environ.get("JOB_ID", f"probe-{int(time.time())}")
    fp = fingerprint()
    home = Path.home()
    dirty = list(home.glob("**/.agent-uncommitted-*"))[:5]
    extra = {
        "fingerprint": fp,
        "stray_agent_files": [str(p) for p in dirty],
    }
    code = 0 if fp["scratch_writable"] and not dirty else 2
    path = write_receipt(job_id, code, extra)
    print(path.read_text())
    return code

if __name__ == "__main__":
    raise SystemExit(main())
Enter fullscreen mode Exit fullscreen mode

What did we actually prove with that script?

  • The box can write a receipt to a known path.
  • The lockfile hash is visible beside the git SHA.
  • Stray agent droppings fail the probe on purpose.

What did we refuse to prove?

  • Model quality. Uptime. Quota. Hardware. Permanence.

Those belong in vendor docs, not in a FAQ myth.

Decision table

Use this before you send an agent off the laptop.

Situation Local laptop Free remote server Real CI
Explore a refactor with a free model Yes Yes, scratch only No
Keep a dirty worktree overnight Only if you accept the risk No No
Merge gate No No Yes
Two jobs at once Separate worktrees Worktrees plus a lock Isolated jobs
Secrets Local store Inject per job, then shred Vault or OIDC
"It worked in chat" Not evidence Not evidence Still not evidence

If a cell says No, do not negotiate with it.
Negotiation is how idle SSH becomes a queue myth.

A bounded workflow that uses the free pairing

Here is the loop I recommend as a proposal.
It assumes free model access and a free server option.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode is one place that pairing exists today.
Use that pair, or another pair. The contract stays identical.

  1. Freeze a job id and a git SHA on the laptop.
  2. Push the branch. Do not rsync a dirty tree.
  3. Lease a worktree on the free server.
  4. Run the agent against the free model endpoint.
  5. Cap attempts with the envelope above.
  6. Write a receipt. Copy the patch home.
  7. Delete the worktree. Do not save souvenirs.
  8. Let CI replay the patch on a known image.

Step 8 is not optional, even for tiny diffs.
The free server did exploration. CI still owns truth.

Commands as a template, not a memoir

JOB_ID="$(date -u +%Y%m%dT%H%M%SZ)-auth"
git switch -c "agent/$JOB_ID"
git push -u origin "HEAD"
# on the free server, after clone:
git fetch origin "agent/$JOB_ID"
git worktree add "/tmp/jobs/$JOB_ID" "origin/agent/$JOB_ID"
RECEIPT_DIR="/tmp/jobs/$JOB_ID/receipts" JOB_ID="$JOB_ID" python runtime_probe.py
# run the agent here, then:
git -C "/tmp/jobs/$JOB_ID" diff > "$HOME/out/$JOB_ID.diff"
# back on the laptop
git apply --check "$HOME/out/$JOB_ID.diff"
Enter fullscreen mode Exit fullscreen mode

git apply --check is your first adult in the room.
If that fails, the remote success was costume jewelry.
Do not debug the costume. Debug the patch.

Limitations

This FAQ does not rank models against each other.
It does not promise a free server will stay yours.
It does not replace tracing, metrics, or a worker pool.

The probe ignores GPUs, rate limits, and provider outages.
I also do not know your threat model or your audit needs.
A free shared runtime may be wrong for secrets.

If the job touches production data, stop immediately.
If you need multi-hour training, pick another shape.
If your org bans unknown runners, respect that ban.

Who should not use this approach

Skip this if you already have durable workers.
Skip this if a real queue already isolates every job.
Skip this if the agent must hold PCI or health data.

Skip this if you cannot write a receipt on failure.
Skip this if you need sub-minute runtime SLOs.
Skip this if nobody can explain where state lives.

The free path is for bounded exploration only.
It is not a night-shift operator standing watch.
It is not a merge gate wearing a cheaper jacket.

The corrected model in four questions

Ask these before every remote agent run.

  1. What is the job id, in one string?
  2. Where does state live after SSH dies?
  3. What receipt proves done, not chatting?
  4. Which CI job will replay the patch?

If you cannot answer, you are sightseeing.
Sightseeing is fine. Merging sightseeing is not.

Free models help you draft a change set.
Free servers help you isolate that draft.
Neither one owes you persistence or a queue.

You bring the envelope, the lock, and the receipt.
That is the whole myth, with the romance removed.

Top comments (0)