PandaStack

Templates

Pre-baked rootfs images used to boot sandboxes — from a universal apps runtime to a 4 GB browser-automation stack.

A template is a snapshot-ready root filesystem with a pinned kernel. When you Sandbox.create({ template: "..." }), the scheduler restores a snapshot of that template into a fresh microVM — that's how cold-start lands at ~49 ms instead of "boot a kernel from scratch".

Templates are immutable and content-addressed: once a snapshot exists for code-interpreter@v3, every sandbox boots from the same bytes.

Official catalog

PandaStack ships 5 production templates. All names are stable and accepted by template: in the SDKs and --template in the CLI.

CPU and memory are baked into each template at build time — they are a property of the snapshot, not a per-launch knob. Pick a template that matches the size you want, or bake your own with --cpu and --memory-mb.

TemplateBasevCPUMemorySizeWhat's inside
baseUbuntu 24.04 + mise22 GB2 GBUniversal apps runtime — Node 22, Python 3.12, Go, Bun via mise. The default for apps & general use.
code-interpreterPython 3.11 + Node 2222 GB2 GBpandas, numpy, matplotlib, scipy, scikit-learn, jupyter, playwright, openai-agents. LLM tool-use, data analysis, Functions, agents.
agentUbuntu 24.04 + Node 2222 GB2 GBEvery public coding-agent CLI in one image: claude-code, codex, opencode. Pick one via its API key.
browserUbuntu 24.04 + Chromium44 GB4 GBHeadless Chromium + Playwright + crawl4ai. Scraping, RPA, screenshots, LLM-friendly web extraction.
postgres-16Ubuntu 24.04 + PGDG21 GB2 GBManaged PostgreSQL 16 + pgvector + pgbouncer on a durable volume.

Every template autostarts sshd and the pandastack-agent systemd unit. When you deploy an app on the base runtime, its dev/start command runs via pandastack-autostart.service, so the preview URL works the instant the sandbox is running.

Older SDKs accepted cpu and memory_mb arguments on Sandbox.create(...). Those arguments are now deprecated and ignored — the server always uses the template's baked size. Pass them and you'll get a deprecation warning; the sandbox will still launch at the template's size. To run at a different size, bake a new template.

Picking a template

If you're doing…Use…
Running apps — Node / Python / Go / Bunbase
LLM tool calling, data analysis, Jupyter-stylecode-interpreter
Running Claude Code / Codex / OpenCode as an agentagent
Browser automation, scraping, screenshots, crawlingbrowser
Managed PostgreSQL 16 with pgvectorpostgres-16
Anything custombake your own (below)

Bake your own

A custom template is just a Dockerfile plus a CPU/memory spec and a size limit. You give PandaStack a Dockerfile and it does the rest — builds the image, flattens it to a rootfs, and bakes a snapshot. You need only an API key: no local Docker, no registry credentials. The request is the Dockerfile plus an optional small build context (≤50 MB), never a multi-GB rootfs, so there is no upload-size cliff.

Debian-based images only. Your base image must be Debian or a Debian derivative — debian, ubuntu, python, node, etc. Alpine, RedHat-based (Fedora/CentOS/Rocky/Alma/UBI), distroless, and other non-Debian distributions are not supported (the microVM's init + SSH contract needs apt/glibc). A non-Debian FROM is rejected before the build starts.

One command: template build

Start from any Dockerfile (e.g. fork templates/base/Dockerfile), then:

pandastack template build \
  --name my-template \
  -f Dockerfile \
  --context .          # include files your Dockerfile COPYs (entrypoint.sh, app/, …)
  --cpu 4 \
  --memory-mb 4096 \
  --size-mb 2048

If your Dockerfile uses COPY/ADD, pass --context <dir> so those files are uploaded with it — otherwise the build fails with COPY failed: file not found in build context. The context is capped at 50 MB / 50 files (it's the source, not the built rootfs).

This uploads the Dockerfile, kicks off a server-side build, and streams the build logs live until the snapshot is ready. --cpu and --memory-mb are baked into the snapshot and become the size of every sandbox launched from this template. Add --context ./dir to include files your Dockerfile COPYs.

The SDKs expose the same flow:

from pandastack import Client
client = Client()

build = client.templates.build_from_dockerfile(
    "my-template",
    "Dockerfile",            # path on disk, or inline Dockerfile content
    context_dir="./app",     # optional — files for COPY/ADD
    cpu=4, memory_mb=4096, size_mb=2048,
)
for line in client.templates.build_logs(build.id):   # live logs
    print(line)
final = client.templates.wait_for_build(build.id)    # raises TemplateBuildError on failure
const build = await client.templates.buildFromDockerfile({
  name: "my-template",
  dockerfile: "FROM ubuntu:24.04\nRUN apt-get update && apt-get install -y curl\n",
  cpu: 4, memoryMb: 4096, sizeMb: 2048,
});
for await (const line of client.templates.buildLogs(build.id)) {
  console.log(line);   // live build logs until the build finishes
}

The build pipeline (all server-side):

  1. Your Dockerfile + context are staged and built with Google Cloud Build (with Docker layer caching — see below), and the image is pushed to PandaStack's private registry.
  2. An agent pulls the image and flattens its layers into a rootfs (whiteouts resolved — equivalent to docker export), writes a sparse ext4 of size_mb MB, and injects working guest DNS.
  3. Publishes a durable copy to object storage (so any host can serve later launches).
  4. Boots the image once under Firecracker and takes a memory + disk snapshot.
  5. Marks the build donemy-template is instantly available for Sandbox.create.

Advanced — bring your own image. If you already build + push images yourself (e.g. in your own CI), skip the Dockerfile upload and register by reference: client.templates.register({ name, imageRef }), or the pandastack template push CLI command (which runs a local docker build + docker push then registers). This needs Docker + registry access on your side.

Example Dockerfiles

Add a few system packages to the universal runtime. The simplest custom template — start from ubuntu and apt install what you need:

FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
      ffmpeg imagemagick poppler-utils \
    && rm -rf /var/lib/apt/lists/*
pandastack template build --name media-tools -f Dockerfile --size-mb 2048

Pin a specific language runtime + dependencies. Bake your dependencies in so every sandbox starts ready — no install step at create time:

FROM python:3.12-slim
RUN pip install --no-cache-dir \
      pandas numpy scikit-learn duckdb pyarrow
# Bake your own code/assets too:
COPY ./pipeline /opt/pipeline
pandastack template build --name data-pipeline -f Dockerfile --context . --size-mb 3072

A heavier toolchain image. Multi-runtime images are fine — just keep the final stage Debian-based (earlier build stages can be anything):

# Build stage may use any base…
FROM golang:1.22 AS build
WORKDIR /src
COPY . .
RUN go build -o /out/app ./cmd/app

# …but the FINAL stage must be Debian-derived.
FROM debian:12-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*
COPY --from=build /out/app /usr/local/bin/app

Sizing tip: --size-mb is the ext4 rootfs size — make it comfortably larger than your image's unpacked size (e.g. a 1.5 GB image → --size-mb 3072) so there's headroom for runtime writes. --cpu/--memory-mb are the VM size baked into the snapshot; every sandbox from this template gets exactly that.

Notes & requirements

  • Debian base onlydebian, ubuntu, python, node, golang, etc. See the callout at the top of this section; non-Debian FROM is rejected before the build runs.
  • sshd + an init are auto-handled. Booting as a microVM needs an init (/sbin/init) and sshd (the host↔guest exec bridge). If your image lacks them — most application images like python:3.12-slim or node:20-slim do — the build automatically installs systemd-sysv + openssh-server into the rootfs and injects an SSH key. You don't add them to your Dockerfile or set a special entrypoint; just build a normal image. (A CMD/ENTRYPOINT you set isn't the VM's init and isn't used for boot.)
  • Size is fixed per template. cpu/memory_mb are baked into the snapshot. To change them, build a new template — they can't be overridden on Sandbox.create.
  • Source caps: Dockerfile ≤ 512 KB, build context ≤ 50 MB across ≤ 50 files. This is the source you upload, not the built rootfs — large images are fine, you just can't ship 50 MB+ of context files inline. For big assets, RUN curl/git clone them during the build instead of COPYing.
  • Builds run linux/amd64. Don't rely on arch-specific FROM tags.
  • Naming: [a-zA-Z0-9._-], ≤ 64 chars. Re-running template build with the same name replaces it (and reuses cached layers).

Troubleshooting

ErrorCause & fix
COPY failed: file not found in build contextYour Dockerfile COPYs a file you didn't upload. Pass --context <dir> (often --context .).
unsupported base image … only Debian-based images are supportedA non-Debian FROM (Alpine, Fedora, distroless, …). Switch to a Debian/Ubuntu base.
build context too large / too many build-context filesContext exceeds 50 MB / 50 files. Fetch large assets during the build (RUN curl …) instead of COPYing them.
Build stuck on a slow RUN (compiling from source)Heavy builds run on an 8-vCPU builder, but compiling multiple language runtimes from source is still minutes-long. Prefer prebuilt runtime base images (python:3.12, node:20) over pyenv/rbenv-from-source where you can.
template build failed with no obvious stepStream the full log: pandastack template build-logs <id> — the failing Step N/NN and its output are there.

Caching

Rebuilds are fast and cheap:

  • Content dedup — an identical Dockerfile + context + size spec short-circuits to the existing template; no rebuild runs at all.
  • Docker layer cache — a changed Dockerfile reuses unchanged layers from the previous build (--cache-from).
  • Snapshot reuse — every Sandbox.create restores the one baked snapshot; the image is never rebuilt per launch.

Streaming build logs

Every build emits timestamped progress (pulling, mounting + extracting, installing, baking, …). Stream them any time:

pandastack template build-logs <build_id> --follow
for line in client.templates.build_logs(build_id, follow=True):
    print(line)

Or just wait for the terminal result:

final = client.templates.wait_for_build(build_id)   # raises TemplateBuildError on failure

A typical 2 GB template takes 1–2 min end to end (Cloud Build + snapshot bake); a 4 GB one takes 3–5 min. Cached rebuilds are much faster.

Versioning and pinning

template: "code-interpreter" resolves to the latest snapshot tagged code-interpreter. To pin, pass the snapshot ID directly:

await Sandbox.create({ fromSnapshot: "snap-019fe18f-…" });

Useful for reproducibility: tie a release of your agent to a known template snapshot, regardless of what the cloud rolls out later.

CLI

pandastack template list
pandastack template get code-interpreter
pandastack template build -f Dockerfile --name my-template --size-mb 2048   # server-side build (recommended)
pandastack template build-logs <id> --follow                               # stream a build's progress
pandastack template build-status <id>                                      # poll a single build
pandastack template builds                                                 # all recent build jobs
pandastack template push  -f Dockerfile --name my-template                 # (advanced) local docker build + push
pandastack template delete my-template

pandastack sandbox create --template code-interpreter --ttl 1h

REST equivalents

GET    /v1/templates
GET    /v1/templates/{name}
POST   /v1/templates/build               (name, size_mb, cpu, memory_mb; plus ONE of: dockerfile (+context files, server-side build), image_ref (pre-built image), or a rootfs file)
GET    /v1/templates/builds
GET    /v1/templates/builds/{id}
GET    /v1/templates/builds/{id}/logs    (SSE; ?follow=1 to tail live)
DELETE /v1/templates/{name}              (also clears the cached snapshot)

The image_ref form field is the registry path (recommended); the legacy rootfs tarball field still works for small templates and local development.

On this page