Skip to content

Daytona

The @beachside/daytona package runs Beachside sandboxes on Daytona through its REST API, with no native SDK dependency. Install it with npm install beachside @beachside/daytona; it is a supported provider. Use Node 20+ or Bun.

import { Image, Beachside } from "beachside";
import { daytonaAdapter } from "@beachside/daytona";
const client = await Beachside.connect({
adapter: daytonaAdapter,
credentials: { apiKey: process.env.DAYTONA_API_KEY! },
config: {
target: "us",
environment: Image.prepared("daytona-small"),
lifecycle: { lifetimeSeconds: 600 },
network: "daytona-default",
},
});
try {
const box = await client.sandboxes.create();
try {
await box.exec(["/bin/sh", "-c", "printf ready"]);
} finally {
await box.destroy();
}
} finally {
await client.close();
}

Set DAYTONA_API_KEY on the server. connect reads the key’s organization and checks that the target region is available to it. Everything is scoped to that organization, region and network setting; a rotated key for the same organization works with saved references.

The package also exports daytona(), which takes credentials and config in one object and returns an adapter you pass straight to Beachside.connect: Beachside.connect(daytona({ apiKey, target: "us", network: "daytona-default" })). It is the same setup; use whichever reads better.

Setting Default Notes
apiKey required Daytona API key.
target required Daytona region id, such as us.
environment none Default image for create(): a prepared Daytona snapshot (Image.prepared(...)) or an OCI image. A per-call environment wins. With neither, create() rejects INVALID_ARGUMENT.
network daytona-default daytona-default, internet or blocked. See Network policy. Deprecated alias: networkPolicy; setting both rejects.
lifecycle.lifetimeSeconds 900 Lifetime at create and the default renew() window. Rounded up to whole minutes. No adapter maximum. create({ lifecycle }) can override it per sandbox. Deprecated alias: ttlMinutes (set one, not both).
lifecycle.idleSeconds off Stop the sandbox after this many idle seconds, rounded up to minutes, up to 86,400. Off means Daytona’s native 15-minute idle stop is disabled.
lifecycle.suspension.preserve class default filesystem stops a Linux VM instead of pausing it. filesystem+memory makes containers reject UNSUPPORTED before any change.
preview.access protected public rejects UNSUPPORTED at connect, because Daytona would publish every port of the sandbox.
snapshots.restartAfterCapture true Restart a running source after snapshot(). false leaves it stopped.
snapshots.preserve filesystem Preserve.FILESYSTEM_AND_MEMORY takes hot snapshots of running Linux VMs. Containers and stopped VMs reject before any change.
resources none Minimum { vcpus, memoryMiB }. OCI builds use it; a prepared snapshot must already meet it.

Create-time env is sent as native sandbox environment variables, so it applies to every command, including after reopening; per-call env overrides a name. Values are not stored in references and are redacted from Daytona error text. Create-time labels are supported. An invalid image override never falls back to the default or starts an unrequested build.

Feature Support Live tested
Sandbox lifecycle Create, inspect and destroy. Destroy waits for confirmed deletion. Yes
Reconnect later sandboxes.get(reference) reopens the same sandbox without starting or extending it. Yes
Lifetime renewal renew() resets the deadline, rounded up to whole minutes. Yes
Pause and resume Native stop/start. Files are kept; processes are not. Yes
Commands argv and shell, exact stdout/stderr bytes up to 1 MiB per stream, deadline default 300 seconds. Yes
Streaming and terminals processes.start(), signals, terminals and reattaching after reconnecting. Yes
Command input Finite byte stdin. No
Files Binary read and write up to 16 MiB, overwrite and no-clobber, plus byte streams. Yes
Directories List, stat, create, remove, copy and move. Yes
HTTP previews Protected only, with an x-daytona-preview-token header. Public previews are not supported. Yes
Images Prepared snapshots and OCI builds; images.get, list and delete over native snapshots. No
Network controls daytona-default, internet or strict blocked, set once per connection. No
Snapshots Filesystem capture and restore into a new sandbox. Yes
Volumes and mounts Create, delete and mount object-backed volumes, including at restore. Yes
Listing sandboxes and images sandboxes.list() returns Beachside-created sandboxes only; images.list() returns all organization snapshots. No

Live results cover the tested image, region and network setting; see Provider support. Live runs used the prepared daytona-small snapshot in us with daytona-default; other snapshots are not qualified.

The policy is set once on the connection and is part of its scope. A per-call policy must match it; open another connection to use a different one. There is never a fallback.

  • daytona-default keeps Daytona’s organization-managed egress rules. On lower tiers this still allows some essential services, so it is neither unrestricted nor strictly blocked.
  • internet asks for unrestricted egress. It needs an organization without limited sandbox egress (Tier 3 and above); otherwise create rejects before anything is allocated.
  • blocked asks for strict no egress. Connect must verify the organization supports native block-all; if the check is restricted or unreadable, creation is unavailable.

Earlier releases defaulted to blocked. To reopen sandboxes created under that default, connect with network: "blocked". See Images and networking.

Start from an active, Linux snapshot available to your organization in the target region. Public (general) snapshots can be used but are never deleted by Beachside.

The image needs /bin/sh, mktemp, mkfifo, cat, wc, head, od and rm for command output, plus mkdir, rmdir, mv, copy and link utilities for file writes. Atomic no-clobber writes need ln -T and hard links. Directory operations and streaming processes need Python 3; no-clobber rename also needs Linux libc renameat2. Streaming needs writable private /tmp. Nothing is installed for you.

images.list() reports every snapshot in the organization; general ones show as public and ownership is unknown. images.delete() refuses general snapshots, other organizations’ snapshots, snapshots captured from a sandbox (use snapshots.delete()), and any snapshot an organization warm pool uses or when warm pools cannot be read, because Daytona’s delete cascades to them. See Images and networking.

The lifetime is a hard wall-clock deadline. When it passes, Daytona deletes the sandbox, even while it is stopped or archived, so saved files can be lost. renew() sets a new deadline from now, which may shorten a longer one; it is not additive. renew({ forSeconds: 61 }) becomes 120 seconds. Renewal and preview access need a running sandbox.

Idle stop is separate from the lifetime and renewing does not disable it. Daytona natively stops sandboxes after 15 idle minutes, even with background processes running; the adapter turns that off unless you set lifecycle.idleSeconds. inspect() reports the policy as idleStop.

suspend() and resume() keep the same sandbox id, and what is kept depends on the sandbox class, which comes from the snapshot you create from:

Class suspend() resume()
Container Stops it. Files are kept; processes end. Starts fresh processes (execution: "fresh").
Linux VM Pauses it. Files, memory and running processes are kept. Continues the paused processes (execution: "resumed").

Sockets drop in both cases; reconnect after resume. Set lifecycle: { suspension: { preserve: "filesystem" } } to stop Linux VMs too. "filesystem+memory" pauses Linux VMs and rejects containers with UNSUPPORTED before any change. If a paused VM is later stopped, its memory is gone and resume reports execution: "fresh". Daytona pauses idle Linux VMs on its own after 60 minutes unless idle stop is set; inspect().idleStop reports this as action: "suspend".

The lifetime keeps running while suspended and resume does not reset it. Only sandboxes with no mounts and Daytona’s auto-delete-after-stop disabled qualify: mounted sandboxes reject UNSUPPORTED and enabled auto-delete rejects UNAVAILABLE. Suspending an inactive sandbox or resuming a running one rejects CONFLICT. See Renew lifetime and Suspend and resume.

box.snapshot() stops a running sandbox, captures its filesystem, and starts it again (unless snapshots.restartAfterCapture is false). An already stopped source stays stopped. Processes end; memory and volumes are not captured. This applies to containers and Linux VMs alike; a Linux VM snapshot restores as a new Linux VM. Restoring a Linux VM snapshot with volumes is unsupported.

Set snapshots: { preserve: Preserve.FILESYSTEM_AND_MEMORY } to capture a running Linux VM’s memory as well. The VM keeps running (it may pause briefly), and a restored VM resumes the captured processes. Containers can’t capture memory, so they reject this setting before any change, as do stopped VMs.

Daytona marks a snapshot inactive after two weeks without use. Restoring an inactive snapshot rejects UNAVAILABLE because it isn’t ready; activate it in the Daytona dashboard or API first. If the capture succeeds but the restart fails, the error is SOURCE_RESTART_FAILED and still carries the snapshot reference. Snapshots survive their source. Deleting one is refused while a warm pool uses it or warm pools cannot be read.

Volumes are object storage, not POSIX filesystems: no file locking or atomic rename. They attach at create, or when restoring a snapshot taken without mounts, with optional subpaths. Restore with mounts needs internet or daytona-default; blocked rejects. On mounted paths, writeFile needs overwrite: true; no-clobber writes, stream writes and copy/move destinations reject UNSUPPORTED before any change; and a completed write does not guarantee durability at shutdown. See Snapshots and volumes.

  • Creates Daytona refuses map to INVALID_ARGUMENT (400, 422), UNAUTHENTICATED (401), FORBIDDEN (403), NOT_FOUND (404) or RATE_LIMIT (429) with Daytona’s reason; nothing was created. A 409, server error or lost response is OUTCOME_UNKNOWN, as is a refused create after an OCI snapshot was built, because that snapshot remains. See Errors and recovery.
  • Command deadlineSeconds (default 300) sets Daytona’s native timeout. Cleanup of child processes at timeout is not verified, and a lost response does not prove the command stopped. See Execution and waiting timeouts.
  • A preview token grants full sandbox access, including command execution and files. Treat it as a credential, send it with redirect: "error", and know it cannot be revoked individually; stop/start rotates it. See Preview access.
  • Public previews, memory suspension and mounted suspension are not supported.
  • sandboxes.list() only covers sandboxes with Beachside labels in the configured organization and region; pages can hold fewer items than limit.
  • close() does not stop compute. Snapshots you built from OCI images need separate cleanup.
  • Only the Daytona cloud endpoints are trusted by default.

See the Daytona docs, snapshots, volumes, wall-clock TTL and the runnable agent examples.