CLI Commands Reference
This is the canonical reference for the portoser command, generated from the script's own --help output and verified against the running code at bin/portoser (the entry-point shim) and the top-level portoser script.
The CLI is a single Bash script that dispatches to handlers under lib/. There is no daemon, no agent, and no client/server protocol — every subcommand runs in the local shell and SSHes (or docker execs) where it needs to.
Global flags
| Flag | Purpose |
|---|---|
-h, --help |
Show top-level help and exit. |
-v, --version |
Print version and exit. |
--debug |
Enable bash trace + debug logging for the run. |
--json-output |
Where supported, emit machine-readable JSON instead of human-formatted output. Used by the web UI and downstream scripts. Note: the canonical flag is --json-output; a few subcommands also accept --json as a shortcut. |
CADDY_REGISTRY_PATH overrides which registry.yml is read. The script defaults to $SCRIPT_DIR/registry.yml, so a clone-anywhere checkout works out of the box.
Service lifecycle
deploy
portoser deploy MACHINE SERVICE [SERVICE...] [OPTIONS]
Deploy one or more services to a target machine through the full observation → diagnosis → solving → learning loop. Auto-heal is on by default.
| Option | Purpose |
|---|---|
--dry-run |
Print what the loop would do; don't apply anything. |
--no-auto-heal |
Run observation + diagnosis only; skip solver actions. |
--json-output |
Structured JSON output (used by the web UI). |
Examples:
portoser deploy m1 my-api # standard deploy
portoser deploy mini1 kag requirements # multiple services, same host
portoser deploy m4 kag --dry-run # plan without applying
portoser deploy mini1 kag --no-auto-heal # diagnose only
start / stop
portoser start <service|machine> [service...]
portoser stop <service|machine> [--force] [service...]
Start or stop services. The first arg is interpreted as a machine name if it matches one in the registry, otherwise as a service. --force on stop skips dependency checking (useful when you know dependents will be brought down separately).
portoser start requirements
portoser start mini2 # start everything on mini2
portoser stop requirements --force
move
portoser move SERVICE FROM TO
Migrate a single service from one machine to another. Updates current_host in the registry, redeploys on the target, and tears down on the source.
docker (advanced)
Bypass the loop and operate on Docker directly. Useful when you've already diagnosed a problem and want to run the precise step yourself.
portoser docker build SERVICE [MACHINE]
portoser docker deploy SERVICE MACHINE
portoser docker stop SERVICE MACHINE
portoser docker restart SERVICE MACHINE
local (advanced)
Same idea, but for deployment_type: local services (uv-managed Python apps).
portoser local start SERVICE [MACHINE]
portoser local stop SERVICE [MACHINE]
portoser local restart SERVICE [MACHINE]
portoser local logs SERVICE [LINES] # default 50 lines
Diagnostics & health
status
portoser status
Print every service in the registry with its host, type, and current status. Read-only — no SSH chatter.
verify
portoser verify
Verify every HTTP service is reachable via its .internal domain. Useful after a Caddy regenerate.
diagnose
portoser diagnose SERVICE [MACHINE] [--json-output]
Walks the same observation pipeline a deploy uses, but in read-only mode. Produces an analyzer fingerprint (e.g. PROBLEM_PORT_CONFLICT), a health score, and a frequency count from the knowledge base.
Diagnostic reports are saved to ~/.portoser/diagnostics/ for later inspection. The --json-output form is what the web UI calls.
health
portoser health check SERVICE # single service
portoser health check-all # all services
portoser health status SERVICE # detailed status
portoser health SERVICE MACHINE [--json-output] # quick health score
portoser health --all [--json-output] # all-services scores
The "score" form is the JSON-friendly entry point used by the web UI's Health Dashboard.
Knowledge base & history
learn
portoser learn summary # human-readable summary
portoser learn stats [--json-output] # numbers only
portoser learn playbooks [--json-output]
portoser learn playbook NAME [--json-output]
portoser learn insights SERVICE [--json-output]
learn summary is the friendliest entry point — total problems, unique fingerprints, generated playbooks, on-disk path. learn insights <service> is what you'd hand a colleague when they ask "what fails most for this service?".
history
portoser history list [SERVICE] [--json-output]
portoser history show DEPLOYMENT_ID [--json-output]
portoser history rollback DEPLOYMENT_ID [--force]
portoser history preview DEPLOYMENT_ID
portoser history compare ID1 ID2
portoser history stats [SERVICE] [DAYS]
portoser history cleanup [KEEP_COUNT] [KEEP_DAYS]
History is stored in the backend Postgres database (running the web stack), so the CLI sees an empty list when run against a machine that isn't connected to that database.
Dependencies
portoser dependencies list [--json-output]
portoser dependencies graph [--json-output]
portoser dependencies check [--json-output]
portoser dependencies order SERVICE [--json-output]
portoser dependencies impact SERVICE [--json-output]
portoser dependencies info SERVICE [--json-output]
portoser dependencies add SERVICE DEPENDENCY
portoser dependencies remove SERVICE DEPENDENCY
dependencies check runs both validation passes (no missing services + no circular deps) and exits non-zero if it finds either. impact shows blast radius — what would break if SERVICE went down. order returns a topologically valid deploy sequence for a service plus its dependencies.
Metrics & uptime
portoser metrics service SERVICE MACHINE [--json-output]
portoser metrics machine MACHINE [--json-output]
portoser metrics all [--json-output]
portoser metrics test # platform compat probe
portoser uptime service SERVICE MACHINE [--json-output]
portoser uptime all [--json-output]
portoser uptime history SERVICE MACHINE [--days N] [--json-output]
portoser uptime status SERVICE MACHINE
metrics test is a useful first call on a fresh host — it tells you which CPU / memory / disk probes work on that platform.
Networking
dns
portoser dns status
portoser dns config
portoser dns check SERVICE
portoser dns check-all
portoser dns test HOSTNAME
portoser dns verify-all
caddy
portoser caddy sync # regenerate Caddyfile + reload
portoser caddy update SERVICE
portoser caddy reload
portoser caddy validate # `caddy validate` against generated config
portoser caddy regenerate
portoser caddy proxy SERVICE
network
portoser network update-ips MACHINE IP ...
portoser network migrate MACHINE IP ...
Security
certs
portoser certs init-ca
portoser certs generate SERVICE
portoser certs generate-all
portoser certs generate-server SERVICE
portoser certs generate-all-servers
portoser certs update-registry
portoser certs deploy-servers [SERVICE]
portoser certs list
portoser certs deploy SERVICE MACHINE
keycloak
portoser keycloak create-client SERVICE
portoser keycloak create-all
portoser keycloak list-clients [REALM]
portoser keycloak get-secret CLIENT_ID
vault
portoser vault init
portoser vault unseal [KEY1] [KEY2]
portoser vault status
portoser vault setup-approles
portoser vault migrate SERVICE
portoser vault migrate-all
portoser vault get SERVICE
portoser vault put SERVICE KEY VALUE
portoser vault list
Registry & remote
registry
portoser registry list-services
portoser registry list-machines
portoser registry info SERVICE
portoser registry machine-info MACHINE
portoser registry validate
remote
portoser remote test-connections
portoser remote exec MACHINE COMMAND
portoser remote system-info MACHINE
Cluster (Raspberry Pi-focused)
portoser cluster build [--all|SERVICE]
portoser cluster deploy [--all|--pi PI]
portoser cluster sync [PI...]
portoser cluster clean [PI...]
portoser cluster health [--watch]
portoser cluster setup-buildx
portoser cluster scan [PI...]
portoser cluster status [--json]
These are tuned for arm64 cluster nodes and assume cluster.conf describes them. They overlap with the generic deploy flow but bake in some Pi-specific defaults (rsync sync directories, multi-platform buildx, cleanup of cached layers).
Onboarding & devices
portoser onboard generate-token [--expires HOURS] [--description DESC]
portoser device list
portoser device status HOSTNAME
onboard generate-token is what you hand a fresh machine so it can call the device-registration endpoint. device list / device status show what has registered.
Templates
portoser template list [--category CAT]
portoser template show NAME
portoser template use NAME OUTPUT [--var KEY=VALUE ...] [--interactive]
portoser template create NAME [--category C] [--description D]
portoser template validate NAME
portoser template export NAME
portoser template import FILE
portoser template status
Categories: backend, frontend, database, infrastructure, plugin. The bundled templates (e.g. fastapi-rest) live under templates/; exported tarballs preserve template metadata so they can be re-imported on another machine.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success. |
| 1 | Generic failure. Most user errors (missing args, unknown service) exit 1 with a human message on stderr. |
| 2 | Invalid usage (caught by argument validation before any side-effect). |
--json-output mode never prefixes errors with ANSI colors so it's safe to pipe into jq.
See also
- Quickstart — your first deploy.
- Service Registry — every field in
registry.yml. - HTTP API — REST + WebSocket endpoints exposed by the web backend.
- Self-Healing Loop — what
deployactually does under the covers.