CLI reference — Versola Docs
VersolaVersola/docs
Pre-1.00.7.1versola.kzGitHub

CLI reference

Every versola-cli command, grouped by task, with synopsis, flags and defaults, examples, exit behaviour, the state directory layout and the doctor thresholds.

versola is the command-line tool that deploys and runs a released version of Versola with Docker on one machine. This page describes every command and flag of versola-cli 0.2.10; for an end-to-end walk-through of a server deployment see VPS deployment.

The CLI is deliberately thin. It checks the machine, talks to Docker, and runs the versioned ghcr.io/versolauth/versola-tools:<version> image to generate everything that depends on how a specific Versola release is built (configuration, Compose files, the admin console, migrations). Because of that, versola-cli and Versola have separate version numbers: the CLI version is whatever versola version prints, and the Versola version is the <version> you pass to configure/bootstrap.

Install and upgrade

macOS and Linux:

curl -fsSL https://raw.githubusercontent.com/versolauth/versola-cli/main/install.sh | sh

The script installs to ~/.local/bin/versola without sudo. On Windows (PowerShell) the script installs to %LOCALAPPDATA%\versola:

iwr https://raw.githubusercontent.com/versolauth/versola-cli/main/install.ps1 -useb | iex

To move to a newer CLI later, run versola upgrade (see below).

CLI release tags have no leading v (releases up to v0.2.5 did). Each release publishes these assets, and nothing else:

PlatformAsset
Linux, x86-64versola-linux-amd64
macOS, Intelversola-darwin-amd64
macOS, Apple Siliconversola-darwin-arm64
Windows, x86-64versola-windows-amd64.exe
Checksumschecksums.txt

There is no Linux ARM64 build: on that platform the installer fails because the asset does not exist.

Checksum verification. The release workflow writes checksums.txt (sha256sum format) next to the binaries. The installers and versola upgrade download it and compare the SHA-256 of the binary; a mismatch aborts the install. If checksums.txt is missing, cannot be fetched, or has no entry for the asset, they print a warning and continue without verification. To verify by hand, download the asset and checksums.txt from the release page and run sha256sum -c --ignore-missing checksums.txt.

To update an installed CLI, run versola upgrade. It replaces the binary in place and refuses to touch a dev build.

Targets

A target is where a deployment runs. It is the first argument of configure and bootstrap.

TargetIntended forWhat runs
localTrying Versola on a workstation. A throwaway stack.Docker Compose: a Postgres 18 container, OpenBao, central, auth, edge, a certificate authority (step-ca) and Versola’s reverse proxy published on 127.0.0.1:2821. Needs no flags. After up the admin console is at http://localhost:2821/central/admin/ and the login printed is admin with a fixed demo password.
vpsA real server.The same Compose project, named versola-vps, but without Postgres: you provide a native PostgreSQL. Adds --auth-url and --postgres-host (both required), the optional --postgres-db and --postgres-ca, and a choice of reverse-proxy mode. The admin password is never printed; it is in auth.secrets.env in the bundle.

Only one deployment is active on a machine at a time: configuring again replaces the record of the previous one. Commands on vps that change something ask Continue? [y/N] on standard input. There is no --yes flag for these prompts; an empty answer or end of input aborts with a non-zero exit code.

State directory

Everything the CLI keeps is under ~/.versola:

PathContent
~/.versola/active/state.jsonThe active deployment: target, Versola version, bundle directory, --auth-url, proxy mode, --postgres-host, --postgres-db, when migrate last ran, the secrets schema revision, and the replica slots of auth and edge. A deployment made by a much older CLI is reported as such and replaced by running configure again.
~/.versola/active/postgres-ca.crtCopy of the file given as --postgres-ca, so later steps do not depend on the original path. Present only when you passed the flag.
~/.versola/active/proxy-vps/The vps reverse proxy’s own configuration, kept apart from the bundles so that it survives every configure: one directory per configuration generation (gen-000008/) and a current link to the one in use. See Reverse proxy on vps.
~/.versola/active/bundle-<n>-<random>/One directory per configure run, generated by versola-tools: compose.yml, proxy.yml, replicas.yml (only when a service has more than one replica), auth.conf, central.conf, edge.conf, <service>.secrets.env (mode 600, resolved secret values), utils.private-key.jwk, openbao.hcl, postgres-ca/ca.crt (the CA bundle, mounted read-only into the three services and the migration container; the directory is empty without --postgres-ca), the admin console static files, and the proxy configuration. Do not edit these files: the next configure writes a new bundle and removes the old ones once nothing runs from them.
~/.versola/active.lockLock held while a command that changes state runs, so two terminals cannot reconfigure at once.
~/.versola/openbao/<target>.jsonOpenBao AppRole credentials for that target. Written by secrets login, or by configure when it provisions OpenBao itself.
~/.versola/openbao/vps-admin.jsonOpenBao root token and unseal key for vps, written when configure provisions OpenBao. Keep a copy off the machine: neither value can be recovered.

versola uninstall removes ~/.versola/active but leaves ~/.versola/openbao alone.

Reverse proxy on vps

The CLI generates the nginx configuration of Versola’s reverse proxy. Three details matter when you run vps.

  • Configuration generations. The proxy does not mount files from the bundle. Each change (a configure, a replica added or removed) builds a complete new configuration under ~/.versola/active/proxy-vps/gen-<n>/, has nginx check it in a throwaway container, switches the current link and reloads nginx. If the reload is refused, the link goes back. The proxy container is not recreated for a new bundle, so requests are not dropped.
  • auth’s internal listeners go through the proxy. On vps the proxy listens on 127.0.0.1:8082 (the Account Settings listener, APORT) and 127.0.0.1:8083 (the mutual-TLS listener, MPORT) and balances them over every auth replica. The replicas’ own listeners are moved 1000 ports up (and 100 more per slot). central and edge keep the addresses they had in their configuration. The mutual-TLS connection is passed through as TCP, so auth still terminates TLS with its own certificate.
  • No client certificate from the caller. The proxy sets the ssl-client-cert header to an empty string on every request it forwards, so a caller cannot assert a client certificate by sending that header. A certificate is accepted only where it was verified: on auth’s own mutual-TLS listener, or through a TLS terminator you configured to set that header.

Check

versola doctor

versola doctor [--target local|vps]

Checks that this machine has what Versola needs. It installs nothing. If Docker is missing it offers to open an install page in your browser and only after you confirm.

FlagDefaultMeaning
--targetlocallocal or vps. Changes the memory check and whether port 2821 is checked.

Checks, printed as [ok] or [!!] lines:

CheckPasses when
Docker daemondocker version reaches the server.
Docker Compose plugindocker compose version works. Compose 2.24 or newer is needed for replica (the replica file uses !override).
Port 2821 free (local only)Nothing but the local proxy listens on 127.0.0.1:2821.
Docker memory, localDocker reports at least 4 GiB in total.
Docker memory, vpsMemAvailable in /proc/meminfo is at least 3 GiB for a fresh deployment, 1 GiB when central, auth and edge already run (a replace), plus 256 MiB when OpenBao is not running. The check fails off Linux and when Docker points at a remote host, because it can only read memory of the machine it runs on. Run it on the server.
Disk spaceAt least 4 GiB free on the volume of the current directory.

Exit code is non-zero if any check fails. Always pass --target vps when checking a server: the plain default checks the wrong memory rule.

Deploy

The three deploy steps can run together (bootstrap) or one at a time. Run them separately when migrations must be a reviewable step of their own.

versola bootstrap

versola bootstrap <local|vps> <version> [flags]

Runs configure, migrate and up in order, asking for confirmation once up front on vps. <version> is a Versola release tag without a leading v.

FlagDefaultMeaning
--auth-urlnonePublic origin of auth, for example https://id.example.com. Required on vps.
--postgres-hostnonehost:port of Postgres, for example 127.0.0.1:5432. Required on vps.
--proxynginxvps only. nginx: Versola’s proxy serves ports 80/443 and obtains a Let’s Encrypt certificate for an https --auth-url. external: your own web server stays on 80/443 and forwards to Versola’s proxy on 127.0.0.1:2821.
--acme-stagingfalsevps only. Use Let’s Encrypt’s staging environment (untrusted certificate, no rate limits).
--postgres-dbauthvps only. Name of the Postgres database, 1 to 63 letters, digits, _ or -. Use the same name in the CREATE DATABASE and \c lines of the setup SQL.
--postgres-canonevps only. PEM file with the CA that signed Postgres’ server certificate, for sslmode=verify-full when the JVM does not already trust that CA. The file must contain a certificate. It is copied into the bundle, mounted into central, auth, edge and the migration container, and written as postgres.ssl-root-cert.
--setup-openbaofalsevps only. You provision OpenBao yourself; the CLI then needs credentials stored with secrets login.
--generate-missingnoneComma-separated secret names that may be generated although the plan would stop (see --generate-missing).
--allow-legacy-secretsfalseConfigure a release that has no secret schema by the old rule (see below).
--no-browserfalseDo not open the admin console in a browser when ready (local).

--proxy, --acme-staging, --postgres-db and --postgres-ca are rejected for local.

On vps the first run against an empty OpenBao generates the Postgres role’s password. bootstrap then stops before migrate with a non-zero exit code, because the role does not have that password yet: run the SQL that configure printed, then versola migrate and versola up.

versola bootstrap vps 0.7.1 --auth-url https://id.example.com --postgres-host 127.0.0.1:5432

versola configure

versola configure <local|vps> <version> [flags]

Prepares a deployment without starting it. It checks the machine, pulls versola-tools:<version>, generates the bundle (configuration, Compose files, admin console, proxy configuration), starts OpenBao if needed and resolves every secret against it. It starts none of Versola’s services and does not touch the database, so it is safe to repeat. On vps it asks for confirmation, because it replaces this machine’s record of the active deployment.

Flags are those of bootstrap except --no-browser. configure does not remember --auth-url, --postgres-host or --proxy: pass the same values on every run, --proxy external included, otherwise the proxy mode falls back to nginx. --postgres-db and --postgres-ca are the exception: they are recorded (the CA file is copied next to state.json), a later configure without the flag keeps the stored value, and passing the flag again overrides it. They need a versola-tools release that reads POSTGRES_DB and POSTGRES_CA_FILE (0.7.1 and later); an older one ignores them.

Before writing any secret, configure works out what it may do about each one (the same plan secrets plan prints) and stops, with nothing written, if a secret looks lost. See Secrets.

versola configure vps 0.7.1 --auth-url https://id.example.com --postgres-host db.example.com:5432 --postgres-db versola --postgres-ca /etc/ssl/db-ca.pem

Generating secrets the plan would refuse

--generate-missing NAME[,NAME...] confirms that the named secrets may be generated even though the plan would stop for them (a secret that looks lost, or an install of unknown origin). Names that are not in the release’s schema are reported as a problem, because they are most likely typos. A key pair is generated only if every member of its group is named, since half a pair is useless. Generating a replacement for a data-bound secret makes the data it protects unreadable; use the flag only to recover from a lost value you are sure nothing depends on.

--allow-legacy-secrets applies to a release whose versola-tools writes no secrets.schema.json. Without a schema the CLI cannot tell a new secret from a lost one, so on vps it refuses when OpenBao already holds secrets or a deployment is recorded. With the flag it uses the old rule: anything missing from OpenBao is generated, lost or not. With an empty OpenBao and no record there is nothing to lose, and no flag is needed. local is never refused.

versola migrate

versola migrate [--dry-run] [--service auth|central|edge]

Runs each service’s database migrations against what configure most recently prepared, from one throwaway versola-tools container. No server starts. On vps it asks for confirmation first and states that migrations cannot be rolled back (there are no down-migrations): back up the database before answering yes.

FlagDefaultMeaning
--dry-runfalseConnect to the real database and list the migrations that would be applied, without applying anything and without a confirmation prompt.
--serviceall threeLimit to auth, central or edge. Any other value is an error.

Exit code is non-zero when a migration fails, so a script or pipeline step can rely on it. With --dry-run, releases up to and including 0.6.2 report migrations that are merely pending as a validation failure; that is a bug of the versola-tools image of those releases, fixed in 0.7.0.

The services never migrate on their own: they start with RUN_MIGRATIONS=false, validate their schema and refuse to start on an out-of-date one.

versola up

versola up [--no-browser]

Starts the stack that configure prepared and waits until it serves traffic: central first (auth and edge sync their configuration from it), then auth and edge, then the reverse proxy; it finishes with a request through the proxy to auth. It assumes migrate already ran. On vps it asks for confirmation, because it replaces whatever currently serves traffic. central starts only after step-ca and the one-shot mtls-init container have produced the certificates it reads (0.7.1 and later bundles), which is part of the first step.

FlagDefaultMeaning
--no-browserfalseDo not open the admin console in a browser (local only; vps never opens one).

On vps it prints the address and the file that holds ADMIN_BOOTSTRAP_PASSWORD. That password is temporary, valid for 24 hours, and the first login with it forces a new one.

Operate

versola status

versola status

Prints the deployed version and target, when migrations last ran, the replicas, and docker compose ps for the project. It exits 0 and says so when nothing is deployed.

Deployed version: 0.7.1 (target: vps)
Migrations applied: 2026-10-14 17:11:51
Replicas: auth x1 (slot 1), edge x1 (slot 1)

versola down

versola down [--volumes]

Stops the containers of the deployment, including OpenBao and the proxy, and keeps data and images. The Postgres volume of the local target is kept unless you pass --volumes, which also removes the stack’s volumes. On vps Postgres is yours and is never touched.

versola uninstall

versola uninstall [-y|--yes]

Stops the stack with --volumes, removes the Docker images Versola pulled and clears ~/.versola/active. It lists what it will remove and asks for confirmation unless -y is given. On vps it leaves the OpenBao volume (versola-openbao-file-vps) and the certificate volume (versola-acme-vps) in place and prints the docker volume rm commands. If Docker is unreachable it keeps ~/.versola/active so the deployment can still be stopped later. It does not delete the versola binary; it prints its path.

versola replica

versola replica add <auth|edge> [count]
versola replica remove <auth|edge> [count]

Runs more than one copy of auth or edge behind Versola’s reverse proxy, which balances requests between them. central is not replicated. The replicas are recorded, so versola up restores them and versola status lists them. There is no flag; count defaults to 1.

add starts count more replicas one at a time in the lowest free slots (at most 9 in all). Each is waited for until ready and only then added to the proxy’s upstreams. A replica that does not come up is removed again; those started before it stay. Each slot has its own container name (versola-auth-2) and listens on ports shifted by 100 per slot.

remove stops the highest slots first, one at a time: the proxy first stops sending the replica requests and waits for in-flight ones to finish, then the replica is stopped. The last replica of a service cannot be removed.

The deployment must be running (versola up) from its latest configure. On vps both ask for confirmation.

versola replica add edge 2

versola version

versola version

Prints versola-cli <version> (<go version>, <os>/<arch>). versola --version prints the same.

versola upgrade

versola upgrade [-y|--yes]

Checks the latest versola-cli release on GitHub; if it differs from this binary’s version it downloads the asset for this platform, verifies it against checksums.txt and replaces the running executable in place. It asks for confirmation unless -y is given. It does not change the Versola version of a deployment: that is configure with a new <version>. A dev build is never touched.

Secrets

These commands manage this machine’s access to OpenBao and preview what configure would do. What the secrets are and how to rotate them is in Secrets.

versola secrets login

versola secrets login <target> <address> <role-id>

Stores AppRole credentials for a target in ~/.versola/openbao/<target>.json, after verifying them with a login. The secret ID is asked for as a separate masked prompt (or read from standard input when it is not a terminal), so it never lands in shell history or the process list. Needed with --setup-openbao, or when the machine lost its credentials file.

versola secrets login vps http://127.0.0.1:8200 <role-id>

versola secrets test

versola secrets test <target>

Logs in with the stored credentials, writes a scratch value to versola/<target>/_secrets-test, reads it back and reports whether the credentials and the KV v2 engine work. It touches no service secret. Exits non-zero on any failure.

versola secrets plan

versola secrets plan <target> <version> [--json] [--generate-missing NAME[,NAME...]]

Prints what configure would do with each secret, without doing it. It reads what OpenBao holds, runs versola-tools:<version> in a temporary directory to learn the release’s secrets and candidate values, and prints for every secret whether it would be kept, created, or stops the run. It needs stored credentials (secrets login), a running OpenBao and Docker. Nothing is written to OpenBao or to the deployment. Secret values are never printed.

FlagDefaultMeaning
--jsonfalsePrint the report as JSON.
--generate-missingnoneSame as for configure: preview the plan with these names confirmed.

Exit code is non-zero when the plan has problems. The JSON report has version, ok, target, state, schemaRevision, deployedRevision (when a record exists), actions and problems. The report format is described with the plan rules in Secrets.

Maintenance

Routine maintenance maps to commands already listed:

TaskCommand
Update the CLIversola upgrade
Move a deployment to another Versola versionversola configure, versola migrate, versola up (Redeploying later)
Preview migrationsversola migrate --dry-run
Check OpenBao accessversola secrets test <target>
Preview a secrets changeversola secrets plan <target> <version>
Stop everything, keep dataversola down
Remove the deployment and imagesversola uninstall

Next steps

  • VPS deployment: the commands above in order, on a real server.
  • Secrets: what configure does per secret and how to rotate one.
  • Configuration: what the generated configuration files contain.