vox deploy CLI reference
vox deploy — ship a Vox app to a runtime
Section titled “vox deploy — ship a Vox app to a runtime”vox deploy reads the [deploy] section of your project’s Vox.toml, builds
the right artifact, and ships it to the target runtime. Six target kinds are
supported today: container, compose, kubernetes, bare-metal, fly,
coolify.
For a one-line summary of which target to pick, jump to Choosing a target.
Quickstart
Section titled “Quickstart”# 1. Scaffold a project with a deploy block pre-wired.vox init --template web my-appcd my-app
# 2. Inspect what deploy would do without invoking a runtime.vox deploy --dry-run
# 3. When you're ready, run for real.vox deployThe web template scaffolds Vox.toml with [deploy] target = "container" and
commented [deploy.fly] / [deploy.coolify] blocks you can uncomment to switch
platforms.
| Flag | Default | Meaning |
|---|---|---|
<environment> (positional) | production | Image tag suffix (e.g. staging, production). |
--target <kind> | from [deploy].target | Override the target kind for this run. |
--runtime <name> | from [deploy].runtime | Override the container runtime (auto, docker, podman). Only honored when target is container. |
--dry-run | false | Print the planned actions without invoking a runtime or mutating remote systems. Does NOT require docker/podman to be installed, even for container targets. |
--detach | false | For compose targets: run docker compose up with -d. |
--locked | false | Require vox.lock to exist before deploying. Use in CI to guarantee reproducible builds. |
Choosing a target
Section titled “Choosing a target”| Target | Latency to ship | Portability | Ops complexity | Use when |
|---|---|---|---|---|
container | Fastest local | Highest (OCI image runs anywhere) | None — you own the image | You want a build artifact you’ll push elsewhere by hand. |
compose | Fast | Medium (needs Docker) | Low | Single-host deploys, dev environments, small teams. |
kubernetes | Medium | Medium (cluster-bound) | High | You already run a k8s cluster. |
bare-metal | Medium | Low (host-specific) | Medium | SSH-accessible host, want systemd unit, no container. |
fly | Fast | High (Fly hosts it) | None — Fly manages | ”I want a URL, now.” Hosted, global, billed by Fly. |
coolify | Fast | Medium (your Coolify instance) | Low — Coolify manages | Self-hosted PaaS; you run a Coolify instance and want auto-deploy. |
Target reference
Section titled “Target reference”container
Section titled “container”Builds an OCI container image and (optionally) pushes it to a registry. Does not run the image — that’s the next step in your pipeline.
[deploy]target = "container"runtime = "auto" # "auto" | "docker" | "podman"
[deploy.container]# All fields optional — sensible defaults derive from the package name.# image_name = "my-app" # default: package.name# registry = "ghcr.io/your-org" # if set, also pushes# dockerfile = "Dockerfile.prod" # default: Dockerfile (project root)# build_args = { VOX_FEATURE_X = "1" }Dry-run output:
Deploying environment `production` via container target [dry-run] would build OCI image: my-app:production [dry-run] would push: ghcr.io/your-org/my-app:productionNote: vox deploy --dry-run does NOT require docker/podman to be installed.
The runtime detection is skipped on dry-run paths so CI / air-gapped
contributors can validate deploy plans without a container daemon.
compose
Section titled “compose”Runs docker compose up against a docker-compose.yml you provide.
[deploy]target = "compose"
[deploy.compose]file = "docker-compose.yml" # defaultproject_name = "my-app" # default: package.nameservices = ["web", "worker"] # optional; empty = all servicesPair with --detach to launch in the background.
kubernetes
Section titled “kubernetes”Applies a directory of manifests via kubectl apply -f <dir> --namespace <ns>.
[deploy]target = "kubernetes"
[deploy.kubernetes]manifests_dir = "k8s/"namespace = "production" # default: "default"# cluster = "prod-east" # optional — sets kubectl --context# replicas = 3 # informational; for templates that read itbare-metal
Section titled “bare-metal”Generates a systemd unit, SCPs it to a host, and starts it. No container runtime required on the target.
[deploy]target = "bare-metal"
[deploy.bare-metal]host = "ops-1.example.com"user = "deploy" # default: $USER / $USERNAME / "root"port = 22deploy_dir = "/opt/my-app" # default: /opt/<package.name>service_name = "my-app" # default: package.nameDeploys via the flyctl CLI to Fly.io. Reuses your existing flyctl auth
session.
[deploy]target = "fly"
[deploy.fly]app_name = "my-app" # default: package.nameregion = "iad" # optional# org = "my-org" # optionalcoolify
Section titled “coolify”Triggers a redeploy on a self-hosted Coolify instance via its API.
[deploy]target = "coolify"
[deploy.coolify]base_url = "https://coolify.example.com"app_uuid = "abc-123-..."token_env = "COOLIFY_TOKEN" # name of env var carrying the API tokenforce_rebuild = falseThe token is read from the environment variable named by token_env (or from
the vox-secrets CoolifyToken slot as a fallback). Never commit the token
itself.
Portable backend artifact lane (SBOM / signing)
Section titled “Portable backend artifact lane (SBOM / signing)”For container-promoting targets (container, compose, kubernetes, fly,
coolify), two environment-driven gates can enforce supply-chain artifacts
before a non-dry-run deploy:
| Env var | When 1 / true | What it enforces |
|---|---|---|
VOX_BACKEND_ARTIFACT_SBOM_REQUIRED | An SBOM file must exist at <project>/<lane>/sbom.json (or sbom.spdx.json / sbom.cyclonedx.json). | Aborts deploy if missing. |
VOX_BACKEND_ARTIFACT_SIGNING_REQUIRED | A signing artifact must exist at <project>/<lane>/signing.attestation.json or artifact.sig. | Aborts deploy if missing. |
These gates are off by default; turn them on in CI to enforce the portability
lane described in
docs/src/reference/vox-portability-ssot.md.
See also
Section titled “See also”vox new/vox init— scaffold a project with a deploy block.vox doctor --project— verify the project compiles clean before deploying.docs/src/reference/deployment-compose.md— deeper dive on the compose target.- CR-L7 integration test:
crates/vox-cli/tests/cr_l7_new_deploy_doctor_e2e.rs— the contract test that exercisesvox new web → vox deploy --dry-run → vox doctor --projectend-to-end.