
<div class="callout callout-attn"><p><strong>Alpha.</strong> VIA is under active development. APIs, operational flows, and host requirements may change between releases.</p></div>

This page sets up one Via host. It installs the layout, writes config and signing keys, checks preconditions, mints the first operator credential, starts the services, and provisions the first app.

Operator examples assume `via install` has added your host user to the Via
admin/shared group. Reconnect or run `newgrp vary` after first install so the
membership is active in your shell.

## Prerequisites

| Requirement | Detail |
|---|---|
| Host OS | Ubuntu 24.04 with `sudo` and active `systemd`. |
| Container runtime | Docker daemon running and reachable. |
| Git tooling | `git` and `git-http-backend` on `PATH` from the Ubuntu `git` package. |
| Network | Port 8080 free on the host. |
| Vary release | The release tarball, or a `vary` binary in `/usr/local/bin/`. |

VSIX packaging in the release archive requires Node 20 or newer. If your build
host has an older Node, the release script stops before `npm ci` with the
detected version and the exact fix instead of continuing through noisy package
engine warnings.

If a full local API smoke run includes an app that binds the generated service
to its default port, port 8080 must be free for that smoke. On a host that was
previously installed by Via, the expected owner may be the Via control-plane
unit:

```text
/etc/systemd/system/via-server.service
ExecStart=/opt/vary/bin/via start
Environment=VARY_HOME=/opt/vary
```

That is Via-owned host state, not a random leaked process. Do not have a local
test harness stop it implicitly. Stop it deliberately before a local default-8080
smoke and start it again afterward:

```bash
sudo systemctl stop via-server.service
# run the local API smoke that needs 8080
sudo systemctl start via-server.service
```

If the host should move to a newer Via version, install and select that version
with `varyup`, then let Via promote it into the system services:

```bash
varyup toolchain install <version>
varyup default <version>
via upgrade
via doctor
```

You do not need to check these by hand. `via install` runs the same checks itself and refuses to continue if any are missing. The next two sections show what that looks like on a host that is not yet prepared and how to inspect the prerequisite script before running it.

No system JRE is required. The release bundles its own JRE; builder and runtime images carry their own, so do not install OpenJDK separately for Via.

### What `via install` prints when prereqs are missing

On a clean Ubuntu 24.04 host without required host packages, `via install` runs preflight, prints a PASS/FAIL line for every probe, and exits non-zero before touching the host:

```text
    Checking Via host prerequisites
      Passed ubuntu: 24.04
      Passed systemd: active
      Failed git: not found in PATH
      Failed git-http-backend: not found; install the 'git' package (supplies /usr/lib/git-core/git-http-backend)
      Failed docker: Docker daemon is not running or not reachable
      Failed proxy: public proxy package is not installed
     Warning docker-network: vary-apps missing; install step will create it
      Passed port: 8080 available
      Passed disk: /var/lib/via has 48 GB free
      Passed permissions: /var/lib/via writable
        Info java: not checked; Vary uses bundled runtime
error: preflight failed. Fix the issues above or pass --skip-preflight.
info: install Ubuntu host prerequisites with: via install --print-prereq-script | sudo bash
```

Every `Failed` line names the probe, the detail, and (where useful) the remediation. The `vary-apps` Docker network is reported as `Warning` because `via install` creates it for you on the next successful run.

### Inspect the prerequisite script

`--print-prereq-script` prints a self-contained Ubuntu 24.04 bootstrap script to stdout and exits. It does not touch the host. Inspect the exact script from your installed Via build before piping it to `sudo bash`:

```bash
via install --print-prereq-script
```

The script refuses to run as a non-root user, refuses anything other than Ubuntu 24.04, installs only the host packages Via depends on, disables the distro-managed public proxy service so Via's managed service can take over, and creates the `vary-apps` Docker network. It does not create Via users, directories, systemd units, or signing keys; those are `via install`'s job in the next step.

## Step 1: install the host layout

```bash
# Install host prerequisites and the Via Docker network.
via install --print-prereq-script | sudo bash

# Create Via users, directories, systemd units, and admin group access.
via install
```

This command creates the host layout, three service users, the shared `vary` system group, and the Via-managed systemd unit files. Re-running it is safe and does nothing when the layout is already correct.

| Path | Purpose | Owner / mode |
|---|---|---|
| `/etc/via/` | Config root | `via-server:vary`, `0755` |
| `/var/lib/via/` | Durable state (DB, identity, repos, builds, artifacts) | `via-server:vary`, `2750` |
| `/var/log/via/` | Per-daemon log files | `via-server:vary`, `2750` |
| `/etc/systemd/system/via-server.service` | Control-plane unit | root, `0644` |
| `/etc/systemd/system/via-builder.service` | Build worker unit | root, `0644` |
| `/etc/systemd/system/via-runner.service` | Runtime supervisor unit | root, `0644` |
| `/etc/systemd/system/via-test-runner.service` | Test worker unit | root, `0644` |
| `/etc/systemd/system/via-config.service` | Runtime config unit | root, `0644` |
| Managed proxy unit | Public routing unit | root, `0644` |

Three service users are created, all in the shared `vary` group. The invoking
operator is added to the same Via admin/shared group so follow-up commands such
as `via init`, `via doctor`, and `via app ensure` do not need to be run as root.

| User | Role |
|---|---|
| `via-server` | Owns config and the SQLite control plane. Runs the HTTP daemon. |
| `via-builder` | Runs build jobs in sandboxed containers. Member of `docker`. |
| `via-runner` | Supervises runtime containers. Member of `docker`. |

After installation, reconnect to the host or run `newgrp vary` if this is the
first time your user was added to the group. `via doctor` verifies that the unit
files, service users, shared group, and Docker access are present.

## Step 2: initialize the host

```bash
via init --domain <your-host-fqdn>
```

This writes `/etc/via/server.toml`, mints two signing keys under `/var/lib/via/identity/`, and creates the empty control-plane SQLite database at `/var/lib/via/control-plane.db`. One key signs identity tokens. The other signs artifacts. The default log path (`/var/log/via/server.log`), control-plane listen port (`8080`), and config-server port (`8181`) are recorded in `server.toml`.

`server.toml` has four sections:

```toml
[server]
domain = "your-host-fqdn"
external_url = "https://your-host-fqdn"
host = "127.0.0.1"
port = 8080

[storage]
root = "/var/lib/via"

[identity]
issuer = "https://your-host-fqdn"

[git]
https_enabled = true
backend = "git-http-backend"
root = "/var/lib/via/repos/bare"

[builder]
poll_interval_ms = 1000

[runner]
poll_interval_ms = 1000

[runtime]
backend = "docker"
network = "vary-apps"

[config_server]
host = "0.0.0.0"
port = 8181

[acme]
mode = "prod"

[logging]
log_file = "/var/log/via/server.log"
```

Re-running `init` against a populated `/etc/via/` exits non-zero rather than rotating keys silently. Key rotation is a separate operator action.

After initialization, `via doctor` verifies that the config file, database,
signing keys, and app-build prerequisites are present. The app-build section
names the selected concrete toolchain from `varyup`, compiler input, stdlib
input, runtime input, runtime image readiness, Docker, and Git endpoint status.

## Step 3: upgrade an installed host

After installing and selecting a newer Vary/Via toolchain with `varyup`,
promote it into the systemd service runtime with one command:

```bash
varyup toolchain install <version>
varyup default <version>

# Back up Via state, reconcile host layout, promote the runtime, and restart services.
via upgrade
```

`via upgrade` creates a pre-upgrade backup, applies any non-destructive host
layout changes from the new release, updates `/opt/vary/current`, rewrites the
service launchers, restarts all Via units, and checks that the services stay
active. You do not need to run `via install` before every upgrade.

If an upgrade is interrupted after the new toolchain is installed, rerun:

```bash
via upgrade resume
```

`resume` follows the same idempotent upgrade path and is safe after a
pre-baseline repair or after a previous attempt stopped before service
promotion.

For a host created by an unreleased pre-baseline Via build, `via doctor` may
report `control-plane:migration-checksum` with
`prebaseline_checksum_drift`. Do not force the migration. Use the supported
recovery path:

```bash
via backup create --include-control-plane --allow-checksum-drift /var/lib/via/backups/prebaseline.zip
via control-plane rebaseline --from-prebaseline --backup-id <backup-id> --confirm "REBASELINE /var/lib/via/control-plane.db TO 1"
via upgrade resume
via doctor
```

## Step 4: check host health

```bash
# Confirm services, permissions, Docker, Git, and public-routing health.
via doctor
```

`via doctor` is a fast, read-only check. It reports schema version, systemd unit health, Docker reachability, the `vary-apps` Docker network, `git-http-backend` on `PATH`, port 8080 binding, and key file permissions. Use it after install, after config changes, and before investigating deploy failures.

Probes (each prints PASS or FAIL with a one-line reason):

| Probe | What it checks |
|---|---|
| `schema` | Control-plane DB exists and the embedded schema version matches the binary |
| `systemd` | Each Via-managed unit is loaded and either `inactive` (pre-enable) or `active` |
| `docker` | `docker info` succeeds and the `vary-apps` network exists |
| `git` | `git-http-backend` is on `PATH` |
| `port` | Port 8080 is either free (pre-start) or owned by `via-server` |
| `keys` | Signing keys have the expected owner and permissions |

Exit code is 0 only when every probe passes. Non-zero exit means a precondition for the next steps is missing.

Each probe prints a PASS line when the host is ready for the next step.

## Step 5: create the first operator credential

On the host:

```bash
via admin create <your-name>
```

This prints a one-time `adm_...` token and a `vary login` command. The token is shown once. The server stores only a hash for verification. The token never lands in `~/.git-credentials` or any keychain on the workstation. After `vary login` exchanges the admin token for a session, the workstation stores a short-lived session under `~/.vary/credentials/sessions/<slug>.json` (mode `0600`).

For deployment interruptions caused by an expired workstation session, create a
short-lived scoped token instead of a new permanent admin credential:

```bash
via admin token create deployer --scope app:deploy --ttl 2h
```

`vary login` prints the resulting session scope and expiry so the developer can
verify they received the intended temporary access.

Sample output:

```text
Admin user 'alice' created.
One-time token (will not be shown again):

  adm_01HXXX...

Run on your workstation:

  printf '%s\n' 'adm_01HXXX...' | vary login https://your-host-fqdn --name alice --token-stdin
```

On your workstation:

```bash
vary login https://<your-host-fqdn> --name <your-name> --token-stdin
```

`vary login` exchanges the admin token for a session JWT, stores the session at `~/.vary/credentials/sessions/<slug>.json` with mode `0600`, and prints the session expiry plus the stored path. The admin token is single-use; subsequent `vary login` calls re-use the session until it expires, then prompt for re-auth.

The saved session includes the server URL, key id, expiry, and bearer token, but never the original admin token.

## What you have now

After steps 1 through 4, you have an installed, initialized, checked host with one operator credential exchanged for a short-lived session on your workstation.

## Step 6: verify the server daemons

`via init` reloads systemd, enables the Via units, starts them, and checks that
each unit becomes active. Verify the running host through Via:

```bash
via status
via doctor
```

| Unit | Responsibility |
|---|---|
| `via-server.service` | Control-plane API, auth, JWKS, health, and Git HTTP frontend. |
| `via-builder.service` | Claims queued deploys and runs checkout, typecheck, tests, compile, and signing. |
| `via-runner.service` | Verifies signed artifacts and launches runtime containers. |
| `via-test-runner.service` | Runs server-side test work. |
| `via-config.service` | Serves runtime config, secret, and add-on descriptor requests. |
| Managed proxy service | Publishes validated public routes. |

You want each service to report `active`. If a unit fails to start, `via init`
and `via doctor` print the journal command for the affected units.

## Step 7: provision the first app

Before a project can deploy, the server needs an app record and managed source repository:

```bash
via app ensure my-api --owner alice
```

For clean-host validation, use the minimal smoke fixture
`tests/fixtures/vary-server/hello` as the first deployed app. It has a valid
`[app]` declaration, one build test, no external dependencies, and is the app
used by the remote smoke harness before larger apps such as `issue-tracker`.

This command is idempotent for the same app and owner. It creates:

| Resource | Result |
|---|---|
| App record | The server-side app entry. |
| Source repository | The managed source repository for deploy intake. |
| Hook | Managed deploy-intake hook. |

## Finished state

The host is now installed, initialized, healthy, running the core daemons, and has one app ready for app-owner deploys.

## Uninstall

To remove a Via host installation, stop the daemons first, run a dry run, then confirm the removal:

```bash
via uninstall --dry-run
via uninstall --yes
```

`via uninstall --yes` removes Via-managed config, logs, state, and systemd unit files:

| Resource | Default path |
|---|---|
| Config | `/etc/via/` |
| State | `/var/lib/via/` |
| Logs | `/var/log/via/` |
| systemd units | `/etc/systemd/system/via-*.service` for Via-managed units |

Use `--keep-data` when you want to remove the host wiring but preserve durable server state:

```bash
via uninstall --keep-data --yes
```

With `--keep-data`, `/var/lib/via/` is preserved, including the control-plane DB, identity keys, repos, artifacts, app state, add-ons, toolchains, and runtime image cache. Config, logs, and units are still removed. Host packages are left installed.

The uninstall command does not delete system users or groups (`via-server`, `via-builder`, `via-runner`, `vary`), does not remove Docker group membership, and does not remove Docker networks or containers. Clean those up separately only after confirming no other Via-managed processes or data need them.

Continue to [Deploy an app](/docs/via/deploy/).
