Deploy from your terminal.
The Canner CLI tars your project, ships it to Quebec, and streams the build log back to your shell. Works anywhere Node 20 runs — macOS, Linux, Windows, and Cursor’s integrated terminal. No git required.
Quick start
Three commands. canner login stores an API token from canner.ca/dashboard/identity/api-keys. canner init creates a project and writes canner.json in the current directory. canner deploy --follow ships it and streams the build log.
canner login cd my-site canner init canner deploy --follow
Commands
canner loginPrompt for an API token and store it at ~/.canner/credentials (mode 0600).canner logoutDelete ~/.canner/credentials from this machine. The token stays valid until revoked.canner whoamiShow the email and plan this CLI is signed in as.canner projectsList your projects.canner initCreate a new Canner project and write canner.json in the current directory.canner statusShow one project's state — URL, status, source, last request.canner openOpen the tenant URL (or, with --dashboard, the dashboard project page) in your browser.canner deployTar the directory and upload it. --follow streams the build log and exits with the build's success code, so it works as the last step of a CI pipeline.canner redeployTrigger a rebuild from the GitHub repo's HEAD without uploading. GitHub-connected projects only.canner deploymentsList recent deployments, or with an id, show one deployment's build log (add --follow to tail if still in progress).canner cancelAbort the in-flight build (or a specific one by id).canner logsStream the running tenant's runtime logs. --lines N for history, --follow to keep streaming.canner tokenList, create, or revoke the cnr_ API tokens that authenticate the CLI and API.canner envList, set, or remove a project's environment variables (env set KEY=VALUE, --sensitive to hide).canner domainsList, add, verify, or remove a project's custom domains.canner dbList a project's databases, or init/create one. Starter has one database per project; paid plans can add more. `db init --dedicated [--pitr-days N]` creates the primary on its own dedicated instance with point-in-time recovery; if the project has no dedicated-instance unit, that adds one ($9/month), so it shows the price and needs `--yes`.canner billingRead-only. `billing addons` lists the add-ons your organization owns, their price, and which projects they are assigned to. `billing preview <addon> <quantity>` shows the monthly change a quantity would cause (for example `dedicated_instance 3`). Nothing is charged or changed.canner db export / importDownload a pg_dump of a database (every plan), or restore one into a database (every plan).canner db migrate-in / migrate-out / copyStream a database in from (or out to) any public Postgres server, or copy one Canner database into another (copy needs a paid plan).canner db rm / pg-version / opsDelete an additional database, move one to another Postgres version, or check on a running operation.canner db external / rotateTurn external access on/off with an IP allowlist, or rotate a database's password. Every plan.canner db backup-policy / backup / backupsSet a database's scheduled-backup interval and retention, take a manual backup, or list/download/restore/delete backups. Starter: at most one backup a day, and restore in place instead of into a new database.canner db diff / inspect / backups --verify / alertsRestore into a temporary inspect database and compare it with the live one, keep or extend it, test-restore a backup, and manage email/webhook alerts. Every plan.canner db resourcesShow and change the memory budget your app shares with its dedicated instance, and apply it (restarts the instance briefly). Every plan.canner db extensions / tuning / restartInstall pgvector, PostGIS, pg_trgm, pg_cron and other extensions on the dedicated instance; pick a workload preset, set timeouts and a maintenance window; restart the instance now. Paid add-on.canner db replicationTurn on logical replication (restarts the instance), create publications, watch replication slots and their WAL cap, and give a subscriber a replication login with `canner db roles <name> create <n> --replication`. Paid add-on.canner db subscriptionsFollow an external Postgres server live: add a subscription from a connection URL (read from an env var or stdin, never printed) and publication names, then refresh, pause, resume or remove it. Changes are applied with your table owner's rights. Paid add-on.canner db metrics / monitoring / insights / logsDatabase metrics, and on the dedicated instance: switch on query insights and logs (restarts the instance briefly), list the slowest queries, and read the Postgres log. Every plan.canner db roles / offsiteCreate read-only or read-write roles with their own connection strings, and manage up to five S3-compatible off-site destinations (add, edit, test, remove) that receive copies of your backups. A read-write role's migration ledger (schema_migrations and similar) stays SELECT-only automatically; `canner db roles <name> write-exclusions <role> <table>` protects any other table the same way, live, without a new connection string. Every plan.canner db roles (groups, logins, audit) / injectRun your own privilege model on the dedicated instance. Create a group (a role nobody logs in as, named by you, that your migrations grant to and attach row-level security policies to; `--adopt` takes over an existing NOLOGIN role of that name) and a login that is a member of your groups and holds nothing else, with no blanket read access. `canner db roles <name> member-of` changes a login's groups live, `canner db inject <name> <login>` picks which login the app receives as DATABASE_URL (the owner by default; it takes effect on the next deploy), and `canner db roles <name> audit` reports what every role can actually do (tables, row-level security, policies) and what looks wrong; `--strict` fails a CI run on any warning. Moving a database or changing its Postgres version is not yet possible while it has groups or logins. Every plan.canner db checkA read-only security and performance check of any database, on every plan. It reads the catalog (never your data) and lists: row-level security policies that allow every row, several permissive policies that combine with OR, functions anyone can execute as their owner (SECURITY DEFINER) or without a fixed search_path, views that bypass row-level security, schemas where every role can create objects, tables without a primary key, unindexed foreign keys, duplicate indexes and tables with autovacuum off. On a dedicated instance it also reports the roles: the app connecting as the owner, blanket read access and a writable migration ledger. `--category security|performance` filters, `--strict` exits 1 on any warning for CI, and `--json` prints the whole result. The same check is in the dashboard on each database.canner db least-privilegeRun the app as a limited role in one step (dedicated instance). It creates a read-write role (`--label` to name it, default `app`) that can read everything and write data but cannot change the schema, keeps every well-known migration ledger (Prisma, Rails, Django, Flyway and others) read-only for it, even one created by your first migration, and injects it as `DATABASE_URL`. The database owner, which can change the schema, is given to the release command only as `DATABASE_URL_MIGRATE` (never to the build or the running app), so run your migrations there. Safe to run again. Takes effect on the next deploy. `canner db roles audit` shows the result.canner db instance upgradeUpgrade a dedicated instance to a newer Postgres major version in place. `canner db instance upgrade` reports what would stop it; `upgrade run --yes` does it (`--pg 18` names the version). Every database, role, grant, policy and extension carries over and the connection strings do not change, so nothing you built with your own SQL has to be redone. Every database on the instance is offline for a few minutes; if the upgrade fails the instance is left as it was. Point-in-time recovery starts a fresh window afterwards. The previous version is kept for 7 days: `upgrade rollback --yes` goes back (and loses what was written since), `upgrade finish --yes` discards it.canner db instance / pitr / pitr-restore / moveCreate a dedicated Postgres instance, manage point-in-time recovery, recover a database as of a moment into a new one, copy the recovery archive off-site (`canner db pitr --offsite <destination>`), or move a database onto/off the instance. Paid add-on.JSON output
Add --json to any command for machine-readable output — tables and progress are suppressed and a single JSON value is printed, so it pipes straight into jq or an AI agent. Every command wraps an endpoint in the documented REST API.
canner projects --json | jq -r '.[].slug'
What gets uploaded
The CLI tars the current directory, excludes the “build noise” defaults, and streams the archive to the same upload endpoint the dashboard’s drag-drop uses. The server-side build pipeline detects the framework and runs install / build / deploy just like a git push.
Always excluded: node_modules, .git, .next, .nuxt, .turbo, .vercel, .svelte-kit, .DS_Store, Thumbs.db. Add a .cannerignore file for additional excludes.
CI / GitHub Actions
--follow exits with the build's success code, so a failed build fails the workflow.
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npx @canner-ca/cli@latest deploy --follow
env:
CANNER_TOKEN: ${{ secrets.CANNER_TOKEN }}Cursor / AI agents
Works in Cursor's integrated terminal with no extension or MCP server. Agents can invoke it as a shell tool:
canner deploy --follow
Plan size caps
Same per-tier compressed-archive cap as the dashboard upload: Starter 100 MB, Live 500 MB. The typical thing that pushes a project over is a public/ folder full of media — drop it into .cannerignore and serve it from external storage if so.
What it stores and sends
The CLI writes two files and nothing else: ~/.canner/credentials (your API token, mode 0600, written by canner login) and canner.json in your project directory (your project slug, written by canner init). It changes no system settings, shell profiles, or PATH entries. Network requests go only to api.canner.ca. There is no telemetry, analytics, or crash reporting of any kind — the CLI makes no request you didn't ask for by running a command.
Removing it
canner logout deletes the stored credential from this machine; npm uninstall -g @canner-ca/cli removes the package. Logging out is local only — the token itself stays valid, because the same token may be running in CI or on another machine. To revoke it everywhere, run canner token revoke, or use the API tokens page in your dashboard.
canner logout npm uninstall -g @canner-ca/cli
Use of the CLI and the Canner service is governed by our Terms of Service and Privacy Policy.
Full docs and source: see the README at tools/cli/ in the canner repo on GitHub.