Deploying
zsql deploy packs the project directory into a tar.gz, POSTs it to the service under the checked-out git branch, and prints what the service loaded: counts, warnings and test results. zsql check does the same against the validate route and keeps nothing.
zsql check
zsql check
Identical to zsql deploy --dry-run: the same archive, the same loader, the same output with validate/validated in place of deploy/deployed. Nothing is stored. See Modeling with zsql for a transcript.
zsql deploy
zsql deploy [--dry-run] [--watch]
$ git branch --show-current
main
$ zsql deploy
deploy tpcds (6214 bytes) as tpcds/main (the production branch) to https://app.0sql.io
deployed tpcds/main: 6 tables, 42 fields, 5 joins, 9 paths, 1 policies
PASSED store net paid by category
PASSED catalog quantity by month
PASSED date range
tests: 3 passed, 0 failed
The first line says what is being sent and where: the project name, the archive size, the uid/branch it lands on, and (the production branch) when the branch equals production_branch in project.yml. The second line is the service’s summary. Then one warning: line per warning, one line per test, and the test total. A project with no tests/*.yml prints no tests deployed (tests/*.yml) instead.
What goes in the archive
| Shipped | Never shipped |
|---|---|
project.yml | .zsql, .strata |
security.yml | .git |
datasources.yml with secret keys removed (password, private_key, personal_access_token, secret_access_key, access_key_id, oauth_client_secret, oauth_client_id, api_key) | data files, anything not listed on the left |
every .yml / .yaml under models/ and tests/ |
Exit codes
zsql deploy exits non-zero when the service rejects the archive (a DeployError naming the file and message) and when any test fails. Note the second case: the HTTP deploy succeeds with status 200 and the branch is live with the new model, but the CLI prints tests failed and exits non-zero so a CI job stops. Check the output, fix the model or the test, deploy again.
$ zsql deploy
deploy tpcds (6240 bytes) as tpcds/main (the production branch) to https://app.0sql.io
deployed tpcds/main: 6 tables, 42 fields, 5 joins, 9 paths, 1 policies
PASSED store net paid by category
FAILED catalog quantity by month
--- generated
SELECT
DATE_TRUNC('month', T1."d_date")::DATE AS "Month(Date)",
sum(T0."cs_quantity") AS "Catalog Quantity"
FROM
catalog_sales T0
JOIN date_dim T1
ON T0.cs_sold_date_sk = T1.d_date_sk
GROUP BY
DATE_TRUNC('month', T1."d_date")::DATE
PASSED date range
tests: 2 passed, 1 failed
Error: tests failed
$ echo $?
1
The production branch
Deploying while main (or whatever production_branch names) is checked out updates what production reads. The output says so: as tpcds/main (the production branch). On the service a project owner can mark the production branch protected; then deploying to it needs owner access, and anyone else gets Forbidden: deploying to the protected production branch 'main' of tpcds needs an owner.
—watch
$ zsql deploy --watch
deploy tpcds (6214 bytes) as tpcds/main (the production branch) to https://app.0sql.io
deployed tpcds/main: 6 tables, 42 fields, 5 joins, 9 paths, 1 policies
tests: 3 passed, 0 failed
watching /home/you/tpcds for changes (ctrl-c to stop)
Deploys once, then polls the project directory every 500 ms for a changed .yml or .yaml file and redeploys 200 ms after a change. Errors are printed and watching continues. Pair it with the repl in a second terminal to edit a model and query it as you go.
Branches
The service branch is the checked-out git branch. There is no separate “promote” step: a branch is deployed by checking it out and running zsql deploy, and main is updated by merging and deploying main.
zsql deployonfeature/returnscreates or updatestpcds/feature/returns. Nothing else changes.- Each deployed branch is a complete, isolated model. An application names the branch it wants in the URL:
/projects/tpcds/branches/feature/returns/sql. - A query key granted a project without a branch reads
production_branch. A key granted*reads every branch; a key granted one branch name reads that one. --branch NAMEoverrides the git branch. CI checkouts are often detached, so pass it there (see CI/CD).
Deploying from a dirty working tree deploys the files on disk, committed or not.
zsql status
The deployed branch’s summary, as the service returns it.
$ zsql status
{
"project": "tpcds",
"branch": "main",
"deployed_at": "2026-10-01 15:04",
"datasources": 1,
"tables": 6,
"fields": 42,
"joins": 5,
"paths": 9,
"policies": 1,
"tests": 3,
"warnings": []
}
A branch that was never deployed answers NotFound: no deployment for project tpcds branch feature/returns.
zsql list
One line per deployment the key can see, across projects and branches.
$ zsql list
tpcds main 6 tables, 42 fields, 1 policies, 3 tests
tpcds feature/returns 7 tables, 48 fields, 1 policies, 3 tests
zsql list works outside a project directory too; it only needs a key and a server.
zsql remove
Removes the deployed branch from the service. The confirmation is a flag.
$ zsql remove
Error: this removes tpcds/feature/returns from https://app.0sql.io; add --yes to confirm
$ zsql remove --yes
{
"removed": "tpcds/feature/returns"
}
Removing a branch removes its deployment, not the project or the git branch.
zsql test
Runs the tests the branch was deployed with, on the service, without redeploying. Useful after a key or policy change, and as a cheap health check of a branch.
$ zsql test
PASSED store net paid by category
PASSED catalog quantity by month
PASSED date range
tests: 3 passed, 0 failed
Exits non-zero with tests failed when any fail. The format is on Tests.