Skip to content

Configuration

The operator reads its configuration from a TOML file, environment variables, and CLI flags. This page documents every configuration field, its type, default value, and behavior.

Configuration is merged using Figment with the following precedence (highest wins):

  1. CLI flags--bind, --wg-port, etc.
  2. Environment variables — prefixed with AIRDRESS_
  3. Config file — TOML at the path given by --config or AIRDRESS_CONFIG
  4. Compiled defaults — built into the binary

The default config file path is /etc/airdress/operator.toml. Override it with:

Terminal window
airdress-operator -c ./my-config.toml serve

Validate the resolved configuration at any time:

Terminal window
airdress-operator config # print merged result
airdress-operator config --check # validate without printing (exit 0 = OK)

This example shows every section with representative values. Fields set to their defaults are commented.

# Top-level settings
# bind = "127.0.0.1:8080" # HTTP listener address
# wg_port = 51820 # WireGuard UDP port
# name = "airdress-operator" # Instance name (appears in logs)
[dns]
enabled = true
# bind_udp = "0.0.0.0:53"
# bind_tcp = "0.0.0.0:53"
zone = "humans.airdress.co"
[dns.soa]
mname = "ns1.airdress.co"
rname = "noc.airdress.co"
[dns.cache]
# max_entries = 10000
# default_ttl = 60
[dns.ratelimit]
# queries_per_second = 1000
# burst = 200
[[dns.records]]
name = "myapp.humans.airdress.co"
type = "A"
rdata = "192.168.1.10"
ttl = 300
[[dns.records]]
name = "api.humans.airdress.co"
type = "A"
rdata = "192.168.1.11"
# ttl = 60
[auth]
issuer = "https://auth.airdress.co"
jwks_uri = "https://auth.airdress.co/.well-known/jwks.json"
audience = "airdress-operator"
[database]
# url = "postgresql://user:pass@localhost:5432/airdress" # omit for embedded
# data_dir = "./var/postgres"
# version = "=18.3.0"
# shared_buffers_mb = 64
# max_connections = 32
# pool_max_size = 16
# pool_acquire_timeout_secs = 5
[enrollment]
bootstrap_token = "change-me-on-first-run"
# operator_base_url = "http://127.0.0.1:8080"
[logging]
# level = "info"
# format = "pretty" # pretty | compact | json
# target = "stderr"
# debug_file = "/var/log/airdress/debug.log"
# service_name = "airdress-operator"
# service_version = "0.x.y"
# deployment_environment = "production"
[proxy]
enabled = true
# bind_http = "0.0.0.0:8081"
# via_token = "secret-proxy-token"
# rate_limit_rps = 100
# rate_limit_burst = 20
[proxy.health]
# enabled = true
# interval_secs = 5
# timeout_secs = 1
# unhealthy_threshold = 3
# healthy_threshold = 2
[proxy.circuit_breaker]
# enabled = true
# failure_threshold = 5
# success_threshold = 1
# cool_down_secs = 10
[chat]
enabled = true
# store = "postgres" # memory | postgres
[ingress]
enabled = true
# buffer_size = 100
# metrics_interval_secs = 30
[[ingress.endpoints]]
slug = "github-webhook"
url = "http://localhost:3000/webhooks/github"
[apps]
enabled = true
# apps_dirs = ["/etc/airdress/apps"]
# workdir = "./var/apps"
# zone = "humans.airdress.co"
[miniapps]
enabled = true
# zone = "humans.airdress.co"
# bundle_root = "./var/bundles"
# max_bundle_size = 52428800
# render_cache_assets = true
# render_cache_ttl_secs = 3600
# miniapps_dirs = ["/etc/airdress/miniapps"]
[plugin_db.secrets]
# key_file = "/etc/airdress/secrets.key"
# Or set AIRDRESS_SECRETS_KEY env var with base64-encoded key
FieldTypeDefaultDescription
bindstring"127.0.0.1:8080"HTTP listener address and port
wg_portinteger51820WireGuard UDP port
namestring"airdress-operator"Instance name, included in log output

Built-in authoritative DNS server. When enabled, the operator answers DNS queries for records defined in the config or added at runtime via airdress-operator dns add.

FieldTypeDefaultDescription
enabledbooleanfalseEnable the DNS server
bind_udpstring"0.0.0.0:53"UDP listener address
bind_tcpstring"0.0.0.0:53"TCP listener address
zonestring"humans.airdress.co"Zone name for served records

SOA record fields for the zone.

FieldTypeDefaultDescription
mnamestringPrimary nameserver hostname
rnamestringHostmaster email (dot-encoded)

In-memory record cache.

FieldTypeDefaultDescription
max_entriesinteger10000Maximum cached entries
default_ttlinteger60Default TTL in seconds for records without an explicit TTL

Response Rate Limiting to mitigate DNS amplification.

FieldTypeDefaultDescription
queries_per_secondinteger1000Sustained query rate per source IP
burstinteger200Burst allowance above the sustained rate

Static DNS records loaded at startup. Each entry is a table in the TOML array.

FieldTypeDefaultDescription
namestringFully qualified record name
typestringRecord type: A, AAAA, CNAME, TXT, MX, SRV
rdatastringRecord data
ttlinteger60Time to live in seconds

OIDC-based authentication for the operator API.

FieldTypeDefaultDescription
issuerstringOIDC issuer URL
jwks_uristringJWKS endpoint for token verification
audiencestringExpected JWT aud claim

PostgreSQL connection and tuning. When url is omitted, the operator runs an embedded PostgreSQL child process using the settings below. When url is set, the operator connects to the external database and ignores data_dir, version, shared_buffers_mb, and max_connections.

FieldTypeDefaultDescription
urlstring(none)External PostgreSQL connection URL. Omit for embedded mode.
data_dirstring"./var/postgres"Data directory for embedded PostgreSQL
versionstring"=18.3.0"Required PostgreSQL version (embedded mode)
shared_buffers_mbinteger64Shared buffer size in MB (embedded mode)
max_connectionsinteger32Max connections (embedded mode)
pool_max_sizeinteger16Connection pool maximum size
pool_acquire_timeout_secsinteger5Timeout in seconds when acquiring a pool connection

The operator supports two database modes:

Embedded (default) — when database.url is absent, the operator downloads and manages a PostgreSQL child process. Data is stored in data_dir. This is the simplest setup for single-node deployments and local development.

External — when database.url is set (e.g. postgresql://user:pass@db.example.com:5432/airdress), the operator connects via sqlx::PgPool. Use this for production deployments where you want a managed database, replication, or shared state between operator instances.

# Embedded (just omit url)
[database]
data_dir = "/var/lib/airdress/postgres"
# External
[database]
url = "postgresql://airdress:secret@db.internal:5432/airdress"
pool_max_size = 32

The AIRDRESS_DATABASE_URL environment variable is the most common way to provide the connection string without writing it to the config file.

Device enrollment settings.

FieldTypeDefaultDescription
bootstrap_tokenstringSecret token for the first device enrollment
operator_base_urlstringBase URL the operator advertises to enrolling devices

Structured logging configuration.

FieldTypeDefaultDescription
levelstring"info"Log level: trace, debug, info, warn, error
formatstring"pretty"Output format: pretty, compact, or json
targetstring"stderr"Log target
debug_filestring(none)Path to an additional debug log file
service_namestring"airdress-operator"Service name in structured log fields
service_versionstringVersion string in structured log fields
deployment_environmentstringEnvironment tag (e.g. production, staging)

Reverse proxy for forwarding traffic to local services.

FieldTypeDefaultDescription
enabledbooleanfalseEnable the reverse proxy
bind_httpstringHTTP listener for proxied traffic
via_tokenstringToken for the Via header (upstream authentication)
rate_limit_rpsinteger100Requests per second per client
rate_limit_burstinteger20Burst allowance above the sustained rate

Health checking for proxy backends.

FieldTypeDefaultDescription
enabledbooleantrueEnable backend health checks
interval_secsinteger5Seconds between health probes
timeout_secsinteger1Probe timeout in seconds
unhealthy_thresholdinteger3Consecutive failures to mark unhealthy
healthy_thresholdinteger2Consecutive successes to mark healthy

Circuit breaker for proxy backends. When a backend exceeds the failure threshold, the circuit opens and requests are rejected immediately until the cool-down period expires.

FieldTypeDefaultDescription
enabledbooleantrueEnable the circuit breaker
failure_thresholdinteger5Failures before the circuit opens
success_thresholdinteger1Successes during half-open to close the circuit
cool_down_secsinteger10Seconds before retrying after the circuit opens

Chat subsystem.

FieldTypeDefaultDescription
enabledbooleanfalseEnable the chat subsystem
storestring"memory"Message store backend: memory or postgres

The chat section also supports limits, sse, and federation subsections for advanced tuning. Federation is feature-gated and may not be available in all builds.

Inbound event endpoints (webhooks, callbacks).

FieldTypeDefaultDescription
enabledbooleanfalseEnable inbound event processing
buffer_sizeinteger100In-memory event buffer size
metrics_interval_secsinteger30Interval for emitting ingress metrics

Each entry defines a named inbound endpoint that receives events and forwards them to a local URL.

FieldTypeDefaultDescription
slugstringURL-safe endpoint identifier
urlstringLocal URL to forward events to

Mini-app runtime.

FieldTypeDefaultDescription
enabledbooleanfalseEnable the app runtime
apps_dirsstring[]Directories to scan for app definitions
workdirstring"./var/apps"Working directory for app data
zonestringDNS zone for app subdomains

Mini-app serving and registry.

FieldTypeDefaultDescription
enabledbooleanfalseEnable mini-app serving
zonestringDNS zone for mini-app subdomains
bundle_rootstring"./var/bundles"Directory for downloaded bundles
max_bundle_sizeinteger52428800Maximum bundle size in bytes (default 50 MB)
render_cache_assetsbooleantrueCache rendered assets
render_cache_ttl_secsinteger3600Asset cache TTL in seconds
miniapps_dirsstring[]Directories to scan for mini-app definitions

The miniapps section also supports registry, tokens, capability, and upgrade subsections for registry authentication, capability grants, and upgrade policies.

Encrypted storage for plugin secrets.

FieldTypeDefaultDescription
key_filestring(none)Path to a file containing the base64-encoded 32-byte master key

Alternatively, set the AIRDRESS_SECRETS_KEY environment variable with the base64-encoded key directly. Generate a key with airdress-operator plugin-db init-key.

Config fields map to environment variables with the AIRDRESS_ prefix. Nested sections use double underscores as separators.

Config pathEnvironment variable
bindAIRDRESS_BIND
wg_portAIRDRESS_WG_PORT
database.urlAIRDRESS_DATABASE_URL
auth.issuerAIRDRESS_AUTH__ISSUER
auth.jwks_uriAIRDRESS_AUTH__JWKS_URI
auth.audienceAIRDRESS_AUTH__AUDIENCE
enrollment.bootstrap_tokenAIRDRESS_ENROLLMENT__BOOTSTRAP_TOKEN
logging.levelAIRDRESS_LOG
chat.federation.keystore_passphraseAIRDRESS_CHAT__FEDERATION__KEYSTORE_PASSPHRASE
plugin_db.secrets (key value)AIRDRESS_SECRETS_KEY