Skip to content

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.

# Set automatically during pairing
device_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 directory
data_dir: /var/lib/atreoagent
# Optional public-IP override; leave empty to auto-detect
endpoint_ip: ""
endpoint_port: 0
# WireGuard tunnel settings
wireguard:
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 settings
proxy:
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 settings
certs:
email: you@example.com
cert_dir: /var/lib/atreoagent/certs
# Notification API
notify:
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"

The full hostname under which apps are served for this device. Populated by atreoLINK at pair time. Examples:

  • alice.atreo.link for the SaaS default subdomain.
  • home.example.com for 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 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.

FieldDefaultDescription
listen_port51820UDP port WireGuard listens on.
firewall_enabledtrueConfine 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_enabledtrueMaster 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_enabledtrueTry 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_enabledtrueOpen 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.

FieldDefaultDescription
enabledtrueUse 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.
forcefalseKeep 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.

FieldDefaultDescription
enabledtrueRun the built-in HTTPS reverse proxy. Set to false if using your own (Caddy / Traefik / nginx).
http_port80HTTP port (used for the 80 to 443 redirect).
https_port443HTTPS port for the reverse proxy.
auth_port9091Forward-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.
FieldDefaultDescription
email(required)Email used for Let’s Encrypt registration.
cert_dir/var/lib/atreoagent/certsWhere TLS certificates and the ACME account key are stored.
FieldDefaultDescription
port9876Port 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.

FieldDefaultDescription
enabledfalseEnable the built-in SMTP-to-push gateway.
listen0.0.0.0:2525Bind 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_bytes1048576Max accepted message size (1 MiB).
rate_per_minute50Per-source-IP rate limit.
trusted_networks127.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_enabledfalseAdvertise 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.

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 key

Each 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.

Resolution order, later wins:

  1. Built-in defaults.
  2. Values in config.yaml (if the file exists).
  3. 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.

WireGuard is a registered trademark of Jason A. Donenfeld.