Via

Deploy lifecycle

The push is only the submission step. After that, the server owns the path:

git push Authenticated Git frontend Managed pre-receive hook Queued deploy row Builder daemon Signed artifact Runner daemon Running container
git push Authenticated Git frontend Managed pre-receive hook Queued deploy row Builder daemon Signed artifact Runner daemon Running container

For a successful deploy, the durable state walk is:

queued building typechecking testing artifact_ready deploying running
queued building typechecking testing artifact_ready deploying running

For a failed test run the new deploy becomes failed, and the prior running deploy stays current.

Accepted pushes also create source/build/deploy operation records. Repeating the same deploy-token/ref/sha push is idempotent and links back to the first accepted source operation instead of duplicating work.

During vary app deploy --target, the CLI prints phase/event lines as the server reports lifecycle activity and emits progress heartbeats during long non-terminal phases. Progress lines include the current deploy id, build id, deploy state, and elapsed time so an operator can tell the command is still following the same attempt.

Manifest apply

If the app has a via.app.toml, the normal target deploy applies it as part of the deploy:

vary app deploy my-api --target prod --apply-manifest

The apply response reports add-ons, bindings, routes, and readiness. When readiness is blocked, each blocker includes the capability id/type, binding, expected condition, observed state, failing check, and the exact next command such as vary app env set ... or via addon create ....

Auth readiness

If the build manifest declares any route with auth optional, auth session, or auth capability, Via blocks the deploy until production auth configuration is present.

Acceptable values for VARY_AUTH_MODE are oidc and jwt; any other value, including unset, fails preflight with a structured blocker. The blocker text names the missing variable, the route that required it, and the next command, usually vary app env set VARY_AUTH_MODE oidc followed by the OIDC issuer, client, and capability claim variables documented in Public API security.

vary app preflight is the workstation-side check that mirrors the same gate:

vary app preflight my-api --target prod
vary app preflight my-api --target prod --json

Runtime evidence

The HTTP runtime surfaces a runtime evidence block through vary app status, vary app inspect, and vary app preflight. The block is the source of truth for what is actually serving traffic:

FieldMeaning
backendAlways javalin for the current HTTP runtime.
main_classGenerated bootstrap class the runner starts.
route_countNumber of routes the boundary registered.
auth_modeResolved VARY_AUTH_MODE (oidc, jwt, or none for fully anonymous apps).
csrf_modeResolved VARY_AUTH_CSRF_MODE.
token_supportenabled when API token verification is wired up.
capability_sourceClaim path used to derive caller capabilities.
provider_summaryOne-line OIDC provider description.
cookie_securityCookie flags in effect.
auth_storeBinding name supplying the auth SQLite store.

Build checkout layout

Via builds each deploy from a clean checkout mounted at /workspace/source inside the builder container. Every build phase starts with that directory as the working directory, and Via also exports VARY_PROJECT_ROOT=/workspace/source so tests and build scripts can locate the deployed project root without guessing the host-side checkout path.

Inspect the Via-hosted source state from a workstation with cached target credentials:

vary app source show my-api --target prod
vary app source resolve-ref my-api refs/heads/main --target prod --json
vary app source commits my-api --target prod --json
vary app source remote my-api --target prod --json
vary app source refs my-api --target prod --json

Validation Ladder

Use fast local gates before a full app smoke. For compiler, HTTP DSL, generated resource, request-boundary, or SQLite binding changes, start with the narrow unit/codegen test that covers the behavior. For app-level HTTP and SQLite binding coverage, prefer the small examples/http-service-smoke fixture:

./gradlew :compiler:test --tests vary.codegen.CodegenServiceHttpDataTest
./gradlew :compiler:test --tests vary.integration.HttpServiceJarSmokeTest

Run the full issue-tracker smoke only as an explicit slow final gate after the fast tests pass:

VARY_RUN_ISSUE_TRACKER_SMOKE=1 ./gradlew :compiler:test --tests vary.integration.HttpServiceJarSmokeTest

Avoid --rerun-tasks unless intentionally doing a cold rebuild; it bypasses Gradle caching and can turn a small source validation into a full rebuild.

Reconcile and delete

Failed deploy attempts remain historical evidence; they should not become the desired app state. When a retry is blocked by stale route/add-on state or a bad staged profile, use the narrowest lifecycle command:

NeedCommand
Fill missing required config or secretsvary app config template my-api --target prod --workdir . > my-api.env then vary app config apply my-api --target prod --file my-api.env
Retry a failed or cancelled deployvary app deploy retry <deploy-id> --target prod --app my-api --apply-manifest --follow
Inspect stale route/add-on/deploy driftvary app reconcile my-api --target prod --apply-manifest --plan --json
Apply developer-safe cleanupvary app reconcile my-api --target prod --apply-manifest --apply
Retire the whole appvary app delete my-api --target prod --plan --json then --apply --confirm my-api

Developer reconcile apply is deliberately narrow: it can handle developer-safe cleanup, but it refuses operator-only platform repairs such as SQLite storage repair or active traffic cleanup and prints an escalation command instead. Deploy retry with --apply-manifest runs the same safe automatic reconciliation before queueing the replacement deploy. Safe inactive stale routes are cleaned up automatically; active stale traffic is not removed by a developer retry and returns the operator reconcile command.

app delete is a decommission workflow, not a deploy retry helper. Its plan lists every affected resource and retention decision before mutation. Apply requires the app name through --confirm; missing or mismatched confirmation fails before route, runtime, add-on, or storage changes. Stateful add-ons are retained by default. App-owner self-service apply is limited to safe retained-state cases and blocks when deletion would disable active routes, retire certificate-bound domains, purge tombstones, or destroy stateful add-ons. Operators can request --detach-addons, --snapshot-addons, or --destroy-addons after deciding the retention policy. Destroying managed SQLite storage also requires --confirm-destroy-sqlite; otherwise the command reports the operator escalation path.

If apply fails after cleanup has started, Via reports delete_partial_retryable with partial: true, leaves the app in decommission_failed, and includes the retry command to run after fixing the blocker.

Deleted apps enter a lifecycle state that blocks preflight and deploy. The app name remains protected by a deletion tombstone until retained resources no longer conflict and an operator purges the tombstone.

← Deploy an app
Deploy diagnostics →