Sandbox image

The layered Docker image, what Kortix injects, constraints, examples, and snapshot rebuilds.

The sandbox base image. For how a session uses it at runtime, see Session runtime.

Every session boots inside a Docker container, built in two parts: your Dockerfile defines the environment (languages, tools, system packages), and the Kortix runtime layer is auto-injected on top so the dashboard can connect.

┌─────────────────────────────────────────┐
│  Kortix runtime layer (auto-injected)   │  ← opencode + kortix-agent + ENTRYPOINT
├─────────────────────────────────────────┤
│  Your Dockerfile                        │  ← .kortix/Dockerfile
└─────────────────────────────────────────┘

Referenced from the manifest as a named template under sandbox.templates (v2 kortix.yaml) or [[sandbox.templates]] (legacy v1 kortix.toml):

# kortix.yaml (v2)
sandbox:
  templates:
    - slug: dev               # any slug except the reserved "default"
      name: Dev box            # optional label
      dockerfile: .kortix/Dockerfile   # OR image: python:3.12-slim
      cpu: 2      # optional — vCPU cores
      memory: 4   # optional — GiB
      disk: 20    # optional — GiB
  # Make a template the project-wide default so every session (UI, triggers,
  # channels) boots it without passing a slug. Omit → the platform default.
  default: dev
# kortix.toml (legacy v1) — same fields, TOML array of tables
[[sandbox.templates]]
slug       = "dev"             # any slug except the reserved "default"
name       = "Dev box"         # optional label
dockerfile = ".kortix/Dockerfile"   # OR image = "python:3.12-slim"
cpu        = 2     # optional — vCPU cores
memory     = 4     # optional — GiB
disk       = 20    # optional — GiB

[sandbox]
default = "dev"

Each entry sets exactly one of dockerfile or image. Paths must be repo-relative — absolute paths and .. traversal are rejected. A singular sandbox block with image/build keys on it directly (rather than under templates) is legacy and rejected; image definitions live under sandbox.templates, and sandbox itself otherwise only carries default. Field details: the manifest reference.

What the Kortix layer injects

On top of your Dockerfile's final stage, the snapshot builder appends:

  1. USER root (the layer needs to install things).
  2. apt-get install a system package floor: ca-certificates curl git gzip nodejs npm unzip tmux iproute2 iputils-arping build-essential ffmpeg fonts-dejavu fonts-liberation fonts-noto fonts-noto-cjk latexmk libreoffice pandoc pkg-config poppler-utils qpdf tesseract-ocr python3 python3-dev python3-pip python3-venv texlive-bibtex-extra texlive-fonts-recommended texlive-latex-base texlive-latex-extra texlive-latex-recommended — CLI/networking basics, a build toolchain, LibreOffice/LaTeX/Pandoc document conversion, OCR, and a Python 3 toolchain.
  3. A pinned Python package floor via pip install --break-system-packages, verified importable at build time: web/parsing (beautifulsoup4, lxml, markdownify, markitdown[pptx], requests), data (numpy, pandas, scipy, scikit-learn), PDF/office (pdf2docx, pdf2image, pdfplumber, pymupdf, pypdf, pypdfium2, python-docx, python-pptx, openpyxl, reportlab), imaging/browser (pillow, pytesseract, playwright), and plotting (matplotlib, plotly, seaborn). This backs the starter's document/data/PDF/presentation skills and is present even if your Dockerfile never touches Python.
  4. npm install -g opencode-ai@<pinned-version>, plus the bun runtime and agent-browser (for the headless-browser tool).
  5. A baked OpenCode tool-dependency cache under /opt/kortix/ (so the boot-time bun install in your config dir is a no-op, not a network round-trip).
  6. COPY the kortix-agent daemon, the kortix CLI, and the kortix-entrypoint script to /usr/local/bin/, plus the slack-cli and executor-sdk package trees under /opt/kortix/.
  7. ENV KORTIX_WORKSPACE=/workspace, WORKDIR /workspace, EXPOSE 8000.
  8. ENTRYPOINT ["/usr/local/bin/kortix-entrypoint"].

Everything you installed stays on PATH; the layer relocates and removes nothing. The exact package lists above are pinned to a runtime layer version and can change between platform releases — they're the floor guaranteed on every sandbox, not a ceiling on what you can add.

Constraints

These rules keep a sandbox connectable. They aren't enforced statically — your build succeeds even if you violate them — but the session won't behave correctly.

RuleWhy
Don't set ENTRYPOINT.Kortix overrides it. Your CMD is also ignored.
Don't claim port 8000.Reserved for the daemon's reverse proxy. Run your dev servers on other ports.
FROM a Debian/Ubuntu-family base.The layer assumes apt-get. Alpine, Fedora, Arch will fail at layer build.
Don't RUN apt-get clean without rm -rf /var/lib/apt/lists/*.The Kortix layer re-runs apt-get update; a broken lists cache breaks it.
Don't bake credentials into the image.Declare the name in env: (or legacy [env]) and set the value in the dashboard's Environment variables page — it's injected at session start.

Everything else is fair game: RUN curl … | sh for toolchains, COPY seed data, set ENV for non-secret config, install any apt/npm packages.

Examples

The starter ships no Dockerfile — sandbox.templates is a commented-out example in kortix.yaml, so a fresh project boots the platform's default image (bare Ubuntu plus the Kortix runtime layer above):

# syntax=docker/dockerfile:1.7
# Kortix platform default sandbox base.
FROM ubuntu:24.04

WORKDIR /workspace

Add your own .kortix/Dockerfile and declare it under sandbox.templates once you need packages beyond the runtime floor. A minimal custom base might look like this:

# syntax=docker/dockerfile:1.7
FROM ubuntu:24.04

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        ca-certificates curl git build-essential \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

A heavier example with Python + Bun:

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates curl git python3.12 python3.12-venv unzip \
    && rm -rf /var/lib/apt/lists/* \
    && curl -fsSL https://bun.sh/install | bash

ENV PATH="/root/.bun/bin:${PATH}"
WORKDIR /workspace

Hardware spec

cpu, memory, and disk on a sandbox.templates entry size the box your sessions run on. All three are optional; an omitted field takes the runtime provider's default. cpu is vCPU cores, memory and disk are GiB. GPUs are not supported in this version — a gpu key is rejected by the manifest validator.

# kortix.yaml (v2)
sandbox:
  templates:
    - slug: big
      image: ubuntu:24.04
      cpu: 4
      memory: 8
      disk: 50

The spec is baked into the snapshot, not set per-session: the provider builds each sandbox from your project's snapshot and inherits its size from there. That has one practical consequence — changing the spec rebuilds the snapshot (it's part of the content hash, below) and the new size applies on the next session, exactly like a Dockerfile edit. Values are rounded to whole numbers; a non-positive value falls back to the default, and anything above the platform ceiling (cpu 32, memory 128 GiB, disk 500 GiB) is clamped down. See the manifest → sandbox.templates for the full field table and aliases.

Snapshot rebuilds

Snapshots are content-addressed: the platform hashes your Dockerfile's own bytes + the hardware spec + a platform runtime fingerprint (opencode version + kortix-agent binary). Unchanged hash reuses the snapshot; otherwise a new one builds. Rebuilds happen on any push that changes the Dockerfile's text, or any change to the sandbox spec — pure code commits, and edits to files your Dockerfile COPYs in without touching the Dockerfile itself, reuse the snapshot for free (the hash isn't computed over those copied files today).

Builds run on the sandbox provider (on the default Daytona provider, via Image.fromDockerfile). The first session on a new content hash triggers an inline build (a few minutes, shown as "preparing image"); later sessions on the same content hit the cache.

Editing the Dockerfile inside a session takes effect on the next session; current ones keep their booted snapshot. The edit reaches main only once a change request merges it.

Sandbox image – Kortix Docs