Skip to main content

Operator CLI

vidra is a host-side binary, not a container command. install.sh puts it in /usr/local/bin from vidra-core's release assets, checksum-verified; for a release cut before those existed, make build-vidra in vidra-core still builds one.

vidra setup --template env/production.env.example   # the configuration interview
vidra doctor # check this deployment
vidra status # what is running, and whether it answers
vidra logs [service]
vidra restart <service>
vidra deploy
vidra rollback <tag>
vidra backup
vidra restore <dump>
vidra release <tag>

It wraps the scripts 1:1

deploy, rollback, backup, restore and release exec deploy/*.sh with ENV_FILE injected and your terminal attached, and return the script's exit code unchanged — same gates, same refusals, no second copy of any of them. logs, restart and status go through deploy/compose.sh.

The scripts remain the source of truth. Every rule is enforced in them, not in the CLI. --yes and RESTORE_CONFIRM reach the script unchanged.

vidra setup

Generates env/production.env from the deployment template, filling in your answers and minting every secret the template leaves blank. Comments and ordering are preserved, and the file is written mode 0600. It also renders deploy/Caddyfile.local from the deploy/Caddyfile template.

vidra setup --template env/production.env.example        # interactive
vidra setup --template env/production.env.example --web # nine-step browser wizard
vidra setup --answers a.txt # pre-seeded interview
vidra setup --non-interactive --domain … --tls-mode … # fully scripted
vidra setup --check env/production.env # validate an existing file

Re-running is safe. The file being written is always read back first and every value it sets is preserved. A secret is only ever replaced when --rotate names it, and rotating a *_KEK additionally needs --yes-i-know, because it orphans data already sealed in the database — a separate answer from --yes, which only confirms the in-place rewrite.

Secrets need not appear on the command line. --s3-secret-key, --smtp-password, --database-url, --redis-url, --peertube-source-url and --peertube-source-s3-secret-key each accept @path (read the file), - (read stdin, with --non-interactive) or a VIDRA_SETUP_* environment variable, and the interactive prompts read them without echoing. A connection string counts as a secret: it carries the password inside it.

--answers <path> reads flag-name = value lines and applies each one the command line did not already set — argv always wins. It implies nothing else: without --non-interactive, the questions it does not answer are still asked.

The optional components (--scan, --captions, --media, --otel, --ipfs) are compose profiles written to VIDRA_COMPOSE_PROFILES. Not passing one keeps whatever the env file already selects; pass --scan=false to turn one off deliberately.

--peertube configures a migration source and only ever writes it down — at setup time the stack does not exist, so nothing dials the source. The answers are shape-checked with the api's own validators, and the import is launched later from <domain>/admin/import-peertube. --peertube=false closes the import surface again.

--tls-mode chooses the deployment's topology, not just its issuer; see Production deployment.

vidra doctor

Checks a deployment the way the runbook says to, and prints one line per check — passed, could not run (or a finding that does not stop a deploy), a problem to fix — each with one line of what to do about it.

vidra doctor
vidra doctor -C /opt/vidra --env env/production.env
vidra doctor --write-probe # additionally prove the object store accepts writes

The exit code is the contract: 0 when nothing failed, 1 when something did. A warning never fails a run, because a command that exits non-zero for warnings is one every wrapper script learns to ignore. That makes it usable as a pre-deploy gate.

Every finding is derived from a file, the rendered compose model, the database or the Docker daemon — never from what a document says should be true. It reads; it changes nothing, creates no bucket and sends no mail.

The checks, in the order they run, which is the order of causation:

SectionChecks
Stackcompose version · published ports · log caps · dev override · stray vidra-core/.env
Configreverse proxy · domain DNS · env file vs template · configuration values
Stateschema ledger · search ledger · db pool sizing · backups · backup timer · disk space · media GC posture · storage migration
Reachabilityobject storage · object write · object retention · bucket ownership · smtp · search service · ffmpeg · video encoders · hardware transcode

--write-probe is the single exception to "it only reads", and it is opt-in for that reason: it stores a small object in the media bucket and deletes it again. A key that can read but not write passes every other object-store check here and then fails every upload — run it after changing storage credentials and before a migration.

Checks whose dependency is missing on this host — no systemd on a laptop, no daemon, a stack that is not up — report with the reason rather than failing.

vidra status and vidra logs

vidra status              # what is running, and whether it answers
vidra logs # tail everything
vidra logs api # one service
vidra restart api

Always use these (or deploy/compose.sh) rather than a bare docker compose logs api. On a deployment host the bare form auto-loads docker-compose.override.yml and addresses a different compose project than the deploy scripts do, so you can be reading a stack that is not the one running.

Everyday Make equivalents

CommandWhat it does
make prod-configRender and validate the production compose chain; catches missing required secrets
make deploydeploy/deploy.sh
make rollback TAG=v0.4.0Rewrite the three tags, pull, restart, re-probe
make backupdeploy/backup.sh
make restore DUMP=… CONFIRM=1Destructive. deploy/restore.sh
make release VERSION=v0.5.0deploy/release.sh across all three repos
make prod-logs / make prod-downTail / stop the production stack

All of them honour PROD_ENV_FILE=env/staging.env.

make restore asks first: like make nuke it wants CONFIRM=1 or the word typed at an interactive prompt, and refuses outright with no terminal. It then invokes deploy/restore.sh --yes, so the confirmation happens exactly once, at the layer the operator is typing at.