Config file
atreoAGENT keeps its configuration in a YAML file at /var/lib/atreoagent/config.yaml. Most fields populate themselves: at install time the file doesn’t exist, and pairing fills in the device-specific values. You only edit it for networking, proxy, or feature toggles.
Full reference
Section titled “Full reference”# Set automatically during pairingdevice_token: ""device_id: ""
# Hostname used for app subdomains. Set automatically at pair time# (e.g. "alice.atreo.link", or your custom domain once attached).# The TLS cert is *.<apps_hostname>.apps_hostname: alice.atreo.link
# Log verbosity: debug | info | warn | error (default: info)log_level: info
# Persistent data directorydata_dir: /var/lib/atreoagent
# Optional public-IP override; leave empty to auto-detectendpoint_ip: ""endpoint_port: 0
# WireGuard tunnel settingswireguard: listen_port: 51820 firewall_enabled: true upnp_enabled: true # master switch for auto port-opening; a manual port forward works too pcp_enabled: true # try PCP (RFC 6887) first, for both IPv4 and IPv6 ipv6_pinhole_enabled: true # open an IPv6 firewall pinhole for the WireGuard port
# CGNAT relay fallback (on by default; used only when there's no public# inbound path). Set enabled false to opt out; force keeps a relay session up# even when a direct path exists.relay: enabled: true force: false
# Reverse proxy settingsproxy: enabled: true http_port: 80 https_port: 443 auth_port: 9091 trusted_networks: [] # empty by default; opt in to specific CIDRs trusted_proxies: [] # external reverse proxies allowed to set X-Forwarded-*
# TLS certificate settingscerts: email: you@example.com cert_dir: /var/lib/atreoagent/certs
# Notification APInotify: port: 9876
# Built-in SMTP server (off by default; LAN-only)smtp: enabled: false listen: "0.0.0.0:2525" max_message_bytes: 1048576 rate_per_minute: 50 tls_enabled: false # advertise STARTTLS via a self-signed cert (TLS 1.3) catch_all: "" # unmatched RCPT TO -> this member's email; empty = reject (default) # Source-IP allowlist. Unset = loopback + RFC1918 + link-local + ULA + the # WireGuard tunnel subnet. Connections from outside the list are rejected # before AUTH. Example to widen to one extra LAN: # trusted_networks: # - "192.168.50.0/24"Key sections
Section titled “Key sections”apps_hostname
Section titled “apps_hostname”The full hostname under which apps are served for this device. Populated by atreoLINK at pair time. Examples:
alice.atreo.linkfor the SaaS default subdomain.home.example.comfor a custom domain.
The agent requests a wildcard TLS certificate for *.<apps_hostname> and uses this value as the suffix it strips when extracting the app slug from incoming hostnames.
log_level
Section titled “log_level”Log verbosity. Accepts debug, info, warn (or warning), and error, case-insensitive. Defaults to info. An unrecognised value causes the agent to refuse to start. Overridden by the LOG_LEVEL environment variable.
wireguard
Section titled “wireguard”| Field | Default | Description |
|---|---|---|
listen_port | 51820 | UDP port WireGuard listens on. |
firewall_enabled | true | Confine tunnel peers to the proxy ports, plus the raw host ports each member is granted via exposed-port apps — a member only reaches the ports they’ve been given. Fails closed: if iptables/ip6tables is unavailable or the rules can’t be applied, the agent refuses to start rather than leaving peers unconfined. Set false only if you genuinely can’t run iptables — this lets every host port bound to 0.0.0.0 (or ::) be reached by every paired peer (and bypasses the per-app ACL); it’s discouraged. |
upnp_enabled | true | Master switch for automatic inbound-port opening. Opens the WireGuard UDP port via PCP, NAT-PMP, or UPnP — and, for IPv6, a firewall pinhole. Set false to disable all of it (none of PCP/NAT-PMP/UPnP/IPv6 pinhole runs); then forward listen_port/UDP manually instead. Overridden by WG_UPNP_ENABLED and the --no-upnp flag. |
pcp_enabled | true | Try PCP (Port Control Protocol, RFC 6887) first when opening the port, for both IPv4 and IPv6, before falling back to NAT-PMP/UPnP. Set false to skip PCP. Overridden by WG_PCP_ENABLED. |
ipv6_pinhole_enabled | true | Open an IPv6 firewall pinhole for the WireGuard port on the gateway (via PCP or UPnP IGDv2), so the published AAAA endpoint is reachable. Set false to manage the gateway firewall yourself. Overridden by WG_IPV6_PINHOLE_ENABLED. |
The WireGuard interface is always called wg-atreo (hardcoded). The tunnel overlay is dual-stack with fixed addressing — IPv4 100.64.0.1/100.64.0.0/24 and IPv6 fd00:64::1/fd00:64::/64.
| Field | Default | Description |
|---|---|---|
enabled | true | Use the CGNAT relay as a fallback when the agent has no reachable public inbound path. It’s automatic and only kicks in when no direct path exists — direct paths always win. Set false to opt this agent out of relaying entirely (off-LAN clients will then be unreachable when there’s no direct path). Overridden by RELAY_ENABLED. |
force | false | Keep a relay session up even when a direct path exists, so a relay endpoint is always available to offer to clients (clients still prefer LAN/direct). Useful for offering a relay config alongside a direct one; not needed for normal use. Overridden by RELAY_FORCE. |
Setting endpoint_ip also keeps the agent off the relay: it tells the agent it already has a reachable public address, so it advertises that direct endpoint instead.
| Field | Default | Description |
|---|---|---|
enabled | true | Run the built-in HTTPS reverse proxy. Set to false if using your own (Caddy / Traefik / nginx). |
http_port | 80 | HTTP port (used for the 80 to 443 redirect). |
https_port | 443 | HTTPS port for the reverse proxy. |
auth_port | 9091 | Forward-auth endpoint port. |
trusted_networks | (empty) | CIDRs that bypass per-app ACL checks. Empty by default — no networks are trusted until you opt in. See Trusted networks. |
trusted_proxies | (empty) | CIDRs of external reverse proxies allowed to set X-Forwarded-For / X-Forwarded-Host on forward-auth requests. Required when using Caddy / Traefik / nginx. |
| Field | Default | Description |
|---|---|---|
email | (required) | Email used for Let’s Encrypt registration. |
cert_dir | /var/lib/atreoagent/certs | Where TLS certificates and the ACME account key are stored. |
notify
Section titled “notify”| Field | Default | Description |
|---|---|---|
port | 9876 | Port for the local notification API. Binds on every interface so self-hosted apps in their own containers can reach it. Gated by a bearer-token API key. |
See Notification API → Authentication for the bearer-auth key.
| Field | Default | Description |
|---|---|---|
enabled | false | Enable the built-in SMTP-to-push gateway. |
listen | 0.0.0.0:2525 | Bind address for the SMTP server. The default binds all interfaces; the trusted_networks allowlist — not the bind address — is what keeps the gateway LAN-only. |
max_message_bytes | 1048576 | Max accepted message size (1 MiB). |
rate_per_minute | 50 | Per-source-IP rate limit. |
trusted_networks | 127.0.0.0/8, ::1/128, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, fe80::/10, fc00::/7, plus the WireGuard tunnel subnet (100.64.0.0/24; the IPv6 overlay fd00:64::/64 falls under fc00::/7) | Source-IP allowlist. Connections from an IP outside the list are rejected before AUTH. Widening it to public IP space logs a warning at startup. Overridden by SMTP_TRUSTED_NETWORKS. See Source-IP allowlist. |
tls_enabled | false | Advertise STARTTLS using a self-signed cert generated under the data dir. STARTTLS requires TLS 1.3. Required for apps that refuse plaintext AUTH (e.g. Grafana). See STARTTLS. |
catch_all | (empty) | Fallback recipient for mail whose RCPT TO matches no member. Must be an existing member’s atreoLINK email — a push can only be sealed to a member — otherwise unknown mail is still rejected and a warning is logged. Empty (default) rejects unknown recipients with 550. Overridden by SMTP_CATCH_ALL. See Catch-all recipient. |
See Built-in SMTP server for the routing model and a critical warning about exposing port 2525.
Persistent data layout
Section titled “Persistent data layout”The agent stores all persistent state under data_dir:
/var/lib/atreoagent/├── config.yaml # Agent configuration├── admin_pin.json # Pinned owner identity (set at pair time)├── acl.json # Cached ACL from atreoLINK├── mobile_devices.json # Paired mobile devices and browsers├── ip_allocations.json # WireGuard tunnel IP assignments├── notify_api_key # Bearer key for the notification API├── custom_domain.json # Custom domain state (if attached)├── cert_renewal_state.json # Per-suffix renewal-failure tracking├── keys/│ ├── ed25519.key # Device identity (private)│ ├── ed25519.pub # Device identity (public)│ ├── wg_private.key # WireGuard private key│ ├── wg_public.key # WireGuard public key│ └── acme_account.key # ACME account private key└── certs/ └── <suffix>/ ├── registration.json # ACME registration metadata ├── fullchain.pem # TLS certificate (full chain) └── privkey.pem # TLS private keyEach served TLS suffix has its own subdirectory under certs/. The legacy <yourname>.atreo.link suffix and any active custom domain are stored side by side.
Precedence
Section titled “Precedence”Resolution order, later wins:
- Built-in defaults.
- Values in
config.yaml(if the file exists). - Environment variables.
config.yaml remains the source of pairing-derived data (device_token, device_id, apps_hostname). Environment variables are for tunables, not pairing state.
See also
Section titled “See also”- Environment variables for the same fields settable from the Docker environment.
- Pair your server for what’s actually written into this file at pair time.
© 2026 atreoLABS. All rights reserved.
WireGuard is a registered trademark of Jason A. Donenfeld.

