The control plane's configuration is file-based and lives in two layers:
overpass.yaml— hand-authored, never written by the server. Anything declared here is authoritative and shows as read-only in the web UI.- The config directory (
<data_dir>/overpass.dby default; relocate withconfig_dir:) — owned and written by the server. Everything created or edited through the web UI or API — cluster settings, roles, SSO connectors, desktop credentials and endpoints — is persisted here as YAML (settings.yaml,roles.yaml,connectors.yaml,desktop.yaml) and applied immediately, no restart needed. Secrets entered in the UI are stored encrypted with the cluster master key. Hand edits belong inoverpass.yaml, not here.
Users, nodes, and join tokens are runtime state and live in the database,
managed with opctl or the web UI. Clusters upgraded from a release that kept
configuration in the database are migrated automatically on first boot: the
old rows are written into the config directory (secrets stay encrypted) and
the legacy tables are dropped.
Config file
overpass server and opctl read /etc/overpass.yaml (or /etc/overpass.yml)
by default, or the path given with --config. A full example:
cluster_name: acme
data_dir: /var/lib/overpass
audit_retention_days: 90 # days to keep audit events; 0 keeps them forever
proxy:
web_addr: 0.0.0.0:8443 # HTTPS: web UI + browser terminal + /api/v1 + /oidc
ssh_addr: 0.0.0.0:3023 # client SSH routing
tunnel_addr: 0.0.0.0:3024 # node reverse tunnels
grpc_addr: 0.0.0.0:3025 # gRPC API: auth, node enrollment, admin
public_addr: gate.acme.com # external host clients dial for SSH/gRPC + cert SANs
public_url: https://gate.acme.com # external base URL (set behind a reverse proxy)
trusted_proxies: [127.0.0.1/32] # proxies allowed to supply X-Forwarded-* headers
tls_routing: false # advertise the web port as the single endpoint (TLS ALPN routing)
# Process logging. --debug, --log-level and --log-format override these.
logging:
level: info # debug, info, warn, error or fatal
format: text # text (styled for a terminal), json or logfmt
# caller: false # source file and line; defaults to on at debug level
# timestamp: true # turn off under journald or `docker logs -t`
# Everything from the settings page; field names mirror the API. A field set
# here is locked in the UI. All optional.
settings:
default_logins: [ubuntu]
agent_tls: mldsa
# Declarative roles. Built-ins `admin` and `access` always exist; a same-name
# entry here overrides the built-in.
roles:
- name: sre
logins: [root, deploy]
node_labels: { env: prod }
rules: { manage_nodes: true, manage_tokens: true, terminal: true }
auth:
default_oidc: okta
oidc:
- name: okta
issuer_url: https://acme.okta.com
client_id: 0oaEXAMPLE
client_secret_env: OVERPASS_OKTA_SECRET
claims_to_roles:
- claim: groups
value: sre
roles: [admin]
# Browser desktop bridges plus declarative desktop targets (credentials and
# endpoints; see examples/overpass.yaml for the full shape). Probe state and
# enablement stay runtime actions in the UI.
desktop:
web_rdp_enabled: false
Connectors, roles, settings fields, desktop credentials and endpoints declared
in overpass.yaml are authoritative: the UI shows them with a config badge
(or as locked fields) and refuses edits until the file changes. Entities of the
same kind created through the UI live in the config directory and stay fully
editable; on a name collision the main file wins.
Precedence
An explicitly defined overpass.yaml value is authoritative, including
false, "", and []. The complete precedence order is:
- Explicit values in the selected
overpass.yaml - The server-managed config directory (UI/API-made configuration)
- Explicit command-line flags, then environment fallbacks
- Built-in defaults
To manage a setting from the web UI, omit that field from overpass.yaml
instead of setting it to an empty value.
Upgrading from database-managed configuration
Releases before the file-based model kept cluster settings, roles, SSO
connectors, and desktop credentials in the database. The first boot of a
file-based release migrates them automatically: every customized row is
written into the config directory (secrets stay encrypted under the same
master key), the fleet target version moves to its own table, and the legacy
tables are dropped. The startup log lists exactly what moved where. Entities
that are also declared in overpass.yaml are skipped with a warning — the
file wins. Nothing is required of you beyond backing up the data directory
before upgrading, as with any release.
Ports
| Port | Setting | Purpose |
|---|---|---|
8443 |
web_addr |
HTTPS — web UI, REST API, OIDC endpoints, browser terminal |
3023 |
ssh_addr |
Client SSH routing (op ssh connects here) |
3024 |
tunnel_addr |
Node reverse tunnels (nodes dial out here) |
3025 |
grpc_addr |
gRPC API — login, node enrollment, admin |
All four default to 0.0.0.0. With tls_routing: false, clients and nodes need
to reach 3023/3025 (and 3024 for nodes) directly.
Alternatively, with tls_routing: true only the web port needs to be
reachable: direct connections use TLS ALPN, while clients behind a normal
TLS-terminating HTTP reverse proxy automatically carry the same routed TLS
connection inside a WebSocket. The web address is advertised to clients and
nodes as the single endpoint. See
single-port TLS routing.
TLS uses private fallback certificates: an ECDSA web leaf issued by the local
tls-ca.crt, plus a self-signed ML-DSA leaf for Overpass agents. See
TLS certificates.
Public address vs. public URL
These cover the two ways the outside world reaches overpass:
public_addr— the external host and optional port clients dial for SSH, tunnels, and gRPC. With TLS routing this may be the same HTTP reverse proxy hostname on port443. The host is also baked into the fallback ECDSA TLS certificate's SANs.public_url— the external base URL the browser and your IdP use to reach the web UI (e.g.https://gate.acme.com). It drives absolute links such as the OIDC redirect URI. Set it when running behind a reverse proxy; when unset, overpass infers it from the request. Forwarded headers are honored only when the direct peer is listed intrusted_proxies.
Flags
overpass server flags provide fallbacks for values omitted from the file:
| Flag | Maps to |
|---|---|
--config |
path to the YAML file (default /etc/overpass.yaml) |
--cluster-name |
cluster_name |
--data-dir |
data_dir |
--web-addr / --ssh-addr / --tunnel-addr / --grpc-addr |
proxy.*_addr |
--public-addr |
proxy.public_addr |
--public-url |
proxy.public_url |
--trusted-proxies |
proxy.trusted_proxies |
--tls-routing |
proxy.tls_routing |
--debug / --log-level |
logging.level |
--log-format |
logging.format |
Environment fallbacks
For container deployments, these optional variables cover the two public
addresses without requiring a Compose command: override:
| Variable | Fallback for | Purpose |
|---|---|---|
OVERPASS_PUBLIC_ADDR |
proxy.public_addr |
Host clients use for SSH/gRPC and the TLS certificate SAN. |
OVERPASS_PUBLIC_URL |
proxy.public_url |
Browser-facing base URL used for links and OIDC redirects. |
These variables do not override a value explicitly present in YAML. When
public_url is empty, Overpass infers the browser address from the request (and
only trusts forwarded headers from trusted_proxies). YAML-defined values are
reapplied on every startup; values omitted from YAML can be managed through the
web UI, which persists them in the config directory.
Deployment mode and runtime
Overpass inspects its environment on startup to decide whether in-place binary self-update and fleet updates are permitted:
| Variable | Values | Purpose |
|---|---|---|
OVERPASS_RUNTIME |
binary, container, package |
Declares how Overpass is deployed. binary allows in-place self-update. container and package disable self-update so updates are managed by image pulls or package rebuilds. |
OVERPASS_CONTAINER |
1, 0 |
Legacy container override. 1 forces container mode; 0 forces binary mode. OVERPASS_RUNTIME takes precedence when set. |
Docker, Podman, and Kubernetes default to container image deployments (OVERPASS_RUNTIME=container). Persistent system containers (LXC, Incus, systemd-nspawn) run systemd over a persistent rootfs and default to binary deployment mode.
Initial setup
On a new data directory, Overpass prints a one-time setup claim code in the
startup log. Open /setup in the browser, enter that code, and create the first
administrator. The setup flow creates a full admin account, starts a browser
session, and points to server enrollment, CLI installation, Unix login access,
HTTPS, and SSO. The claim code is encrypted at rest until it is used; after the
first user is created, setup cannot be reopened, even if that user is later
deleted.
Data directory
data_dir (default /var/lib/overpass) holds the SQLite database
(overpass.db), the CA private keys, the ECDSA tls-ca.crt and
tls.crt / tls.key, plus the self-signed tls-mldsa.crt /
tls-mldsa.key (ML-DSA-44, the default for Go agents).
Browsers and older agents always use the ECDSA pair. Back it up — losing
the CA keys invalidates every issued certificate — and back up the config
directory (overpass.d/) with it: it holds UI-made configuration, and its
secrets are encrypted with the master.key from the data directory. The
server creates the configured directory when needed and restricts it to mode
0700 on startup. In a container it is exposed as a volume; the process runs
as a non-root user, so a bind-mounted or named-volume directory must already be
owned or writable by the configured container uid (default 65532; see
PUID/PGID in the Docker guidance). The process cannot repair ownership of a
root-owned mount.