Skip to main content

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.

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.
npm install dedalus-labs
.env
DEDALUS_API_KEY=<your-dedalus-key>
MACHINE_ID=<your-machine-id>

1. Provision the machine once

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);
const fresh = await dedalus.machines.retrieve(m.machine_id);
await dedalus.machines.sleep(m.machine_id, { "If-Match": fresh.revision });

2. The job runner

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!;
  let m = await dedalus.machines.retrieve(id);
  await dedalus.machines.wake(id, { "If-Match": m.revision });
  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);
  m = await dedalus.machines.retrieve(id);
  await dedalus.machines.sleep(id, { "If-Match": m.revision });
}

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 (idle_sleep_after_seconds: 300). 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.
  • If-Match is a transitional requirement on mutations — see Lifecycle.
Last modified on May 2, 2026