> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dedaluslabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Cron on a Sleeping VM

> Replace Lambda cron with a real Linux box that wakes on schedule, runs the job, sleeps.

A scheduled job that wakes a Dedalus Machine, runs your command, sleeps. You pay per-second of compute (only while the job runs) and storage stays warm between runs — your `~/.cache`, model weights, and installed packages persist.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npm install dedalus-labs
```

```env .env theme={"theme":{"light":"github-light","dark":"github-dark"}}
DEDALUS_API_KEY=<your-dedalus-key>
MACHINE_ID=<your-machine-id>
```

## 1. Provision the machine once

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import Dedalus from "dedalus-labs";
const dedalus = new Dedalus({ apiKey: process.env.DEDALUS_API_KEY! });

const m = await dedalus.machines.create({ vcpu: 1, memory_mib: 1024, storage_gib: 10 });

// One-time setup: install whatever your job needs, persists forever.
await runAndWait(m.machine_id, ["/bin/bash", "-c",
  "apt-get update && apt-get install -y postgresql-client rclone && rclone config",
]);

console.log("MACHINE_ID=" + m.machine_id);
await dedalus.machines.sleep(m.machine_id);
```

## 2. The job runner

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
async function runAndWait(machineId: string, command: string[], timeoutMs = 600_000) {
  const exc = await dedalus.machines.executions.create(machineId, { command, timeout_ms: timeoutMs });
  let status = exc.status;
  while (status !== "succeeded" && status !== "failed") {
    await new Promise((r) => setTimeout(r, 500));
    const cur = await dedalus.machines.executions.retrieve(machineId, exc.execution_id);
    status = cur.status;
  }
  return dedalus.machines.executions.output(machineId, exc.execution_id);
}

async function runCron() {
  const id = process.env.MACHINE_ID!;
  await dedalus.machines.wake(id);
  const out = await runAndWait(id, ["/bin/bash", "-c",
    "pg_dump $DATABASE_URL | gzip | rclone rcat backup:nightly/$(date -I).sql.gz",
  ], 300_000);
  console.log(out.stdout);
  await dedalus.machines.sleep(id);
}

runCron().catch((e) => { console.error(e); process.exit(1); });
```

Run this script from any cron source: GitHub Actions on a schedule, EventBridge, your own k8s `CronJob`, even another sleeping VM running cron.

## When this beats Lambda

* **No 250 MB layer hacks.** Run anything `apt-get` installs.
* **No 15-minute timeout.** Set `timeout_ms` to whatever the job needs.
* **Stateful caches.** `~/.cache/huggingface` (40 GB of weights), `~/.cargo`, build artifacts — all persist between runs without an S3 round-trip.
* **Per-second billing while awake**, zero while sleeping. A 4-second job costs 4 seconds.
* **Real Linux.** systemd services, persistent `crontab -e` if you'd rather schedule inside the VM, full process tree.

## Notes

* **`timeout_ms` on executions** kills the process if it overruns. Without it, executions inherit a generous platform default — set it explicitly for cron.
* **Auto-sleep** is on by default (`autosleep: "5m"`). Even if your script forgets to call `sleep()`, the machine slips back to zero cost five minutes later.
* **Schedule the wake-up itself outside the VM.** A sleeping VM can't wake itself; you need an external scheduler to call `wake` on the machine ID.
