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.

One Dedalus Machine per open PR. The PR’s branch is checked out, dependencies installed, services running. When nobody touches the preview URL for 5 minutes the VM sleeps. When the next reviewer opens the link, it wakes in under a second with the database, seed data, and log history intact.
npm install dedalus-labs

1. On PR opened: provision

import Dedalus from "dedalus-labs";
const dedalus = new Dedalus({ apiKey: process.env.DEDALUS_API_KEY! });

async function provisionPreview(prNumber: number, branch: string) {
  const m = await dedalus.machines.create({
    vcpu: 2,
    memory_mib: 4096,
    storage_gib: 20,
  });

  await runAndWait(m.machine_id, ["/bin/bash", "-c", `
    set -e
    apt-get update && apt-get install -y git nodejs postgresql
    git clone --depth 1 -b ${branch} https://github.com/your-org/your-app /root/app
    cd /root/app && npm install
    pg_ctlcluster 16 main start && createdb app && npm run migrate && npm run seed
    nohup npm run dev -- --host 0.0.0.0 --port 3000 >/var/log/app.log 2>&1 &
  `]);

  const port = await dedalus.machines.previews.create(m.machine_id, {
    port: 3000,
    protocol: "https",
    visibility: "org",   // or "public" for external reviewers
  });

  return { machineId: m.machine_id, url: port.url };
}

2. On webhook hit (or middleware): wake

The simplest pattern is a small router that proxies https://pr-123.previews.your-app.com to the machine’s preview URL. Before forwarding, wake the machine.
import http from "node:http";

http.createServer(async (req, res) => {
  const prNumber = parseInt(req.headers["x-pr-number"] as string);
  const machineId = await lookupMachine(prNumber);   // your DB

  const m = await dedalus.machines.retrieve(machineId);
  await dedalus.machines.wake(machineId, { "If-Match": m.revision });
  // ...then proxy req → the preview URL stored at provision time.
}).listen(8080);
wake is idempotent and returns once the machine is running. If it was already running, it returns immediately. If it was sleeping, it returns when the snapshot has restored — typically under a second.

3. On PR merged or closed: destroy

async function teardownPreview(prNumber: number) {
  const machineId = await lookupMachine(prNumber);
  const m = await dedalus.machines.retrieve(machineId);
  await dedalus.machines.delete(machineId, { "If-Match": m.revision });
}

Why a microVM beats a container here

  • Postgres, Redis, real systemd, anything else that doesn’t survive container restart, just runs. Migrations + seed data persist across sleep/wake.
  • Sleep means zero compute cost while the PR is dormant. A 3-week-old PR costs only the storage GiB-month — not 24/7 uptime.
  • Wake under a second. Reviewers don’t notice they’re hitting a slept VM.
  • Idle auto-sleep is built in (idle_sleep_after_seconds, default 300s). If your wake-on-traffic proxy forgets to put the VM back to sleep, the platform does it for you after five idle minutes.

Notes

  • Set visibility: "public" if external collaborators need to reach the preview without a Dedalus account. Use "org" to gate it on org membership, or "private" for the creator only.
  • Wake latency depends on storage size. A 20 GiB machine wakes faster than a 200 GiB one.
  • A wake-on-cold-start race exists if many requests hit a sleeping VM simultaneously. The first wake call returns once the VM is up; subsequent calls coalesce. Your proxy should serialize per-machine.
Last modified on May 2, 2026