Configuration Reference
Mezite is configured through a YAML file, typically located at
/etc/mezite/mezite.yaml. Every setting can also be overridden
via environment variables. This page documents every field, its type,
default value, and the corresponding environment variable.
Config Loading Order
Settings are resolved in the following order, with later sources taking precedence:
- Built-in defaults — Sensible values baked into the binary.
- Config file — Loaded from the path given by
--config. - Environment variables — Prefixed with
MEZITE_, these override any value set in the config file.
Environment variables always win. This makes it easy to inject secrets (like database passwords and CA passphrases) without writing them to disk.
Minimal Config
The smallest useful configuration — just enough to start a combined-mode server with SQLite (the default):
cluster_name: my-cluster
ssh:
enabled: true For PostgreSQL, set the driver and connection fields:
cluster_name: my-cluster
database:
driver: postgres
host: localhost
port: 5432
user: mezite
password: mezite
name: mezite
sslmode: disable
ssh:
enabled: true Full Example
Below is a complete mezite.yaml with every field and its default
value. In practice you only need to specify values that differ from the defaults.
# ─── Cluster ─────────────────────────────────────────────
cluster_name: my-cluster
# ─── Logging ─────────────────────────────────────────────
log:
level: info
format: json
# ─── Database ────────────────────────────────────────────
database:
driver: sqlite # sqlite (default) or postgres
host: localhost # PostgreSQL only
port: 5432 # PostgreSQL only
user: mezite # PostgreSQL only
password: mezite # PostgreSQL only
name: mezite # PostgreSQL only
sslmode: require # PostgreSQL only
# ─── Auth Service ────────────────────────────────────────
auth:
grpc_allow_http: false
# Top-level: CA private key encryption passphrase
ca_key_passphrase: ""
# ─── Proxy Service ───────────────────────────────────────
proxy:
public_addr: "" # required for OIDC, WebAuthn, etc.
listen_addr: 0.0.0.0:3080
ssh_listen_addr: 0.0.0.0:3023
tunnel_listen_addr: 0.0.0.0:3024
apps_domain: "" # parent domain for application access (empty = off)
apps_oidc_connector: "" # SSO connector that gates browser app access
# apps_keypair: # optional dedicated wildcard keypair for *.<apps_domain>
# cert_file: /etc/mezite/apps-wildcard.crt
# key_file: /etc/mezite/apps-wildcard.key
# ─── SSH Service ─────────────────────────────────────────
ssh:
enabled: false Cluster
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
cluster_name | string | mezite | MEZITE_CLUSTER_NAME | Unique identifier for this cluster. Embedded in all certificates and used to namespace audit events. Must be a valid DNS label. |
Logging
Logging keys below apply to the mezhub server. The mezd agent emits its own structured logs at production defaults; it does not read
MEZITE_LOG_LEVEL or MEZITE_LOG_FORMAT.
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
log.level | string | info | MEZITE_LOG_LEVEL | Minimum log level for mezhub. One of debug, info, warn, error. |
log.format | string | json | MEZITE_LOG_FORMAT | Log serialization format for mezhub. json
for structured output, text for human-readable. |
Database
Mezite supports SQLite and PostgreSQL backends. SQLite is the simplest option for self-hosted deployments (zero external dependencies). PostgreSQL 16+ is recommended for production and managed deployments.
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
database.driver | string | sqlite | MEZITE_DB_DRIVER | Database backend: sqlite or postgres. |
database.url | string | "" | MEZITE_DB_URL | Connection URL. For SQLite: file path. For PostgreSQL: DSN. When
empty, built from fields below (PG) or defaults to <data_dir>/mezhub.db (SQLite). |
database.host | string | localhost | MEZITE_DB_HOST | PostgreSQL hostname or IP address. |
database.port | integer | 5432 | MEZITE_DB_PORT | PostgreSQL port. |
database.user | string | mezite | MEZITE_DB_USER | Database user (PostgreSQL only). |
database.password | string | "" | MEZITE_DB_PASSWORD | Database password (PostgreSQL only). |
database.name | string | mezite | MEZITE_DB_NAME | Name of the PostgreSQL database. |
database.sslmode | string | require | MEZITE_DB_SSLMODE | PostgreSQL TLS mode. Accepts: disable, require, verify-ca, verify-full. |
export MEZITE_DB_DRIVER=sqlite
export MEZITE_DB_URL=/var/lib/mezite/mezhub.db export MEZITE_DB_DRIVER=postgres
export MEZITE_DB_HOST=db.internal.example.com
export MEZITE_DB_PORT=5432
export MEZITE_DB_USER=mezite
export MEZITE_DB_PASSWORD='$(vault kv get -field=password secret/mezite/db)'
export MEZITE_DB_NAME=mezite
export MEZITE_DB_SSLMODE=verify-full Auth
Local username/password authentication is always available out of the box;
SSO connectors (OIDC, SAML, LDAP, GitHub OAuth) are configured at runtime
with mezctl connectors create, not via this YAML file. See
the SSO Guide for the connector schema.
First-issue SSH certificate TTL is set per call to
IssueUserCerts. It defaults to 12h and is hard-capped at 24h
regardless of any role or cluster setting. Per-role max_session_ttl is a separate knob that tightens cross-cluster certificate issuance further;
it does not raise the 24h ceiling. The schema field
auth.session_ttl is currently reserved — it appears for forward
compatibility but is not read by any code path today.
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
auth.grpc_allow_http | boolean | false | MEZITE_GRPC_ALLOW_HTTP | Allow plaintext h2c (HTTP/2 cleartext) on the gRPC listener. This
is HTTP/2 in the clear — use only when a TLS-terminating load
balancer or service mesh fronts mezhub. If anything
other than that fronts the listener, leave this off and present a
certificate to clients directly. |
ca_key_passphrase (top-level) | string | "" | MEZITE_CA_KEY_PASSPHRASE | Passphrase used to encrypt Certificate Authority private keys at rest in the database. |
Proxy
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
proxy.public_addr | string | "" | MEZITE_PROXY_PUBLIC_ADDR | The public address that clients use to reach this proxy (e.g.
mezite.example.com:443). Used to derive the WebAuthn
relying-party ID/origin and the cluster's GetServerInfo
enrollment URL. The OIDC issuer URL is configured separately via
proxy.oidc_issuer_url. |
proxy.listen_addr | string | 0.0.0.0:3080 | -- | Bind address for the HTTPS listener. |
proxy.ssh_listen_addr | string | 0.0.0.0:3023 | -- | Bind address for the SSH listener. |
proxy.tunnel_listen_addr | string | 0.0.0.0:3024 | -- | Bind address for the reverse-tunnel listener. |
proxy.oidc_issuer_url | string | "" | MEZITE_OIDC_ISSUER_URL | Public issuer URL surfaced at /.well-known/openid-configuration and used in workload-identity JWT SVIDs. No trailing slash. |
proxy.trusted_ip_header | string | "" | -- | HTTP header to read real client IP from (e.g. Fly-Client-IP, X-Forwarded-For). Config-file only — there is no
dedicated env var for this field. |
proxy.single_port | boolean | false | MEZITE_SINGLE_PORT | Enable ALPN single-port routing on the HTTPS listener. When on, the
proxy demultiplexes SSH, agent tunnel, and HTTPS traffic onto
proxy.listen_addr using TLS ALPN protocol IDs, so a cluster
can be reached entirely on :443. The individual ssh_listen_addr and
tunnel_listen_addr sockets still bind unless their ports
are unset; clients reaching the proxy on :443
are routed by ALPN. |
proxy.proxy_protocol | string | off | -- | Mode for HAProxy PROXY protocol v1/v2 headers on the SSH and tunnel
listeners. Accepted values are off (default; PROXY headers
ignored) and on (PROXY headers required from trusted source
ranges). When set to on, pair with
proxy.proxy_protocol_trusted_cidrs so only trusted load-balancer
source ranges can rewrite the client IP. See
Reverse Proxy. |
proxy.proxy_protocol_trusted_cidrs | list of strings | (empty) | -- | IPs or CIDRs permitted to send PROXY headers when
proxy.proxy_protocol is on. Required in
that case — connections from any other source are rejected. |
proxy.apps_domain | string | "" | MEZITE_PROXY_APPS_DOMAIN | Parent domain for application access hostnames (e.g. apps.example.com makes apps reachable at
<app>.apps.example.com). Empty disables
application-access host routing; nothing else about the cluster
changes. Requires a wildcard DNS record and a TLS certificate
covering *.<apps_domain>. |
proxy.apps_keypair.cert_file /
proxy.apps_keypair.key_file | string | "" | MEZITE_PROXY_APPS_CERT_FILE /
MEZITE_PROXY_APPS_KEY_FILE | Optional dedicated wildcard (or multi-SAN) keypair for
*.<apps_domain>, SNI-selected for app subdomains.
When unset, the proxy's main HTTPS certificate must cover the apps
wildcard in its SANs. Both fields are required together, and only
take effect when apps_domain is set — otherwise the proxy
refuses to start. |
proxy.apps_oidc_connector | string | "" | MEZITE_PROXY_APPS_OIDC_CONNECTOR | Name of the SSO connector the browser application-access gate redirects unauthenticated users to. Required for the browser app gate to start a login. |
SSH
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
ssh.enabled | boolean | false | -- | Enable the built-in SSH service on this node. |
Recording
Controls where session recordings are written and how/when the agent uploads them. See the Session Recording guide for end-to-end behavior.
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
recording.backend | string | local | MEZITE_RECORDING_BACKEND | Storage backend: local (filesystem under
data_dir) or s3. |
recording.s3.bucket | string | "" | MEZITE_S3_BUCKET | S3 bucket name. |
recording.s3.region | string | us-east-1 | MEZITE_S3_REGION | S3 region. |
recording.s3.endpoint | string | "" | MEZITE_S3_ENDPOINT | Custom endpoint URL for MinIO or other S3-compatible stores. Empty uses AWS defaults. |
recording.s3.access_key | string | "" | MEZITE_S3_ACCESS_KEY | Static access key. Leave empty to use the default AWS SDK credential chain (IRSA, IAM role, env, ~/.aws). |
recording.s3.secret_key | string | "" | MEZITE_S3_SECRET_KEY | Static secret key. Paired with access_key. |
recording.s3.prefix | string | "" | MEZITE_S3_PREFIX | Optional key prefix under the bucket (e.g.
cluster-a/recordings/). |
recording.s3.force_path_style | boolean | false | MEZITE_S3_FORCE_PATH_STYLE | Use path-style addressing (https://endpoint/bucket/key) instead of virtual-host style. Required for MinIO and some
on-prem stores. |
recording.s3.auto_create_bucket | boolean | false | MEZITE_S3_AUTO_CREATE_BUCKET | Create the bucket on startup if it does not exist. Off in production; intended for dev and tests. |
recording_enc_key (top-level) | string | "" | MEZITE_RECORDING_ENC_KEY | Hex-encoded 32-byte AES-256-GCM key. When set, the agent encrypts
every recording chunk before handing it to the storage backend
(covers both local and s3). Leaving this
empty disables recording encryption; the recording is written in the
clear to the backend. |
| -- | string | -- | MEZITE_RECORDING_MODE | Set on mezd, not mezhub. Controls
agent-side recording behaviour: node (default — buffer locally
and upload after the session ends) or node-sync
(stream chunks to the auth service in real time; agent terminates the
SSH session if the upload stream breaks). |
Audit Log Sinks
Audit events are always written to the cluster database. In addition, events can be mirrored to one or more external sinks for SIEM, alerting, or long-term archive. See the Audit Logging guide.
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
audit_hmac_key (top-level) | string | "" | MEZITE_AUDIT_HMAC_KEY | Hex-encoded HMAC key for the tamper-detection chain on persisted audit events. Strongly recommended in production. Rotating this key invalidates the existing chain — keep the old value alongside the new one during transition. |
| -- | string | -- | MEZITE_AUDIT_SINK_FILE_PATH | When set, every audit event is appended to this file in newline-delimited JSON format (one JSON object per line). Useful for shipping to a log forwarder or for offline archival. |
| -- | string | -- | MEZITE_AUDIT_SINK_WEBHOOK_URL | When set, every audit event is POSTed as JSON to this URL. Common target for Splunk HEC, Datadog Logs intake, Elastic ingest, or a tenant-side Lambda. The receiving endpoint should be idempotent — transient retries can deliver the same event more than once. |
| -- | duration | 5s | MEZITE_AUDIT_SINK_WEBHOOK_TIMEOUT | HTTP timeout for the webhook sink. Slow endpoints stall the mirror; the in-process audit pipeline keeps emitting to the database regardless. |
KMS-Backed CA Keys
By default, CA signing keys live in the cluster database, AES-256-GCM
encrypted with the key derived from
MEZITE_CA_KEY_PASSPHRASE. For higher assurance — and for
managed deployments — the cluster can be configured so that every CA
signing operation calls AWS KMS instead, with the raw private key material
never leaving the KMS HSM boundary.
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
kms.enabled | boolean | false | MEZITE_KMS_ENABLED | Route CA signing through AWS KMS. When on, both
kms.region and kms.alias_prefix are required
(validated at startup). |
kms.region | string | "" | MEZITE_KMS_REGION | AWS region the KMS keys live in (e.g. eu-west-2). |
kms.alias_prefix | string | "" | MEZITE_KMS_ALIAS_PREFIX | Prefix used when building per-tenant KMS aliases. Must not start
with alias/ (the CA layer prepends it) and must end with
-. The final alias shape is
alias/<prefix><tenant>-<ca-type>. |
| -- | string | -- | MEZITE_KMS_EXTRA_TAGS | Comma-separated key=value pairs to apply on KMS
CreateKey for each new CA key. Used by the managed plane
to attach the tenant tag the per-tenant IAM policy keys off of. |
Managed Mode
Set on cloud-managed tenants. When on, the cluster fails to start unless
kms.enabled is also true — managed tenants are required to sign
with KMS-backed keys so the platform operator can revoke a tenant out-of-band.
Leave this off for self-hosted deployments.
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
managed_mode (top-level) | boolean | false | MEZITE_MANAGED_MODE | Enable managed-tenant behaviour. Forces
kms.enabled=true; refuses to boot otherwise. |
Moderation and Access Requests
| Field | Type | Default | Env Var | Description |
|---|---|---|---|---|
moderation.transfer_approval_timeout | duration | 5m | MEZITE_TRANSFER_APPROVAL_TIMEOUT | How long the proxy waits for moderator approval on an SFTP transfer before denying. Capped at 5 minutes — the agent enforces the same ceiling locally, so a longer value would let the agent deny a transfer the proxy still considers pending. |
| -- | duration | 7d | MEZITE_ACCESS_REQUEST_MAX_DURATION | Maximum duration an access-request grant can be issued for. A request asking for a longer window is rejected; a shorter window is honoured as-is. |
| -- | duration | 30m | MEZITE_HOST_CERT_TTL | TTL on agent host certificates. Lower values shorten the window an attacker who exfiltrates a node's signing material can keep impersonating that host. |
Workload Identity (mezd identity)
The mezd identity daemon runs on a workload node, exposes a Unix
socket that local workloads can call to fetch short-lived X.509/JWT SPIFFE SVIDs,
and renews those SVIDs on a timer. These knobs are read by mezd identity only, not by
mezhub.
| Variable | Required | Description |
|---|---|---|
MEZITE_IDENTITY_TOKEN | Yes | Bootstrap join token used to authenticate the daemon to
mezhub on first start. |
MEZITE_IDENTITY_DIR | No | Directory holding the persisted identity material between renewals. |
MEZITE_IDENTITY_TTL | No | Requested SVID TTL (server may clamp lower). |
MEZITE_IDENTITY_RENEWAL_INTERVAL | No | How often the daemon renews. Should be well under
MEZITE_IDENTITY_TTL. |
MEZITE_IDENTITY_ONE_SHOT | No | Run a single renewal and exit. Useful for CI helpers and Machine ID-style use cases where another process holds the SVID lifecycle. |
MEZITE_WORKLOAD_SOCKET | No | Path of the Unix socket workloads connect to. Default
/var/run/mezite/workload.sock. |
Bootstrap and Operations
| Variable | Description |
|---|---|
MEZITE_ADMIN_PASSWORD | When set on first boot, seeds the bootstrap admin account with this
password. After the cluster is initialised the variable can be
removed; subsequent admin accounts are created via
mezctl users create. |
MEZITE_BOOTSTRAP_JOIN_TOKEN | When set on first boot, creates a node-role join token with this
value so an initial mezd can register without first invoking
mezctl tokens create. |
MEZITE_DATA_DIR | Override for the on-disk data directory (default
/var/lib/mezite) used by SQLite, local recordings, and
the join-state file. |
MEZITE_METRICS_ADDR | When set (e.g. :9100), exposes a Prometheus
/metrics endpoint on the given address. |
MEZITE_METRICS_TOKEN | Optional bearer token required to scrape the metrics endpoint. Leave unset to expose metrics without authentication on an internal listener. |
MEZITE_HTTPSAFE_ALLOW_PRIVATE /
MEZITE_HTTPSAFE_ALLOW_LOOPBACK | Test-and-dev overrides for the outbound HTTP allowlist that
normally blocks mezhub from calling private or loopback addresses
(used by OIDC discovery, SAML metadata, webhook sinks). Off in production. |
Port Reference
| Port | Protocol | Component | Description |
|---|---|---|---|
3025 | gRPC | Auth Service | Internal auth API. Should not be exposed publicly. |
3080 | HTTPS | Proxy Service | Web UI, REST API, OIDC callbacks. |
3023 | SSH | Proxy Service | SSH client connections via msh. |
3024 | gRPC | Proxy Service | Agent reverse-tunnel connections. |
5432 | PostgreSQL | Database | State store (only when using PostgreSQL backend). |
Agent Configuration
The mezd binary is configured entirely via environment variables.
| Variable | Required | Description |
|---|---|---|
MEZITE_JOIN_TOKEN | Yes (first run) | One-time join token. Only needed on first join. |
MEZITE_AUTH_ADDR | Yes | Address of the Auth service (e.g. mezite.example.com:3025). |
MEZITE_PROXY_ADDR | Yes | Address of the Proxy tunnel listener (e.g. mezite.example.com:3024). |
MEZITE_NODE_NAME | No | Node name shown in msh ls. Defaults to hostname. |
MEZITE_NODE_LABELS | No | Comma-separated key=value labels for RBAC matching (e.g. env=prod,role=web). |
MEZITE_DATA_DIR | No | Data directory for identity and recordings. Default: /var/lib/mezite. |
MEZITE_RECORDING_MODE | No | Recording mode: node (default — async upload after session)
or node-sync (real-time streaming to the auth service). See
Session Recording. |
MEZITE_TLS_WRAP | No | Wrap tunnel connection in TLS. Required when connecting through a TLS-terminating load balancer. See Reverse Proxy. |
MEZITE_BPF_ENABLED | No | Enable eBPF enhanced recording (Linux only, requires privileged mode). Captures command executions in addition to terminal I/O. |
MEZITE_PAM_SERVICE | No | PAM service name for session hooks (Linux only). |
MEZITE_AUTH_H2C | No | Run gRPC auth in h2c (HTTP/2 cleartext) mode. Use when a TLS-terminating load balancer sits in front (Fly.io, ALB, Istio/Linkerd service mesh). Also suitable for local development. |
MEZITE_JOIN_TOKEN=d4f8a2e1-7b3c-4d9e-a5f6-1234567890ab \
MEZITE_AUTH_ADDR=mezite.example.com:3025 \
MEZITE_PROXY_ADDR=mezite.example.com:3024 \
MEZITE_NODE_NAME=web-server-01 \
MEZITE_NODE_LABELS="env=production,role=webserver" \
mezd start Environment Variables
| Variable | Config Equivalent | Description |
|---|---|---|
MEZITE_CLUSTER_NAME | cluster_name | Cluster name |
MEZITE_DB_DRIVER | database.driver | Database backend (sqlite or postgres) |
MEZITE_DB_URL | database.url | Connection URL (file path for SQLite, DSN for PostgreSQL) |
MEZITE_DB_HOST | database.host | PostgreSQL host |
MEZITE_DB_PORT | database.port | PostgreSQL port |
MEZITE_DB_USER | database.user | PostgreSQL user |
MEZITE_DB_PASSWORD | database.password | PostgreSQL password |
MEZITE_DB_NAME | database.name | PostgreSQL database name |
MEZITE_DB_SSLMODE | database.sslmode | PostgreSQL TLS mode |
MEZITE_LOG_LEVEL | log.level | Log verbosity |
MEZITE_LOG_FORMAT | log.format | Log format (json or text) |
MEZITE_CA_KEY_PASSPHRASE | ca_key_passphrase (top-level) | CA private key encryption passphrase |
MEZITE_AUTH_H2C | - | Run gRPC in h2c mode (required behind a TLS-terminating LB).
Process-level env var read directly by mezhub at startup;
not a config-file field. |
MEZITE_GRPC_ALLOW_HTTP | auth.grpc_allow_http | Allow plaintext h2c on the gRPC listener (also enabled by
MEZITE_AUTH_H2C). |
MEZITE_PROXY_PUBLIC_ADDR | proxy.public_addr | Public proxy address. Derives the WebAuthn RP ID/origin and the
GetServerInfo enrollment URL; the OIDC issuer URL is set
separately via MEZITE_OIDC_ISSUER_URL. |
MEZITE_OIDC_ISSUER_URL | proxy.oidc_issuer_url | Public issuer URL surfaced at
/.well-known/openid-configuration. No trailing slash. |
MEZITE_AUDIT_HMAC_KEY | audit_hmac_key (top-level) | Hex-encoded HMAC key for the audit-log tamper-detection chain. |
MEZITE_RECORDING_BACKEND | recording.backend | Recording storage backend: local (default) or s3 |
MEZITE_S3_BUCKET | recording.s3.bucket | S3 bucket for recording storage |
MEZITE_S3_REGION | recording.s3.region | S3 region (default: us-east-1) |
MEZITE_S3_ENDPOINT | recording.s3.endpoint | Custom S3 endpoint (for MinIO or other S3-compatible stores) |
MEZITE_RECORDING_ENC_KEY | recording_enc_key (top-level) | 32-byte hex-encoded AES-256 key for recording encryption at rest |
MEZITE_CLUSTER_NAME=production \
MEZITE_DB_DRIVER=sqlite \
MEZITE_DB_URL=/var/lib/mezite/mezhub.db \
MEZITE_LOG_LEVEL=info \
MEZITE_CA_KEY_PASSPHRASE='another-secret' \
mezhub MEZITE_CLUSTER_NAME=production \
MEZITE_DB_HOST=db.internal.example.com \
MEZITE_DB_PORT=5432 \
MEZITE_DB_USER=mezite \
MEZITE_DB_PASSWORD='hunter2' \
MEZITE_DB_NAME=mezite \
MEZITE_DB_SSLMODE=verify-full \
MEZITE_LOG_LEVEL=info \
MEZITE_LOG_FORMAT=json \
MEZITE_CA_KEY_PASSPHRASE='another-secret' \
mezhub Production Config
SSO connectors are not configured in mezite.yaml — create them
at runtime with mezctl connectors create once the hub is up. See
the SSO Guide.
cluster_name: production
log:
level: info
format: json
database:
driver: postgres
host: db.internal.example.com
port: 5432
user: mezite
# password set via MEZITE_DB_PASSWORD
name: mezite
sslmode: verify-full
# ca_key_passphrase set via MEZITE_CA_KEY_PASSPHRASE
proxy:
public_addr: mezite.example.com:443
listen_addr: 0.0.0.0:3080
ssh_listen_addr: 0.0.0.0:3023
tunnel_listen_addr: 0.0.0.0:3024
oidc_issuer_url: https://mezite.example.com
ssh:
enabled: false # dedicated proxy node, not an SSH target Next Steps
- Quickstart — Apply this configuration in a working setup.
- Architecture — Understand how auth, proxy, and agent components interact.
- SSH Access Guide — Deep dive into SSH certificate authentication and session recording.
- SSO Guide — Configure OIDC or SAML authentication.
- Application Access — Publish internal
web and TCP apps using the
proxy.apps_*settings above.