Skip to content

CLI Commands

Add a team to the CLI by pasting its API key. ZevCloud API keys are team-scoped (one key = one team), so a user who’s a member or admin of multiple teams configures each one separately and switches between them with team switch.

CLI
zevcloud login

If you’re already logged in to one team, login adds the new team alongside it and makes the new one active. Run it again per team you belong to.

Remove the active team from the CLI. Other configured teams remain.

CLI
# Log out of just the active team zevcloud logout # Wipe every configured team zevcloud logout --all

Show the active team, its API URL, and a list of any other teams you’ve configured.

List every team you’ve configured. The active team is marked with a check.

CLI
zevcloud team list

Switch the active team. Pass a name, slug, or id to switch directly, or run with no argument for an interactive picker.

CLI
# Interactive picker zevcloud team switch # Direct switch by name zevcloud team switch acme-corp

Remove a configured team’s stored API key. If you remove the active team, the CLI auto-selects another configured team as active.

Override the active team for a single invocation without changing your default. Useful in scripts and one-off cross-team commands.

CLI
# Deploy in the "side-project" team without switching out of "main" zevcloud --team side-project deploy a1b2c3d4-... # List services in another team zevcloud --team acme-corp services

List all projects in the active team.

CLI
zevcloud projects

List services. Optionally filter by project.

CLI
# All services across all projects zevcloud services # Services in a specific project zevcloud services 6412b9be-13ef-469c-b451-311439a8a9cd

Interactive wizard that creates a new service from the current directory and triggers its first deploy. Walks you through project, source, framework, and plan picks, then handles the build end-to-end.

CLI
# Run from inside your project directory cd my-app zevcloud init

What init does, in order:

  1. Detect the source mode. Looks at the current directory:
    • GitHub remote found (a .git/ with a GitHub origin URL): defaults to GitHub-source. The CLI confirms the repo + branch with you and probes whether the repo is public or private. Public repos clone directly. Private repos require the ZevCloud GitHub App to be installed with access to the repo — if it isn’t, the CLI prints an install URL and waits for you to grant access in your browser, then continues.
    • No git, or remote isn’t GitHub: defaults to direct terminal upload. The CLI tars your current directory (respecting .gitignore and .zevcloudignore) and uploads it via a pre-signed URL. No GitHub setup required — useful for vibe-coder projects that haven’t been pushed anywhere yet.
  2. Pick a project + environment. Lists existing projects or offers to create a new one inline. New projects get a default Production environment automatically.
  3. Existing services in the chosen environment are listed first, so you can redeploy or run other actions (logs, restart, env, stop) against them instead of creating a new one. Picking + Create new service continues with the wizard.
  4. Service name + slug. The slug becomes part of your auto-generated URL (<slug>.zevcloud.app). The CLI checks the slug is available before continuing.
  5. Framework detection. Reads package.json and common config files to pre-select the right framework — detected ones show (Detected — recommended) at the top of the picker so you can hit Enter.
  6. Plan picker. Fetches the live plan catalog and renders a comparison. Picking a paid plan triggers a credit pre-flight — if you have plan credits available, the CLI confirms coverage. If not, the service is created anyway and an invoice URL is printed for you to settle in the dashboard. Free plan skips this step.
  7. Confirm summary of everything that’s about to happen. Cancel here if anything looks wrong.
  8. Create + upload + deploy. Creates the service, runs the source upload (for upload-mode), triggers the first build, and polls until it succeeds or fails. Prints the live URL at the end.

Upload mode supports any framework with an optimised Dockerfile template — currently Next.js, Astro, Nuxt, Remix, SvelteKit, Vite, and Node.js. The framework picker in init automatically filters to the supported set when you’re in upload mode, so you can’t pick something that would fail at build time.

If your stack isn’t in the list (Python, Go, Ruby, …), use a GitHub repo for the source and the build runs through nixpacks instead.

Upload mode has a 200MB hard cap on the source tarball (plan-independent). If your project is bigger than that, almost always one of two things is happening:

  • node_modules isn’t in your .gitignore. Add it.
  • You’re shipping large binary assets that belong on a CDN.

The cap stops accidental abuse but isn’t tied to your plan; production app sizes are typically a few MB.

Controlling what’s uploaded with .zevcloudignore

Section titled “Controlling what’s uploaded with .zevcloudignore”

When you deploy in upload mode, the CLI tars your current directory and uploads it. The exclude list is built from three sources, applied in order:

  1. Built-in hard excludes — always skipped: node_modules, .git, framework caches (.next, .nuxt, .svelte-kit, .astro, .turbo, .cache, .parcel-cache), build output (dist, build), local env files (.env.local, .env.development*, .env.test*), .DS_Store, .vscode, .idea, and *.log patterns.
  2. .gitignore — every pattern in your repo’s .gitignore is honored. Most projects need nothing else.
  3. .zevcloudignore — optional. Same syntax as .gitignore, but only the CLI reads it. Use it when something belongs in your repo but should not be in your deploy (test fixtures, design source files, internal docs).
CLI
# .zevcloudignore example # Test data we don't want shipped to the runtime container test-fixtures/ *.test.snap # Design source files (large .psd / .fig exports) design/ # Internal-only docs RUNBOOK.md

Pattern syntax (subset of gitignore):

PatternMeaning
fooMatch foo anywhere in the tree (file or directory)
foo/Same as above — the CLI treats both forms identically
/fooLeading slash is stripped; behaves the same as foo
*.logGlob: any path segment matching the pattern (single * only, no **)
# commentLines starting with # are ignored

Patterns are deliberately conservative — there’s no !negation, no ** recursion, and no per-directory .gitignore. If you need fine-grained inclusion, point the source at a Git repo instead.

Build-time vs runtime environment variables

Section titled “Build-time vs runtime environment variables”

ZevCloud injects two distinct kinds of env vars into your service. Knowing which is which avoids “I set DATABASE_URL and the build failed because it can’t find it” confusion:

Where it’s availableWhat it’s forHow to set it
Build-time onlyCompile/bundler inputs read by the build script. Next.js NEXT_PUBLIC_* baked into the static bundle, Vite VITE_* vars, Astro public vars, build-step API keys for prerendering.Set via zevcloud env set before the next deploy. Anything you set is available in both stages by default.
Runtime onlyServer-side config the app reads after boot: DATABASE_URL, third-party API secrets, feature flags. Not embedded in the bundle, so changing them is a restart, not a deploy.Same — zevcloud env set, then zevcloud restart. No rebuild needed.

The rule of thumb: if your framework’s docs say a variable must be set at build time (e.g. Next.js NEXT_PUBLIC_*), set it first and then run zevcloud deploy. If it’s pure runtime config, zevcloud env set + zevcloud restart is enough — the existing image is reused.

For secrets, pass -s / --secret:

CLI
# Secret runtime value (masked in dashboard, never echoed) zevcloud env set <serviceId> DATABASE_URL postgres://... -s # Public-bundle build-time value (Next.js example) zevcloud env set <serviceId> NEXT_PUBLIC_API_URL https://api.example.com zevcloud deploy <serviceId> # rebuild so the value is baked in

Most projects need nothing beyond the framework’s default config. A few stacks have quirks the build picks up automatically — knowing them helps when the runtime isn’t behaving as expected:

  • Next.js — no output: 'standalone' requirement. The runner copies the full .next directory and runs npm start, so any Next.js config works. Pages Router and App Router are both supported. NEXT_PUBLIC_* vars must be set before the deploy (build-time).
  • Astro — needs the Node SSR adapter (@astrojs/node) if you have server routes or middleware. The build’s start command is node ./dist/server/entry.mjs. Pure-static Astro projects work too, but consider deploying as a Static Site service for better performance.
  • Nuxt 3 — runs node .output/server/index.mjs. Universal/SSR mode is the default; if you set ssr: false in nuxt.config.ts (SPA mode), the build still works but you may want a Static Site service instead.
  • SvelteKit — needs @sveltejs/adapter-node. The runner executes node build. The default adapter is adapter-auto, which usually resolves correctly, but pinning adapter-node removes ambiguity.
  • Remixnpm start runs whatever your package.json defines as start. The default Remix template uses remix-serve build/index.js, which is fine; custom servers (Express, Hono) work too as long as they bind to 0.0.0.0 on the platform-provided PORT.
  • Vite — built artifacts are served via vite preview --host 0.0.0.0 --port 4173. Suitable for SPA testing but not production-grade. For real production SPAs, deploy as a Static Site service instead and skip the Node runtime.
  • Node.js — generic catch-all. Runs npm start. Your package.json must define a start script that binds to 0.0.0.0 on $PORT. Pure-JS APIs, Express servers, custom workers — all fine.

The PORT contract: every web/worker service receives the listen port via the PORT env var. Hardcoding 3000 works for local dev but breaks in production. Always read process.env.PORT.

Lockfile is required. Upload mode looks at pnpm-lock.yaml, yarn.lock, or package-lock.json to pick the install command. If none exists, the build falls back to npm install (not npm ci) — slower and non-deterministic. Commit a lockfile.

ZevCloud has four service types and the CLI commands behave differently per type — the tool figures out the right action automatically based on what you ran it against.

Trigger a deployment of an existing service. The CLI knows the service’s source mode (GitHub or direct upload) and does the right thing automatically — pulls from your configured GitHub branch, or re-tars and re-uploads your current directory for upload-mode services.

When deploying to a service that’s currently running, the CLI shows a confirmation prompt with what’s about to be replaced so a stray Enter doesn’t take down production. The prompt is automatically skipped when stdin isn’t a TTY (CI, agents, scripts) — automation runs without hanging.

The exact action depends on the service type:

Service typeWhat deploy does
Web app / worker (GitHub source)Triggers a fresh build pulling from the configured branch; CLI polls and reports status
Web app / worker (upload source)Re-tars CWD, uploads via the pre-signed flow, triggers the build, polls and reports status
WordPress (not yet provisioned)Provisions WordPress, MariaDB, and SFTP for the service
WordPress (already running)Refuses with guidance — the site is already live; use restart to recycle, or push files via SFTP / wp-admin
Static siteRefuses with guidance — static sites are updated by uploading files (dashboard or SFTP), then restart
DatabaseRefuses — databases don’t deploy; use restart, stop, or the dashboard
CLI
zevcloud deploy a1b2c3d4-...

For Git-based apps the command will:

  1. Trigger the build
  2. Poll status every 5 seconds
  3. Report when live (or if it failed)

Alias for deploy.

Recycle the service container. Works for any service type. Useful after env-var changes, file pushes via SFTP, or to clear a wedged container.

CLI
zevcloud restart a1b2c3d4-...

Brief downtime per service (5–15 seconds depending on type).

Stop a service. Storage and config are preserved. restart brings it back up.

CLI
zevcloud stop a1b2c3d4-...

View runtime logs from a service container.

CLI
# Last 100 lines zevcloud logs a1b2c3d4-... # Last 500 lines zevcloud logs a1b2c3d4-... -n 500 # Follow (tail) logs zevcloud logs a1b2c3d4-... -f

Options:

FlagDescription
-n, --lines <number>Number of lines to show (default: 100)
-f, --followFollow log output (polls every 5s)

Environment variables are most useful on web-app and worker services. They can be set on other types but won’t change runtime behavior — WordPress reads its config from the database, databases use baked-in credentials, and static sites have no runtime env.

List all environment variables for a service.

CLI
zevcloud env list a1b2c3d4-...

Secret variables show •••••••• instead of their value.

zevcloud env set <serviceId> <key> <value> [options]

Section titled “zevcloud env set <serviceId> <key> <value> [options]”

Set an environment variable.

CLI
# Regular variable zevcloud env set a1b2c3d4-... DATABASE_URL postgres://... # Secret variable zevcloud env set a1b2c3d4-... API_SECRET sk_live_... -s

Options:

FlagDescription
-s, --secretMark as secret (hidden in dashboard)

zevcloud env remove <serviceId> <envVarId>

Section titled “zevcloud env remove <serviceId> <envVarId>”

Remove an environment variable by its ID.

Configure CLI settings.

CLI
# Set custom API URL zevcloud config --api-url https://api.zevcloud.net/v1

Options:

FlagDescription
--api-url <url>Set the API endpoint. HTTPS required — plaintext HTTP URLs are rejected to keep API keys off the wire in cleartext.

Show the installed CLI version, the latest version on npm (when known), the time of the last update check, and your Node and platform info. Paste this into bug reports.

CLI
zevcloud version

zevcloud --version (or -v) is also available and prints just the semver string for scripts.

Show the top-level command list, or detailed help for a specific command. zevcloud --help and zevcloud <command> --help work the same way.

CLI
# Top-level help zevcloud help # Help for a specific command zevcloud help deploy zevcloud deploy --help

The CLI honors a few environment variables, useful in CI pipelines and AI agent workflows:

VariablePurpose
ZEVCLOUD_API_KEYAuthenticate without storing a key on disk. Takes precedence over the configured active team when set.
ZEVCLOUD_TEAM_IDPair with ZEVCLOUD_API_KEY to set the team-id header explicitly.
ZEVCLOUD_DISABLE_UPDATE_CHECKSet to true to skip the daily “is there a newer version?” check.
ZEVCLOUD_DISABLE_POSTINSTALLSet to true to silence the postinstall welcome message.
CIAuto-detected. The CLI suppresses the postinstall message and the update-check banner when set.

For a CI runner or AI agent, the recommended pattern is:

CLI
export ZEVCLOUD_API_KEY=sk_live_... export ZEVCLOUD_TEAM_ID=<your-team-id> zevcloud deploy <serviceId>

When both variables are set the CLI never reads or writes the on-disk credential store, which keeps shared runners free of leftover credentials.

When generating an API key, you choose a scope. The CLI is bound to that scope on every request — it cannot escalate.

ScopePermissions
ReadList and inspect projects, services, deployments, env-var keys, logs, DNS, billing. No mutations.
DeployEverything Read can do, plus trigger deployments, restart/stop services, set or delete env vars, verify a custom domain. The right choice for CI and AI agents.
FullEquivalent to a human admin. Adds project/service/environment create + delete, custom-domain management, terminal commands, SFTP password rotation, public DB toggling, and DNS-record management on purchased domains.

Some actions — managing API keys themselves, deleting the team, inviting members, billing changes, buying domains, opening the database GUI, connecting GitHub — always require interactive authentication and cannot be done from the CLI even with a Full key. See api/auth for the full list.

CodeMeaning
0Success
1Error (authentication, deployment failure, etc.)