CLI

CLI

Install, authenticate, and drive Otterdeploy from the terminal.

The otterdeploy CLI drives everything the dashboard can — projects, services, databases, domains, deployments, logs, backups, and more — from your terminal and your CI. It talks to the same typed oRPC API as the web app.

Install

npm install -g @otterdeploy/cli
# or: bun add -g @otterdeploy/cli
otterdeploy --version

Runs on Node ≥20 or Bun.

Authentication

otterdeploy login <url> runs a browser device-code flow and stores your token at ~/.config/otterdeploy/config.json (mode 0600). Commands that need auth trigger the same flow automatically the first time. An expired token is detected and re-authenticated in place.

Environment variables

VariablePurpose
OTTERDEPLOY_URLControl-plane URL (overrides stored config).
OTTERDEPLOY_TOKENAuth token — a session token or an otter_ API key. Bypasses the browser flow entirely (use in CI).
OTTERDEPLOY_CONFIG_DIROverride the config directory.
NO_COLORDisable ANSI color (also --no-color).
DEBUGPrint full stack traces on error.

For CI, mint a scoped key and export it:

otterdeploy tokens create --name ci --expires 90d --read-only
export OTTERDEPLOY_TOKEN=otter_...
export OTTERDEPLOY_URL=https://deploy.example.com

The config file

Most commands operate on a project described by an otterdeploy.config.json (or .ts) file in the working directory — a declarative manifest of services, databases, and compose stacks. deploy/sync/up reconcile the live project to match it; pull writes the server's manifest back to disk; status shows drift.

{
  "$schema": "https://deploy.example.com/otterdeploy.schema.json",
  "version": 1,
  "project": "my-app",
  "databases": { "primary": { "engine": "postgres", "version": "16" } },
  "services": {
    "web": {
      "source": "git",
      "repo": "acme/my-app",
      "ports": [{ "container": 3000, "appProtocol": "http", "primary": true }],
      "env": { "DATABASE_URL": "${database:primary.url}" }
    }
  }
}

Commands that target a single resource take --slug <project> to override the project (otherwise it's read from the config's project field) and a resource name as a positional argument.

Global conventions

  • --json — machine-readable output on read commands (raw JSON to stdout, no decoration). Ideal for scripting.
  • --yes — skip confirmation prompts (destructive actions confirm by default).
  • --url <url> — override the control-plane URL for a single command.
  • Exit codes — non-zero on failure, on pending drift (status), on skipped manifest changes, and on a failed --wait deploy.

Shell completions

otterdeploy completions bash > /etc/bash_completion.d/otterdeploy
otterdeploy completions zsh  > "${fpath[1]}/_otterdeploy"
otterdeploy completions fish > ~/.config/fish/completions/otterdeploy.fish