Self-hosting
Run your own private instance of Kortix on a VPS you control — one Docker Compose stack, one CLI.
Run the full Kortix stack — frontend, API, LLM gateway, and the official
Supabase distribution — as one Docker Compose project, on any box you
control. Agent sessions still run on a sandbox provider — Daytona
or Platinum are the recommended, standard
choices (managed compute, not part of this box); E2B is
also supported. local-docker (same-machine containers) is available too,
listed last — it is experimental and not recommended for production.
For production, Kortix self-host is VPS-first. The supported way to run it for real is your own VPS or server with a persistent domain pointed at it. A local machine with no public domain can run the identical stack via a Cloudflare tunnel — not recommended for production, but it's the standard way to run Kortix locally for development: the same stack becomes a real instance you build and test against. See Reachability below, and Developing with Kortix for the local-dev workflow.
Quickstart: VPS + domain
Point DNS at the box
Create an A/AAAA record for your domain (and its API subdomain, api.<domain>
by default) pointing at the box's public IP. Ports 80 and 443 must be
reachable from the internet — the bundled Caddy reverse proxy uses ACME
HTTP-01 to issue a TLS certificate automatically.
Install and initialize
curl -fsSL https://kortix.com/install | bash
kortix self-host init --domain kortix.example.cominit is a short guided flow (skippable non-interactively with flags, or
--yes for defaults) that asks, in order:
- Reachability — confirms the domain above, or
--tunnel cloudflareif you have no public domain (local machine / evaluation only). - Admin email — which account gets platform-admin on first sign-up.
- Deployment shape — whether you hold an Enterprise license (SSO/SCIM/RBAC/audit), and whether to restrict account creation to the admin (on by default — see "Configuration" below).
- Sandbox provider —
daytona(default) orplatinum(recommended),e2b(also supported), orlocal-docker(experimental, listed last, not recommended for production — same-machine containers, noticeably slower since it builds sandbox images locally), plus its API key. - Pipedream (optional) — the 3,000+ app connector catalog.
- Update policy — auto-update on/off, channel (
stable/latest), and the daily update window.
On a bare Linux box, scripts/kortix-selfhost-up.sh (in the main repo) does
this whole step for you — installs Docker and the CLI, then runs init with
your --domain/--email.
Start the stack
kortix self-host startPulls images and brings the stack up. kortix self-host status / logs /
doctor are your friends while it comes up.
Finish in the dashboard
Open https://kortix.example.com and sign up with the admin email from step
2, then:
- Settings → Git — connect a GitHub App (or PAT) so the platform can create project repos.
- Settings → Model — connect your own model key (BYOK: Anthropic, OpenAI, OpenRouter, etc.).
That's a complete, working instance. From here, use the main kortix CLI
against it exactly like you would against Kortix Cloud:
kortix hosts use selfhost # already registered + pointed at your instance by `init`/`start`
kortix login
kortix whoami
kortix projects ls
cd your-project && kortix shipReachability
Agent sessions run on a cloud sandbox VM — outside your network — that
calls back to this instance's API over the public internet. That callback is
the one thing that can never be a loopback/internal address: the sandbox
itself is perfectly reachable compute, it's this API being reachable from
the sandbox that has to be real. init/configure only ever offer two
choices:
--domain <domain>— you have a public domain pointed at this machine. The recommended, production path: turns on the bundled Caddy reverse proxy + ACME TLS.--tunnel cloudflare— no public domain. Exposes the API through a Cloudflare tunnel with zero DNS/firewall setup. This is the path for local development and evaluation — not recommended for production; the default quick-tunnel URL is ephemeral (a fresh one every restart).
There is no third "local-only" mode to pick. If you're on a local machine
with no public domain, use the tunnel — passing neither flag just leaves
reachability unconfigured, and agent sessions won't run until you set one.
init/start print a warning every time that's the case.
Switch any time with kortix self-host configure (interactive) or the same
flags on init/update.
Configuration: one .env, one command
Every value this instance's Docker services read — ports, URLs, feature
flags, and every secret — lives in a single .env file per instance
(~/.config/kortix/self-host/<instance>/.env), consumed via Compose's
env_file:. Nothing leaves your machine. One command reads and writes it:
kortix self-host env ls # every value, grouped by service; secrets masked
kortix self-host env ls --show # reveal masked values
kortix self-host env set DAYTONA_API_KEY=... # set anything; restarts only the services it affects
kortix self-host env rotate SUPABASE_JWT_SECRET # regenerate a rotatable generated secret
kortix self-host env rotate --all-generated # regenerate every rotatable generated secretBy default, only the admin (the account(s) listed in KORTIX_PLATFORM_ADMIN_EMAILS)
can create new accounts/organizations on a self-hosted instance — everyone
else joins an existing account by invitation or SSO. This doesn't touch
signups, teams, or SSO/JIT provisioning; it only gates spinning up a brand
new organization, and init/configure ask about it as part of "Deployment
shape". Disable it to let any signed-in user create accounts freely:
kortix self-host env set KORTIX_RESTRICT_ACCOUNT_CREATION=false KORTIX_PUBLIC_RESTRICT_ACCOUNT_CREATION=falseUpdates
Every instance runs an in-compose kortix-updater service (on by default)
that pulls the configured channel's newest images on a schedule, migrates,
and rolls the stack forward with zero downtime. stable (default) or
latest; pin an exact version with kortix self-host update --tag <version>.
kortix self-host update # converge to the configured channel/version right now
kortix self-host version # what's running vs. what's publishedA migration that needs a brief downtime window sets KORTIX_ALLOW_DOWNTIME
— there's no dedicated flag for it. A release that requires it will say so;
set it yourself with kortix self-host env set KORTIX_ALLOW_DOWNTIME=1.
Run a specific version or your own build
kortix self-host init --channel latest # track the bleeding-edge moving tag instead of stable
kortix self-host init --version 0.10.1 # pin an exact released version
kortix self-host init --version dev-a1b2c3d # pin a published dev build (e.g. from a branch's CI)Tracking dev/staging/prod
CI keeps kortix/kortix-{api,gateway,frontend}:dev / :staging / :prod
pointed at whatever's actually deployed to that live environment right now
(re-tagged after each environment's deploy succeeds — never a rebuild).
--version/--tag already accepts any published tag, so tracking one is the
same recipe as any other moving tag:
kortix self-host init --tag dev --auto-update on # track dev.kortix.com's current build, nightly
kortix self-host init --tag staging --auto-update on # track staging.kortix.com
kortix self-host init --tag prod --auto-update on # track api.kortix.com's current releaseThese may break at any time — dev especially — and are for testing against
a specific live environment, not production self-hosting. stable (the
default) is the only curated, promoted channel.
kortix/kortix-sandbox isn't published by any of these pipelines yet, so the
sandbox image doesn't move with dev/staging/prod — a box keeps whatever
SANDBOX_IMAGE it already has.
Testing a locally-built image (never pushed to any registry):
docker build -t kortix/kortix-api:mytest apps/api
kortix self-host init --version mytest --local-images
kortix self-host start--local-images skips docker compose pull (a locally-built tag isn't on
any registry) and forces auto-update off — a box running an unpublished
build must never let the nightly updater try to pull it from nowhere.
Uninstalling
kortix self-host uninstall # interactive: type the instance name to confirm
kortix self-host uninstall --yes # non-interactive / scriptsStops the stack, removes its containers and Docker volumes, deletes the
instance's config directory, and clears the CLI's selfhost host entry if it
points at this instance. This permanently deletes all data for that
instance — back up first if you might need it (see the runbook). Reinstall
any time with init + start.
Provisioning a VPS with Terraform (optional)
If you'd rather provision the box declaratively than run a script by hand,
infra/terraform/modules/selfhost-ec2 is a thin, optional convenience
provisioner — not a different deployment system. It creates the EC2
instance, an encrypted EBS data volume, a security group, an Elastic IP,
optional Route53 records, and a scheduled snapshot policy, then cloud-init
runs the exact same kortix self-host init/start described above.
module "kortix_selfhost" {
# The double slash is Terraform's repo//subdirectory separator — required
# syntax, not a typo.
source = "github.com/kortix-ai/suna//infra/terraform/modules/selfhost-ec2"
domain = "kortix.example.com"
tags = { Project = "kortix-selfhost" }
}Learn more
- Self-hosting architecture — how the Compose stack fits together.
- CLI reference — every
kortix self-hostsubcommand and flag. - Operator runbook — the full day-to-day reference: troubleshooting, backup/restore mechanics, SMTP, using the CLI from a different machine, and the auto-updater's internals.