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

# Sandbox lifecycle and expiration

> Track sandbox deployment status, set an expiration policy, and preserve files before deletion or expiration removes local data.

export const SandboxLifecycleDiagram = () => {
  const ref = React.useRef(null);
  const init = React.useRef(false);
  React.useEffect(() => {
    if (!ref.current || init.current) return;
    init.current = true;
    function box(g, p, x, y, title, sub, opts) {
      const o = opts || ({});
      const fill = o.fill || p.node;
      const brd = o.border || p.nodeBrd;
      const dash = o.dashed ? ` stroke-dasharray="5 4"` : "";
      let s = `<rect x="${x}" y="${y}" width="128" height="46" rx="7" fill="${fill}" stroke="${brd}" stroke-width="1.4"${dash}/>`;
      s += g.txt(x + 64, y + 17, title, {
        fs: 11.5,
        fw: 600,
        fill: o.titleColor || p.txt
      });
      s += g.txt(x + 64, y + 33, sub, {
        fs: 9.5,
        fill: p.sub
      });
      return s;
    }
    function build(g, p) {
      let s = "";
      s += g.txt(84, 12, "create a sandbox", {
        fs: 9.5,
        fill: p.sub
      });
      s += g.arrow(84, 18, 84, 28, p);
      s += box(g, p, 20, 28, "DEPLOYING", "deployment in progress");
      s += g.arrow(148, 51, 218, 51, p);
      s += g.txt(183, 40, "ready", {
        fs: 9.5,
        fill: p.sub
      });
      s += box(g, p, 218, 28, "DEPLOYED", "ready for work", {
        border: p.green,
        titleColor: p.green
      });
      s += g.arrow(346, 51, 416, 51, p);
      s += g.txt(381, 40, "delete or", {
        fs: 9.5,
        fill: p.sub
      });
      s += g.txt(381, 62, "expire", {
        fs: 9.5,
        fill: p.sub
      });
      s += box(g, p, 416, 28, "DELETING", "deletion in progress");
      s += g.arrow(480, 74, 480, 188, p);
      s += g.txt(472, 131, "deletion completes", {
        fs: 9.5,
        fill: p.sub,
        anchor: "end"
      });
      s += box(g, p, 416, 188, "TERMINATED", "removed with its data", {
        titleColor: p.sub
      });
      s += g.arrow(84, 74, 84, 188, p);
      s += g.txt(92, 131, "deployment fails", {
        fs: 9.5,
        fill: p.sub,
        anchor: "start"
      });
      s += box(g, p, 20, 188, "FAILED", "inspect logs, then retry", {
        titleColor: p.noTx
      });
      s += `<path d="M262,74 C262,120 262,140 262,186" fill="none" stroke="${p.line}" stroke-width="1.4"/>`;
      s += `<polygon points="262,190 258,181 266,181" fill="${p.line}"/>`;
      s += g.txt(252, 131, "no activity", {
        fs: 9.5,
        fill: p.sub,
        anchor: "end"
      });
      s += `<path d="M302,186 C302,140 302,120 302,76" fill="none" stroke="${p.line}" stroke-width="1.4"/>`;
      s += `<polygon points="302,72 298,81 306,81" fill="${p.line}"/>`;
      s += g.txt(312, 131, "any request", {
        fs: 9.5,
        fill: p.sub,
        anchor: "start"
      });
      s += box(g, p, 218, 188, "DEACTIVATED", "standby, state persists", {
        dashed: true,
        titleColor: p.sub
      });
      s += g.arrow(346, 211, 416, 211, p);
      s += g.txt(381, 200, "delete or", {
        fs: 9.5,
        fill: p.sub
      });
      s += g.txt(381, 222, "expire", {
        fs: 9.5,
        fill: p.sub
      });
      return g.frame("sbl", 560, 262, p, s);
    }
    let cleanup = null, destroyed = false, timer = null, retries = 0;
    const tryMount = () => {
      if (destroyed || !ref.current) return;
      if (window._sdgMount) {
        cleanup = window._sdgMount(ref.current, build);
      } else if (retries++ < 60) {
        timer = setTimeout(tryMount, 30);
      }
    };
    tryMount();
    return () => {
      destroyed = true;
      if (timer) clearTimeout(timer);
      if (cleanup) cleanup();
      init.current = false;
    };
  }, []);
  return <div ref={ref} />;
};

export const StaticDiagramEngine = () => {
  React.useEffect(() => {
    if (window._sdgMount) return;
    const isDark = () => document.documentElement.classList.contains("dark");
    const esc = s => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    const MONO = "ui-monospace,SFMono-Regular,Menlo,monospace";
    function P() {
      const d = isDark();
      return {
        grid: d ? "#1b2a21" : "#e4ebe3",
        line: d ? "#5b6b60" : "#0c1d13",
        node: d ? "#0c1d13" : "#ffffff",
        nodeBrd: d ? "#cdd6cf" : "#0c1d13",
        txt: d ? "#e7ece7" : "#0c1d13",
        sub: d ? "#8b958d" : "#6a766d",
        green: d ? "#19e76e" : "#0e863f",
        cyl: d ? "rgba(25,231,110,0.12)" : "#eafaf0",
        cylBrd: d ? "#2f7d52" : "#0e863f",
        groupBrd: d ? "#26362c" : "#cdd6cf",
        yesBg: d ? "rgba(25,231,110,0.20)" : "#bdf4d3",
        yesTx: d ? "#3df58a" : "#0a6e34",
        noBg: d ? "rgba(255,120,120,0.18)" : "#f7d4d4",
        noTx: d ? "#ff9a9a" : "#b23b3b"
      };
    }
    function txt(x, y, s, opts) {
      const o = opts || ({});
      return `<text x="${x}" y="${y}" font-family="${MONO}" font-size="${o.fs || 12}" font-weight="${o.fw || 500}" fill="${o.fill}" text-anchor="${o.anchor || "middle"}" dominant-baseline="middle" letter-spacing="-0.3">${esc(s)}</text>`;
    }
    function rect(x, y, w, h, p, fill) {
      return `<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="7" fill="${fill || p.node}" stroke="${p.nodeBrd}" stroke-width="1.4"/>`;
    }
    function group(x, y, w, h, p, title) {
      let s = `<rect x="${x}" y="${y}" width="${w}" height="${h}" rx="9" fill="none" stroke="${p.groupBrd}" stroke-width="1.2" stroke-dasharray="4 4"/>`;
      if (title) s += txt(x + 12, y + 15, title, {
        fs: 10.5,
        fw: 600,
        fill: p.sub,
        anchor: "start"
      });
      return s;
    }
    function cyl(cx, w, top, h, p) {
      const x = cx - w / 2, ry = 7;
      return `<path d="M${x},${top + ry} a${w / 2},${ry} 0 0 1 ${w},0 v${h - 2 * ry} a${w / 2},${ry} 0 0 1 ${-w},0 Z" fill="${p.cyl}" stroke="${p.cylBrd}" stroke-width="1.4"/>` + `<path d="M${x},${top + ry} a${w / 2},${ry} 0 0 0 ${w},0" fill="none" stroke="${p.cylBrd}" stroke-width="1.4"/>`;
    }
    function arrow(x1, y1, x2, y2, p) {
      const ah = 5, ang = Math.atan2(y2 - y1, x2 - x1);
      const hx = x2 - ah * Math.cos(ang), hy = y2 - ah * Math.sin(ang);
      const lx = hx + ah * Math.cos(ang + 2.5), ly = hy + ah * Math.sin(ang + 2.5);
      const rx = hx + ah * Math.cos(ang - 2.5), ry = hy + ah * Math.sin(ang - 2.5);
      return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${p.line}" stroke-width="1.4"/><polygon points="${x2},${y2} ${lx},${ly} ${rx},${ry}" fill="${p.line}"/>`;
    }
    function pill(cx, cy, s, bg, fg) {
      const w = s.length * 7.5 + 18, x = cx - w / 2;
      return `<rect x="${x}" y="${cy - 11}" width="${w}" height="22" rx="11" fill="${bg}"/>` + txt(cx, cy + 1, s, {
        fs: 10.5,
        fw: 600,
        fill: fg
      });
    }
    function frame(id, W, H, p, inner) {
      return `<svg viewBox="0 0 ${W} ${H}" width="100%" style="max-width:${W}px;display:block" xmlns="http://www.w3.org/2000/svg">` + `<defs><pattern id="sdg-${id}" width="16" height="16" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="1" fill="${p.grid}"/></pattern></defs>` + `<rect x="0" y="0" width="${W}" height="${H}" fill="url(#sdg-${id})"/>` + inner + `</svg>`;
    }
    const H = {
      esc,
      MONO,
      txt,
      rect,
      group,
      cyl,
      arrow,
      pill,
      frame
    };
    window._sdgMount = function (root, build) {
      const wrap = document.createElement("div");
      wrap.style.cssText = "margin:14px 0;border:1px solid;border-radius:8px;padding:14px 10px;overflow-x:auto";
      function render() {
        const p = P();
        wrap.style.borderColor = isDark() ? "#203026" : "#eef3ed";
        wrap.style.background = isDark() ? "#021309" : "#fff";
        wrap.innerHTML = build(H, p);
      }
      render();
      root.appendChild(wrap);
      const obs = new MutationObserver(render);
      obs.observe(document.documentElement, {
        attributes: true,
        attributeFilter: ["class"]
      });
      return () => {
        obs.disconnect();
        wrap.remove();
      };
    };
    return () => {
      delete window._sdgMount;
    };
  }, []);
  return <span />;
};

<StaticDiagramEngine />

A sandbox's deployment status tells you whether it's ready for work. Set an
expiration policy so Baseten deletes the sandbox and its local data when it
expires, even if your application stops before cleanup.

Retrieve the task's result before the sandbox's deadline, then
[delete the sandbox](/sandboxes/manage#delete-a-sandbox).

## Deployment status

A create request returns once the sandbox is `DEPLOYED`. Check `status` later
with [Get a sandbox](/sandboxes/manage#get-a-sandbox), or on the sandbox's
detail page in the dashboard.

* `DEPLOYING`: deployment is in progress.
* `DEPLOYED`: the sandbox is ready.
* `DEACTIVATING`: the sandbox is going idle.
* `DEACTIVATED`: the sandbox is on standby. Its files and processes persist,
  and the next request resumes it.
* `FAILED`: deployment failed.
* `DELETING`: deletion is in progress.
* `TERMINATED`: deletion has completed.

<SandboxLifecycleDiagram />

`DEPLOYED` means the environment is ready, not that a task succeeded. Confirm
a task by its command's exit status and output.

Deletion is asynchronous. A delete request returns `DELETING`. Repeat the get
request until the status is `TERMINATED` or the sandbox is no longer found.

## Expiration policy

An *expiration policy* deletes a sandbox automatically. Set one when you
[create a sandbox](/sandboxes/manage#create-a-sandbox), so a sandbox your
application forgets to delete doesn't keep running.

Set the policy when you create the sandbox. The CLI has no expiration flag, so
use the dashboard's **Time to live** or one of these interfaces. These examples
delete the sandbox 1 hour after creation:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    const sandbox = await client.create({
      name: "hello-world",
      lifecycle: {
        expirationPolicies: [{ type: "TTL_MAX_AGE", afterMs: 3_600_000 }],
      },
    });
    console.log(sandbox.name);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os
    from datetime import timedelta

    from baseten.sandbox import SandboxClient, SandboxExpirationPolicyMaxAge, SandboxLifecycle

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        sandbox = client.create(
            name="hello-world",
            lifecycle=SandboxLifecycle(
                expiration_policies=[SandboxExpirationPolicyMaxAge(after=timedelta(hours=1))]
            ),
        )
        print(sandbox.name)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "fmt"
        "log"
        "os"
        "time"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        sb, err := client.Create(ctx, sandbox.CreateOptions{
            Name: "hello-world",
            Lifecycle: &sandbox.Lifecycle{
                ExpirationPolicies: []sandbox.ExpirationPolicy{
                    {Type: sandbox.ExpirationPolicyTypeTTLMaxAge, After: time.Hour},
                },
            },
        })
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(sb.Name())
    }
    ```
  </Tab>

  <Tab title="API">
    Add a policy to `lifecycle.expiration_policies` in the create request:

    ```bash theme={"system"}
    curl --request POST \
      --url https://api.baseten.co/v1/sandboxes/instances \
      --header "Authorization: Bearer $SANDBOX_TOKEN" \
      --header "Content-Type: application/json" \
      --data '{
        "lifecycle": {
          "expiration_policies": [
            {"type": "TTL_MAX_AGE", "value": "1h", "action": "DELETE"}
          ]
        }
      }'
    ```
  </Tab>
</Tabs>

A sandbox's `expires_in` field shows the seconds until it expires.

Each policy `type` measures time differently:

* `TTL_MAX_AGE`: deletes the sandbox a set duration after creation. Idle time
  counts, and activity doesn't extend it. **Time to live** in the dashboard
  sets this policy.
* `TTL_IDLE`: deletes the sandbox after a set duration without activity.
* `DATE`: deletes the sandbox at an ISO 8601 timestamp, such as
  `2026-10-02T18:00:00Z`.

You can combine types, one policy of each. Baseten deletes the sandbox when
the first condition occurs. Choose a duration that covers setup, execution, and
retrieving results. The sandbox's detail page shows the deadline as
**Date of termination**.

## Local data

Deletion and expiration permanently remove a sandbox's local data. A new
sandbox from the same image starts with the image's contents, not the deleted
sandbox's working files. Save outputs outside the sandbox before its deadline.

If a sandbox expires before your work completes, create a new one with a
lifetime that covers your work, and restore the outputs you saved outside it.
