Running a sweep on a GCP Spot VM (startup and teardown)
This runbook covers spinning up a cheap, throwaway Google Cloud VM to run a batch sweep (e.g. the intrinsic-goals selection-pressure sweep), pulling the results back, and deleting the VM so it stops billing. It is written for a one-off CPU batch job: many tiny, independent, single-threaded simulations with no GPU.
The concrete values below (agent-farm-experiments, us-central1-a,
agentfarm-sweep) are the ones this project has used; substitute your own
project/zone/name as needed.
Cost / shape at a glance
n2-standard-8Spot inus-central1is roughly $0.10/hr, so a full sweep is well under $1.- A stopped VM still bills for its boot disk, which is why teardown (deleting, not just stopping) matters.
- Each simulation is effectively single-threaded (many tiny DQNs), so pin BLAS threads to 1 and parallelize across processes (one per pressure level).
0. One-time local setup
gcloud auth login
gcloud config set project agent-farm-experiments
gcloud services enable compute.googleapis.com
gcloud config set compute/zone us-central1-a
If services enable fails with a billing error, link a billing account under
Billing -> Link a billing account in the console, then re-run.
1. Start the VM (Spot, no GPU)
The repo targets Python 3.12, so use an Ubuntu 24.04 LTS image (its default
python3 is 3.12).
gcloud compute instances create agentfarm-sweep \
--machine-type=n2-standard-8 \
--provisioning-model=SPOT \
--instance-termination-action=STOP \
--image-family=ubuntu-2404-lts-amd64 \
--image-project=ubuntu-os-cloud \
--boot-disk-size=20GB \
--boot-disk-type=pd-balanced
Verify it is up:
gcloud compute instances list --format='table(name,zone,status,machineType)'
Connectivity gotcha: plain
gcloud compute ssh/scpmay hang or time out on this project (no public-ingress SSH path). If that happens, add--tunnel-through-iapto everyssh/scpcommand. Examples below show the IAP form. IAP tunneling requires the caller to have theroles/iap.tunnelResourceAccessorrole.
2. Provision the environment on the VM
gcloud compute ssh agentfarm-sweep --zone=us-central1-a --tunnel-through-iap --command='
set -e
sudo apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git python3.12-venv python3-tk
[ -d AgentFarm ] || git clone --depth 1 https://github.com/Dooders/AgentFarm.git
cd AgentFarm
[ -d venv ] || python3 -m venv venv
source venv/bin/activate
pip install -q --upgrade pip
pip install -q -r requirements.txt
pip install -q -e .
python -c "import farm; print(\"farm ok\", farm.__file__)"
echo SETUP_COMPLETE
'
3. Push local-only scripts (if not yet on GitHub)
The VM cloned main from GitHub. Any changes that only exist locally (for
example new/edited sweep or runner files) must be copied over before running:
gcloud compute scp --tunnel-through-iap \
scripts/run_intrinsic_goals_pressure_sweep.py \
scripts/analyze_intrinsic_goals_pressure_sweep.py \
farm/runners/intrinsic_goals_experiment.py \
agentfarm-sweep:~/AgentFarm/ --zone=us-central1-a
Adjust the file list to whatever you changed. Mirror the repo layout on the
destination (scripts under ~/AgentFarm/scripts/, package files under their
package path). When in doubt, scp the specific edited files into their exact
paths.
4. Launch the sweep (detached, survives SSH drops)
Run the three pressure levels as parallel processes so the wall time is set by the slowest single level, not the sum. Use a small launch script uploaded to the VM to avoid SSH quoting pitfalls.
Create start_sweep.sh locally:
cat > /tmp/start_sweep.sh << 'EOF'
#!/bin/bash
set -euo pipefail
cd ~/AgentFarm
source venv/bin/activate
export OMP_NUM_THREADS=1 MKL_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 NUMEXPR_NUM_THREADS=1
mkdir -p experiments
echo "[$(date -Iseconds)] starting 3-pressure sweep" | tee experiments/sweep_master.log
for sp in low medium high; do
python scripts/run_intrinsic_goals_experiment.py \
--num-steps 600 --seed 42 --num-replicates 20 \
--selection-pressure "$sp" --max-population 3000 \
--output-dir "experiments/intrinsic_goals_sweep_${sp}" --log-level WARNING \
> "experiments/sweep_${sp}.log" 2>&1 &
echo "[$(date -Iseconds)] launched $sp pid $!" | tee -a experiments/sweep_master.log
done
wait
echo "[$(date -Iseconds)] all pressures done; analyzing" | tee -a experiments/sweep_master.log
python scripts/analyze_intrinsic_goals_pressure_sweep.py --sweep-dir experiments \
>> experiments/sweep_master.log 2>&1
echo "[$(date -Iseconds)] DONE" | tee -a experiments/sweep_master.log
EOF
Upload and start it detached:
gcloud compute scp --tunnel-through-iap /tmp/start_sweep.sh \
agentfarm-sweep:~/start_sweep.sh --zone=us-central1-a
gcloud compute ssh agentfarm-sweep --zone=us-central1-a --tunnel-through-iap --command='
chmod +x ~/start_sweep.sh
nohup ~/start_sweep.sh >> ~/AgentFarm/experiments/nohup.out 2>&1 </dev/null & disown
sleep 3
pgrep -af "start_sweep|run_intrinsic_goals" || echo NO_PROCS
cat ~/AgentFarm/experiments/sweep_master.log 2>/dev/null || echo no_master_yet
'
Each pressure level now runs 3 arms (uniform, shared, unique) x 20
replicates x 600 steps. Budget roughly 3-5 hours wall time for the full job;
analyze_... runs automatically at the end.
5. Monitor progress
gcloud compute ssh agentfarm-sweep --zone=us-central1-a --tunnel-through-iap --command='
tail -3 ~/AgentFarm/experiments/sweep_master.log
pgrep -c -f run_intrinsic_goals_experiment.py
ls ~/AgentFarm/experiments/intrinsic_goals_sweep_*/intrinsic_goals_summary.json 2>/dev/null || echo summaries_not_ready
'
The job is finished when DONE appears in sweep_master.log and
combined_comparison.md / intrinsic_goals_pressure_sweep.png exist under
experiments/.
6. Pull results back to your local machine
gcloud compute scp --recurse --tunnel-through-iap \
agentfarm-sweep:~/AgentFarm/experiments ./gcp-results --zone=us-central1-a
7. Teardown (do this, or you keep paying)
Delete the VM (removes the instance and its boot disk):
gcloud compute instances delete agentfarm-sweep --zone=us-central1-a --quiet
Confirm nothing is left running/billing:
gcloud compute instances list
gcloud compute disks list
Gotchas
- IAP tunnel: if
ssh/scphangs, use--tunnel-through-iapon every command (see step 1). Add connect timeouts so a flaky tunnel fails fast instead of hanging:--ssh-flag='-o ConnectTimeout=15'forssh, and--scp-flag='-o ConnectTimeout=15'forscp(scpdoes not accept--ssh-flag; it errors with a usage message). -
Duplicate launches: a launch attempt that looks hung (tunnel stalled before echoing output) may still have started the sweep on the VM. Before retrying, count runners and kill any strays:
gcloud compute ssh agentfarm-sweep --zone=us-central1-a --tunnel-through-iap \ --command='pgrep -cf "[r]un_intrinsic_goals_experiment.py"'Expect exactly one process per pressure level. If you must kill and restart, also
rm -rf ~/AgentFarm/experimentsso partial artifacts from the aborted run don’t mix with the fresh one. pkillkills your own SSH session: the remote command line itself contains the pattern, sopkill -f start_sweep.shover SSH matches (and kills) the shell running it, and the connection dies with exit 255. Use a self-excluding bracket pattern:pkill -f "[s]tart_sweep.sh".- Wall time scales with the population cap: with
--max-population 3000a 600-step sim runs ~1.5-1.9 s/step (~15 min/sim), so 60 sims per pressure (20 replicates x 3 arms) is roughly 12-16 hours, not the 3-5 hours the small-cap pilot suggested. Cost is still only ~$1-2 on Spot. - Spot preemption: if the VM is preempted mid-run it STOPs. Restart with
gcloud compute instances start agentfarm-sweep --zone=us-central1-a, re-SSH, and re-run step 4 — at most the interrupted pressure level is redone. - Stopped != free: a stopped VM still bills for its disk. Always delete (step 7) when done rather than leaving it stopped.
- Thread pinning: always export
OMP_NUM_THREADS=1(and the MKL/OpenBLAS/ NumExpr equivalents). Oversubscribed BLAS threads slow these many-tiny-model runs down. - Fully hands-off variant: attach a
--metadata-from-file startup-script=...that clones, installs, runs the sweep, uploadsexperiments/to a GCS bucket, thenshutdown -h now, so you never pay for idle. Harder to debug on the first run, cheapest thereafter.