FAQ. Questions about how it works.
Which stacks does Karts run?
Web apps that use Postgres, MySQL 8.4 or SQLite, on Node 22 or 24, Python 3.12 or 3.14, Go, Ruby 3.3 or 3.4, or PHP 8.3. Karts applies numbered SQL migrations itself, including Prisma's and golang-migrate's, or runs your own tool (Knex, Django, Alembic, Rails, Laravel and others) with migrate:. Up to eight processes can share an environment, with Redis, a mail catcher and an S3 bucket if you ask for them. Other databases, and message queues other than Redis, are not available. In our last sweep of 20 real apps, 19 ran on Karts, 18 of them with only a karts.yml: see Compatibility.
Why was my migration refused as already taken?
Another branch in the project already claimed that number with karts up, or main already has it. The message names the branch and the next free number. Rename your file to that number and run karts up again. Claims are released when the claiming branch's migration reaches main with the same contents, when its author runs karts claims release, or after the branch has gone unused for a while.
Which migrations count as destructive?
The check is rule-based. In SQL migrations it refuses DROP TABLE, DROP SCHEMA, DROP DATABASE, DROP OWNED, ALTER TABLE … DROP COLUMN, TRUNCATE, DELETE without a WHERE, UPDATE without a WHERE (unless it only fills columns the same migration added), column type changes that can lose values, DROP TYPE, DOMAIN or EXTENSION … CASCADE, and a file it cannot parse. Renames and other DROP … CASCADE statements are warnings. Dropping an index, or a DELETE or UPDATE with a WHERE, is neither. With migrate:, Karts compares the schema before and after your tool runs and refuses dropped schemas, tables and columns, narrowed column types and emptied tables. Neither catches every way to lose data, so keep reviewing migrations. Pass --allow-destructive when you mean it.
My branch edits a migration main already ran. What now?
Karts refuses it, because appending it to main's template would not give the same database as a fresh build. Add a new migration instead, or run karts up --from-scratch to build the database from all of your branch's migrations and its seed.
Where does the template database come from?
From the merge base of your branch and origin/<default branch>: Karts runs that commit's migrations and seed once, freezes the database disk, and clones it for every branch built on that base. Pass --base to choose another ref. The template is rebuilt when the base changes.
Do rows I write survive the next karts up?
In the cloud, no. Each karts up builds a new revision whose database starts from the template again, and the CLI says so when it switches. Seed data, or a masked snapshot, is the way to have rows in every environment. To copy a running app with the rows it has now, use karts fork.
On your Mac, yes: a repeat karts up --local keeps a Postgres database and runs only the new migrations. --fresh-db starts it again. See Local mode.
Are dependencies installed every time?
Not when they can be cached safely. When install is npm ci, every package comes from the registry with an integrity hash, and nothing has an install script, Karts installs once and reuses the result. Flags such as --legacy-peer-deps and --omit=dev are fine: each set of flags gets its own cache. karts up says when the install came from the cache. A command that runs other commands with &&, sets variables, or uses pnpm or Yarn runs inside the VM on every build, for now.
My app never becomes ready.
Check that it listens on $PORT on all interfaces (0.0.0.0), not only on 127.0.0.1; Karts tells you if that is the problem. Then check that the health path answers with a status below 500. karts logs --failed shows the failed revision's output.
What gets uploaded?
The working tree as it is on disk: tracked files that exist, plus untracked files git does not ignore. Not .git/. Only files the server doesn't already have for your project are sent. --commit uploads one commit's tree exactly. At most 50,000 files, 512 MiB in total and 100 MiB per file. Git submodules are uploaded as their checked-out files, so run git submodule update --init --recursive first. See Upload limits.
Can my build download packages?
From npm, PyPI, the Go module proxy, RubyGems and Packagist, yes: build steps reach them through a Karts registry proxy, and npm, pnpm, Yarn, pip, uv, Go, Bundler and Composer work as written. The proxy also serves Prisma's engines, GitHub release and commit downloads, Cypress and browser downloads. While packages install and services build, public github.com, Google Fonts and a few download hosts are reachable too. Every other host is unreachable during a build. Your running app reaches nothing outside the VM unless your karts.yml turns on internet access. See Network.
Can I test sign-in, email and payments?
Yes, with fakes: stand-ins for outside services that never call the real ones. List them in karts.yml, as in fakes: [sendgrid, google-login, stripe].
- Email sent through SendGrid, Resend, Postmark, Mailgun or the Gmail API lands in the branch's test inbox (with
mail: true). - “Sign in with Google” or GitHub shows a test sign-in page with test users. Agents can sign in without a browser.
- The Stripe stand-in runs checkout, payments with test cards (declines too), subscriptions and refunds, and sends your app signed webhooks.
- Any other API gets canned answers from
stubs:, answers from its OpenAPI spec withmocks:, or replays of real calls withrecordings:. To call a real sandbox, turn oninternet:.
karts fakes calls shows everything your app sent, with secrets removed. Some sign-in libraries need one line changed, and on your Mac an app that ignores HTTPS_PROXY can still reach the real internet. See Fake outside services.
My frontend is on Vercel or Netlify. Can I still use Karts?
Yes, for the backend. Point the preview at the branch's Karts URL with a rewrite, or list the preview's origin in allow_origins. See Frontends.
Can I use it from CI?
Yes. Set KARTS_TOKEN, and run karts up --commit HEAD --branch "$BRANCH". The same branch updates the same environment on the next run.
Where do secrets go?
Not in karts.yml: env: is for plain settings you would commit anyway. Run karts secret set NAME, and list the name under secrets:. Each branch can have its own value. The app gets it, logs show ‹secret›, and values are stored encrypted. Environment URLs are still public, with no sign-in, so use test keys, not production ones. See Secrets.
Can I test with real data?
With a masked copy, yes. karts db snapshot runs on your machine: it reads your Postgres database without changing it, swaps personal data for fakes, and keeps the links between tables. Only the masked file is uploaded, and it is stored encrypted. Branches load it with seed_snapshot: or karts up --snapshot. Never put raw personal data in Karts. See Masked copies of real data.
Can I run it on my own machine?
On a Mac, yes. karts up --local runs the same karts.yml with no account and no upload. Each branch gets its own database and a *.localhost URL that stays the same. A front end and its API in separate repositories find each other with links:. It needs Docker Desktop; Apple Silicon is tested. See Local mode. Running Karts on your own servers is not something you can install yet: email hello@karts.kartikey.fyi to talk about it.
Karts broke. How do I tell you?
Run karts report FILE, or let your agent do it. It shows you the report and asks before sending. Keys and passwords it can spot are removed before it is stored. See karts report.
Something else?
Email hello@karts.kartikey.fyi.