Production recommendations¶
Basic security¶
SECRET_KEY: set a long, random value explicitly. Without it, a key is generated automatically and stored in/var/lib/wireguard-ui/secret_key— this works, but any reinstall or loss of the data volume invalidates all sessions, and the key cannot be shared across multiple replicas.
- Admin credentials: change the automatically generated password on first login (Profile page). Consider setting
ADMIN_USERNAME/ADMIN_EMAILbefore the very first startup if you don't want the default values (admin/admin@wg.ui). SWAGGER_ENABLED: leave atfalse(default) in production if exposing the OpenAPI documentation is not needed.
Behind a TLS reverse proxy¶
If you expose the interface via Traefik, Nginx, Caddy, etc.:
- Terminate TLS at the reverse proxy level.
- Set
TRUSTED_PROXIESto the proxy's IP or subnet (e.g.172.16.0.0/12for a default Docker network). Without this: - the
access_tokensession cookie will not be markedSecure(HTTPS not detected); - rate limiting will group all visitors under the proxy's IP, which can lock out everyone after a few requests.
- The reverse proxy must forward the
X-Forwarded-ForandX-Forwarded-Protoheaders.
See the details in Authentication & security and Environment variables.
Rate limiting¶
Enabled by default (RATE_LIMIT_ENABLED=true). Adjust RATE_LIMIT_MAX_REQUESTS/RATE_LIMIT_WINDOW_SECONDS to your normal traffic, and keep RATE_LIMIT_AUTH_MAX_REQUESTS low to limit brute-force attempts on /api/auth/login.
Single-worker deployment
The rate-limiter and the WireGuard service state are managed in memory, per process. The image must not be scaled horizontally (multiple containers running simultaneously) without revisiting this architecture — a single wireguard-ui container must manage a given wg0 interface.
Backups¶
Two volumes to back up regularly:
| Volume | Content | Criticality |
|---|---|---|
/var/lib/wireguard-ui |
SQLite database (users, clients, settings, audit, PAT), generated secret key | High — loss = loss of all application data |
/etc/wireguard |
wg0.conf, active server/client keys |
High — loss = manual reconstruction of tunnels |
Database in production¶
SQLite is suitable for a single-instance deployment. For a more demanding environment (centralized backups, replication), point DB_PATH to a PostgreSQL instance:
The URL is automatically converted to the async asyncpg driver — see config.py.
Audit and compliance¶
AUDIT_RETENTION_DAYS(default 90) andAUDIT_MAX_EVENTS(default 10,000) control automatic purging of the audit log. Increase these values if a compliance policy requires longer retention.- The log is accessible via
GET /api/audit(Audit page, admin only) — consider exporting it periodically if the configured retention is short.
Emails¶
Configure SMTP from the admin interface (SMTP page), not via the environment (only MAIL_FROM/MAIL_NAME are environment variables, used as a fallback). Test the configuration with the Send test email button before relying on it for client enrollment.
Pre-production checklist¶
- [ ]
SECRET_KEYexplicitly set and backed up. - [ ] Admin password changed.
- [ ]
TRUSTED_PROXIESset if a reverse proxy is used. - [ ] TLS enabled at the reverse proxy level.
- [ ]
wg_configandwireguard-ui_datavolumes backed up automatically. - [ ]
SWAGGER_ENABLED=falseunless explicitly needed. - [ ] SMTP configured and tested if sending configuration by email is used.
- [ ] OIDC configured if SSO login is required (see Authentication & security).