Sandboxes & previews
The cloud computer behind each session: providers, image and hardware, environment, ports and previews, files, and troubleshooting.
A sandbox is the isolated cloud computer a session runs on. Each session gets its own sandbox, with the project repository cloned at /workspace on the session’s branch. This page is for people who customize that computer, open the apps an agent runs in it, find its files, and debug it. Starting, stopping and sleep rules are on Sessions.
Describe the computer
Add Dockerfile.dev to the repository. Start from a Debian or Ubuntu image and install what your agents need.
FROM ubuntu:24.04
RUN apt-get update \
&& apt-get install -y --no-install-recommends postgresql-client \
&& rm -rf /var/lib/apt/lists/*Declare the template
Name it in kortix.yaml and make it the default. kortix sandboxes add dev --dockerfile Dockerfile.dev --cpu 4 --memory 8 writes the template entry for you. Add default yourself.
sandbox:
templates:
- slug: dev
dockerfile: Dockerfile.dev
cpu: 4
memory: 8
default: devCheck it, then merge
kortix sandboxes build --local builds the image on your own Docker before you push. Kortix reads the Dockerfile from the default branch, so merge the change. The next new session builds the image. kortix sandboxes health shows when it is ready.
Open what the agent runs
When the agent starts a dev server on port 3000, run kortix sessions forward <session_id> --port 3000 and open the local address it prints.
Inside the sandbox
| Path or port | What it is |
|---|---|
/workspace |
The project repository, checked out on the session branch. The working directory. |
/workspace/incoming |
Files that kortix sessions new --with-file uploads before the first prompt. |
/drives |
Shared folders, when the Files feature is on. The person’s own folder is /drives/me. See Files. |
/home/kortix |
Home of the kortix user. The agent runs as kortix, with passwordless sudo. |
Port 8000 |
The Kortix daemon. It runs the agent harness and proxies every other port. |
- The session branch is named after the
session_id. Commits push to that branch through the Kortix git proxy. By default, work reaches the base branch through a change request. - The agents, skills and tools a session runs come from a read-only copy of the base branch, not from
/workspace. An edit in/workspacetakes effect after it reaches the base branch. See Config comes from the base branch. - An agent with
workspace: { project_repository: false }gets no clone and no git variables. Inkortix_version: 3that is the default: an agent opts in withproject_repository: true. See Workspace. - The sandbox has its own id at the provider, the external id. It is not the
session_id. Previews and the API proxy use the external id.kortix sessions info <session_id> --jsonshows it insandbox_url, after/v1/p/.
Ports and previews
Any server that listens on a port in the sandbox is reachable from outside it. Port 8000 is the Kortix daemon. Kortix gives each port two addresses.
| Form | Address | Use it for |
|---|---|---|
| Preview origin | https://{env}-p{port}-{label}.p.kortix.com on Kortix Cloud. {label} is the external id in lowercase, with _ replaced by -. |
A browser. |
| Path | {api}/v1/p/{external_id}/{port}/ |
Scripts and API clients. |
The preview origin serves the app at /, on its own origin. Root-absolute links, cookies, service workers, hot reload and WebSockets work with no change to the app. Sign-in is a cookie for that origin: a cold visit asks you to sign in to Kortix, then returns you to the app. The proxy removes X-Frame-Options and frame-ancestors, so the session page can show the app in a frame. GET /v1/p/config returns the address template for the instance you use.
- Open a preview: the session page’s preview panel offers ports 3000 (App preview), 5173 (Frontend preview), 8080 (Dev server) and 8001 (API docs), plus any other port.
- Get a link from a terminal:
kortix sessions preview <session_id> [port]prints a URL for the port (default 3000). The URL carries your sign-in token, so do not share it.--listprints the preview candidates. - Forward to your machine:
kortix sessions forward <session_id> --port 3000serves the port onlocalhost:3000, or on the next free port when 3000 is in use. It prints the address and runs until you press Ctrl+C.--port 3000:4000picks the local port, and--portrepeats. It forwards HTTP and WebSockets. The sandbox must be running. - From code:
kortix.session(projectId, sessionId).previewUrl(port, path)returns the browser address. See Sessions from code. - Share with someone outside Kortix: create a public link. See Public links.
A preview request to a stopped sandbox wakes it. Preview traffic from a signed-in person also keeps the sandbox awake: see When a session sleeps.
To run a server that outlives the agent’s tool call, start it in tmux.
Files
A session’s files live in four places. Pick the store by what you need.
| Store | What it holds | Read and write it |
|---|---|---|
| Live workspace | /workspace in the running sandbox, with uncommitted work and build output. |
kortix sessions files <session_id> ls, status, find, write, touch, mkdir, mv, rm. kortix sessions cp <session_id>:<path> <dst> copies files, -r for a directory. Both wake a stopped sandbox. |
| Committed repository | What a branch holds in git. | kortix files ls, cat, search, history, diff, download, with --ref <branch>. The project’s Files page, not Customize → Files. No sandbox needed. |
| Session attachments | Files people attached to the session, and copies of files the agent showed. Saved at the end of every turn. | kortix sessions attachments <session_id>, with --download <attachment_id> or --all, and --out <dir>. Works while the session is stopped. |
| Shared folders | Folders for the whole project, kept in sync with sessions. | Customize → Files. A new session mounts them under /drives. |
kortix sessions cp also copies between two sandboxes: kortix sessions cp <session_a>:/workspace/out <session_b>:/workspace/in -r. Paths in kortix sessions files resolve under /workspace unless they start with /workspace, /tmp, /home or /opt.
Shared folders
Every person has a private folder, and any folder can be shared with people, groups and agents. A new session mounts:
- the folders the agent or the whole project has a grant on, at
/drives/<folder name>; - in a private session that the person started, also that person’s own folder at
/drives/meand every folder shared with them or their groups.
Shared folders need the Files feature. It needs Platinum and the project’s Volumes flag (Customize → Settings → Feature flags). With Files on, a session on another provider does not start. A platform admin can turn on drive sync: the sandbox then copies each folder into the same /drives path and keeps it in sync through Kortix.
The image and templates
A sandbox image has two layers: your Dockerfile or image, and the Kortix runtime layer on top. A project with no template uses the platform default image: ubuntu:24.04 plus the runtime layer.
Declare a template
Templates live under sandbox.templates in kortix.yaml. You can also create one in Customize → Settings → Sandbox templates. slug, name, dockerfile or image, entrypoint, cpu, memory, disk and container_runtime are the fields. sandbox.default names the template that sessions use. Full rules and the template order are in the manifest reference.
What the runtime layer adds
- System packages:
git,curl,sudo,tmux,build-essential,ffmpeg, fonts. - Shell tools:
rg,fd,bat,jq,fzf,tree,htop,lsof,rsync,sqlite3,wget,zip,unzip,zstd,git-lfs,ssh,nc,nano. - Document tools: LibreOffice, Pandoc, Poppler,
qpdf, Tesseract OCR, LaTeX. - Node.js and npm, managed by
pnpm. Python, managed byuv, aspythonandpython3. Install Python packages per command withuv run --with <package>. bun,opencode, andagent-browserwith a built-in Chromium.- The
kortixCLI and the Kortix daemon, which starts as the entrypoint. /etc/profile.d/zz-kortix.sh, which puts these tools onPATHin every terminal shell. It keeps everyPATHentry your Dockerfile adds.
Dockerfile rules
| Rule | Why |
|---|---|
Start FROM a Debian or Ubuntu image. |
The runtime layer installs with apt-get. Its packages resolve on Ubuntu 22.04, Ubuntu 24.04 and Debian 12. |
Do not set ENTRYPOINT or CMD. |
Kortix replaces both to start its daemon. |
Do not use port 8000. |
The daemon listens there. |
Do not COPY or ADD files from the repository. |
The build context is empty. Fetch files with RUN, or read them from /workspace at runtime. |
| Do not put credentials in the image. | Store them as secrets. Kortix delivers them when the session starts. |
Builds
Kortix names each build by a hash of the Dockerfile bytes (or the image reference), the hardware, container_runtime, and the Kortix runtime version. An unchanged hash reuses the existing image. A changed hash builds a new image when the next session needs it, and later sessions reuse it. A Dockerfile edit inside a session takes effect after it merges to the default branch.
| Command | What it does |
|---|---|
kortix sandboxes ls |
Lists templates and their state at the provider. |
kortix sandboxes health |
Shows if the default template is ready. |
kortix sandboxes builds |
Shows the last 25 builds. |
kortix sandboxes build <slug> |
Starts a build. |
kortix sandboxes rebuild <slug> |
Deletes the existing image, then builds again. |
kortix sandboxes build --local [slug] |
Builds on your own Docker with the same empty context. Needs no sign-in. --print prints the composed Dockerfile. --no-layer builds only your Dockerfile. |
kortix sandboxes fix |
Starts a session that has the last failed build log, so an agent can repair the Dockerfile. |
kortix sandboxes add, update, rm |
Edit sandbox.templates in the local kortix.yaml. |
kortix validate also checks these Dockerfiles before you push.
Hardware and Docker
| Field | Default | Range |
|---|---|---|
cpu |
2 vCPU | 1 to 32 |
memory |
4 GiB | 1 to 128 GiB |
disk |
20 GiB | 1 to 500 GiB |
A value above the range warns, and Kortix uses the maximum. A value below the range is an error in kortix validate, kortix ship and the change request merge. Kortix has no GPU sandboxes: a gpu key gets a warning. The hardware is part of the image, so a change applies to the next session. A running session keeps its size.
Docker in the sandbox
Set container_runtime: true on a template to run Docker inside its sandboxes. docker compose and supabase start then work as they do on your own machine.
sandbox:
templates:
- slug: dev
dockerfile: Dockerfile.dev
container_runtime: true
Install Docker in your Dockerfile, for example RUN curl -fsSL https://get.docker.com | sh.
The runtime layer adds the kortix user to the docker group and starts dockerd at boot. Its log is /var/log/dockerd.log. If dockerd stops, start it again with sudo sh -c 'nohup dockerd >/var/log/dockerd.log 2>&1 &'.
On Platinum, container_runtime also adds the kernel modules Docker needs for its networks and storage.
Environment variables
Kortix sets these variables in every session sandbox.
| Variable | Value |
|---|---|
KORTIX_PROJECT_ID |
The project id. |
KORTIX_SESSION_ID |
The session_id. |
KORTIX_BRANCH_NAME |
The session branch. Equal to KORTIX_SESSION_ID. |
KORTIX_BASE_REF |
The branch the session started from. |
KORTIX_DEFAULT_BRANCH |
Equal to KORTIX_BASE_REF. It differs from the project’s default branch when the session started from another branch. |
KORTIX_BASE_SHA |
The base commit. Set on a new session’s first boot only. |
KORTIX_REPO_URL |
The clone URL, through the Kortix git proxy. |
KORTIX_API_URL |
The Kortix API base, ending in /v1. |
KORTIX_FRONTEND_URL |
The Kortix web app base. Use it for links a person opens. |
KORTIX_SERVICE_PORT |
8000. |
KORTIX_AGENT_NAME |
The session’s agent. |
KORTIX_MODEL |
The session’s model, when one is set. |
KORTIX_HARNESS |
pi on a pi session. Not set on an OpenCode session. |
KORTIX_LLM_BASE_URL |
The LLM gateway. |
KORTIX_TOKEN |
The session credential. See below. |
KORTIX_PROJECT_SECRET_NAMES |
Comma-separated names of the secrets delivered to this session. |
KORTIX_REPO_URL, KORTIX_BRANCH_NAME, KORTIX_BASE_REF, KORTIX_DEFAULT_BRANCH and KORTIX_BASE_SHA are absent when the agent has no repository access.
KORTIX_TOKEN is the one Kortix credential in the sandbox. It names the project, the session and its agent. A session whose agent is declared in kortix.yaml acts as that agent’s service account, with its kortix_permissions capped by its ceiling role. The person who started the session decides only which personal resources reach a private session. See What an agent may do. The kortix CLI, git, the LLM gateway and connector calls all use it, and each API route still checks its own permission. Inside a sandbox the CLI needs no sign-in: see Inside a session sandbox.
Secrets. A secret with environment exposure arrives as a plain variable. An egress-enforced secret arrives as a handle, not the value. A secret with exposure none never enters the sandbox. The agent’s grant decides which secrets a session gets: see Secrets. Kortix drops a project secret named KORTIX_*, OPENCODE_*, or a system name such as PATH, HOME or PORT.
What never enters the sandbox: the upstream git token (pushes go through the Kortix git proxy) and connector credentials.
Sandbox providers
Platinum is the reference sandbox provider. New sessions run on Platinum by default. The operator of the Kortix instance decides which providers are on.
| Provider | Id | Docker in the sandbox | Persistent machines and shared folders | US region |
|---|---|---|---|---|
| Platinum | platinum |
Yes | Yes | Yes, with the US Region flag |
| Daytona (legacy) | daytona |
Yes | No | No |
A new session gets its provider in this order:
- The
providerfield on the create request (POST /v1/projects/{projectId}/sessions). A provider that is not on returns400. - The project’s provider pin, when that provider is still on.
- The instance’s distribution across its enabled providers.
Pin a provider for a project
- Open Customize → Settings → Sandbox templates.
- Under Routing, pick a provider, or the automatic choice to remove the pin.
From a terminal, run kortix sandboxes provider <name>. kortix sandboxes provider --clear removes the pin. kortix sandboxes provider status shows the latest switch.
- A pin needs the
project.settings.writepermission. Only a person can set it. A session token gets403with codeagent_session_forbidden. A session that needs another provider passesprovideron create instead. - A switch to a different provider prepares the project’s images on the target, verifies them, then activates the pin. New sessions use the old provider until the switch completes. The CLI follows the switch for up to 600 seconds (
--timeout <sec>). - The US Region flag (Customize → Settings → Feature flags) places the project’s new Platinum sandboxes in the US region. It changes where compute runs, not where the API, database or archives live. Existing sandboxes keep their region. After a new sandbox image, the first session can wait while Platinum copies the image to the region. If the copy fails, the session starts in the home region instead. That sandbox keeps the home region on restart. A replacement sandbox asks for the US region again. The flag shows only when the operator configured a US region.
Troubleshooting
Open a terminal in the sandbox with kortix sessions shell <session_id>. It reattaches to the existing terminal. --new starts another one. kortix sessions shell <session_id> ls lists terminals, and kill <pty-id> ends one.
The daemon answers three diagnostic routes through the API proxy. Call them with a personal access token (API keys and tokens) as a person who can open the session:
curl -H "Authorization: Bearer kortix_pat_..." \
"https://api.kortix.com/v1/p/<external_id>/8000/kortix/logs?source=all&tail=1000"
Replace logs?source=all&tail=1000 with health or diag for the other two routes. On a self-hosted instance, use its API host.
| Route | Returns |
|---|---|
GET /kortix/health |
JSON: status (ok, error, or the harness state), boot_error, harness.id (opencode or pi), and capabilities. |
GET /kortix/logs |
Plain text. source is daemon (default), opencode on an OpenCode session, or all. tail is 500 lines by default, 5000 at most. |
GET /kortix/diag |
One JSON report: harness state, boot timeline, memory, disk and load, and the tail of the logs. tail is 200 by default, 2000 at most. It holds no secrets. |
| Symptom | What to do |
|---|---|
| The header shows Config failed to load. | The new agent config failed. The session runs the last working release, or the platform default config. Fix the config on the base branch. See Config comes from the base branch. |
| The session runs old agents or skills. | Merge the change to the base branch, then run kortix sessions reload <session_id>. |
| The image build failed. | Read kortix sandboxes builds. Reproduce it with kortix sandboxes build --local, or run kortix sandboxes fix. |
| A preview shows nothing. | Open kortix sessions shell <session_id> and check that a server listens on that port, for example with lsof -iTCP -sTCP:LISTEN. Start long-running servers in tmux. |
| A turn stopped with a memory message. | The sandbox reached 92% of its memory. Kortix first stops the largest process that is not the agent runtime, up to 2 processes. If memory stays at 92%, it ends the turn. Raise memory on the template (see Hardware and Docker), or run fewer heavy processes at once. |
docker cannot reach the daemon. |
Set container_runtime: true on the template, install Docker in the Dockerfile, and read /var/log/dockerd.log. |
400 Unknown or disabled sandbox provider on create. |
The provider value is not on for this instance. Use kortix sandboxes provider to see the enabled providers. |