A comprehensive web-based management interface for AmneziaWG VPN servers. This service provides an easy-to-use web UI to create, manage, and monitor WireGuard VPN servers with AmneziaWG's advanced obfuscation features. All server configuration is done via web interface or via API endpoints. Providing env variables at docker startup is supported but doesn't make much sense: all settings can be overridden via web interface except for NGINX_PORT.
Tip
For project description in Russian please refer to author blog page.
- Web-based Management: Intuitive UI for managing VPN servers and clients
- AmneziaWG Integration: Full support for AmneziaWG's obfuscation features
- Client Management: Generate and download client configurations
- Real-time Monitoring: Live server status and connection monitoring
- Auto-start: Automatic server startup on container restart
- IPTables Automation: Automatic firewall configuration
- Custom values: MTU and other connection settings can be customized
- QR code: Client can be viewed, copied and downloaded via text, file or QR code
- Config view: Both servers' and clients' configs can be viewed directly from UI
- Auto SSL support: Automatic SSL cert deployment with certbot
Flask Backend (app.py)
- RESTful API for server management
- WebSocket support for real-time updates
- AmneziaWG configuration generation
- Client config management
Vue.js Frontend (static/js/app.js)
- Responsive web interface
- Real-time status updates
- Form validation and error handling
Nginx (config/nginx.conf)
- Reverse proxy for Flask application
- Static file serving
- WebSocket proxy support
Supervisor (config/supervisord.conf)
- Process management
- Automatic service restart
- Log management
/app/web-ui/
βββ app.py # Flask application
βββ templates/
β βββ index.html # Main web interface
βββ static/
βββ js/
β βββ app.js # Frontend JavaScript
βββ css/
βββ style.css # Custom styles
POST /api/servers
Content-Type: application/json
{
"name": "My VPN Server",
"port": 51820,
"subnet": "10.0.0.0/24",
"mtu": 1280,
"obfuscation": true,
"auto_start": true,
"obfuscation_params": {
"Jc": 8,
"Jmin": 8,
"Jmax": 80,
"S1": 50,
"S2": 60,
"H1": 1000,
"H2": 2000,
"H3": 3000,
"H4": 4000,
"MTU": 1280
}
}GET /api/servers
POST /api/servers/{server_id}/start
POST /api/servers/{server_id}/stop
DELETE /api/servers/{server_id}
GET /api/servers/{server_id}/config
GET /api/servers/{server_id}/config/download
GET /api/servers/{server_id}/info
POST /api/servers/{server_id}/clients
Content-Type: application/json
{
"name": "Alice's Phone"
}GET /api/servers/{server_id}/clients
DELETE /api/servers/{server_id}/clients/{client_id}
GET /api/servers/{server_id}/clients/{client_id}/config
GET /api/servers/{server_id}/clients/{client_id}/config-both
GET /api/clients
GET /api/system/status
GET /api/system/refresh-ip
GET /api/system/iptables-test?server_id=wg_abc123
GET /api/config/export
Official docker image repository: https://hub.docker.com/r/alexishw/amneziawg-web-ui
| Variable | Default | Description |
|---|---|---|
NGINX_PORT |
80 |
External port for web interface |
NGINX_USER |
admin |
Username for basic auth in the app |
NGINX_PASSWORD |
changeme |
Password for basic auth in the app |
AUTO_START_SERVERS |
true |
Auto-start servers on container startup |
DEFAULT_MTU |
1280 |
Default MTU value for new servers. Effective only for api requests. For UI management set via UI. |
DEFAULT_SUBNET |
10.0.0.0/24 |
Default subnet for new servers. Effective only for api requests. For UI management set via UI. |
DEFAULT_PORT |
51820 |
Default port for new servers. Effective only for api requests. For UI management set via UI. |
DEFAULT_DNS |
8.8.8.8,1.1.1.1 |
Default DNS servers for clients. Effective only for api requests. For UI management set via UI. |
SSL_EMAIL |
- |
Email used to register Let's encrypt account |
SSL_DOMAIN |
- |
Domain used for SSL cert generation by certbot |
IP_LIST |
- |
A list of IP addresses or IP ranges to allow connections from. |
version: '3.8'
services:
amnezia-web-ui:
image: alexishw/amneziawg-web-ui:master
build: .
container_name: amnezia-web-ui
ports:
- "8080:8080/tcp"
- "51820:51820/udp"
environment:
- NGINX_PORT=8080
- AUTO_START_SERVERS=true
- DEFAULT_MTU=1280
volumes:
- amnezia-data:/etc/amnezia
cap_add:
- NET_ADMIN
- SYS_MODULE
devices:
- /dev/net/tun
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
- net.ipv6.conf.default.forwarding=1
restart: unless-stopped
volumes:
amnezia-data:docker run -d \
--name amnezia-web-ui \
--cap-add=NET_ADMIN \
--cap-add SYS_MODULE \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv4.conf.all.src_valid_mark=1 \
--sysctl net.ipv6.conf.all.disable_ipv6=0 \
--sysctl net.ipv6.conf.all.forwarding=1 \
--sysctl net.ipv6.conf.default.forwarding=1 \
--device /dev/net/tun \
--restart unless-stopped \
-p 80:80 \
-p 9090:9090 \
-p 51821:51821/udp \
-e NGINX_PORT=9090 \
-e NGINX_PASSWORD=1234
-e AUTO_START_SERVERS=false \
-e DEFAULT_MTU=1420 \
-e DEFAULT_SUBNET=10.8.0.0/24 \
-e DEFAULT_PORT=51821 \
-e DEFAULT_DNS="8.8.8.8,8.8.4.4" \
-e SSL_EMAIL="your@email.com" \
-e SSL_DOMAIN="your.domain.com" \
-v amnezia-data:/etc/amnezia \
-v ssl:/etc/letsencrypt \
alexishw/amneziawg-web-ui:masterWhen SSL_EMAIL and SSL_DOMAIN are specified, certbot requests certificate for the domain provided at docker container run.
For this purpose you additionally need to expose port 80. It provides access only to the /.well-known endpoint, so there is no additional security risk introduced.
You can't use a single port 80 both for the application and for the SSL deployment: if you wish to use ssl, set the custom port for the app.
You can check if the certificate was issued by checking docker logs at the start or by viewing /var/log/letsencrypt/letsencrypt.log inside the container.
Cronjob to renew a certificate is also automatically created that runs every Tuesday.
If you wish to keep the issued certificate during container recreation, you need to mount container folder to a host:
/etc/letsencrypt or /etc/letsencrypt/live.
Note
If you enable SSL support and want to have healthcheck continue working properly, you need to provide a https healthcheck URL at docker container start:
--health-cmd='curl -f https://localhost:$NGINX_PORT/status || exit 1'
You can protect your webserver by limiting connections to the it through the list of IP address(es) and IP range(s). To enable it you need to provide env variable IP_LIST at docker container run, e.g.
-e IP_LIST="100.200.101.201, 50.100.10.0/24"
Note
Take a note that in case you enable SSL for your server, you have to allow Let's Encrypt IP ranges. Here is a known CIDR used by Let's Encrypt, though is may vary country by country:
23.178.112.0/24
AmneziaWG supports advanced traffic obfuscation to bypass censorship and DPI (Deep Packet Inspection).
| Parameter | Range | Default | Recommended | Description |
|---|---|---|---|---|
Jc |
1-128 | 8 | 4-12 | Controls connection pattern frequency |
Jmin |
1-1279 | 8 | 8 | Minimum padding size for packets |
Jmax |
Jmin+1 to 1280 | 80 | 80 | Maximum padding size for packets |
S1 |
1-1132 | 50 | 15-150 | Obfuscation pattern parameter 1 |
S2 |
1-1188 | 60 | 15-150 | Obfuscation pattern parameter 2 |
H1 |
5-2147483647 | 1000 | Unique | Header obfuscation parameter 1 |
H2 |
5-2147483647 | 2000 | Unique | Header obfuscation parameter 2 |
H3 |
5-2147483647 | 3000 | Unique | Header obfuscation parameter 3 |
H4 |
5-2147483647 | 4000 | Unique | Header obfuscation parameter 4 |
MTU |
1280-1440 | 1280 | 1280-1420 | Maximum Transmission Unit |
- Purpose: Controls how frequently connection patterns are applied
- Lower values: More frequent pattern application (more obfuscation, lower performance)
- Higher values: Less frequent pattern application (less obfuscation, better performance)
- Recommended: 4-12 for optimal balance
- Jmin: Minimum random padding added to each packet
- Jmax: Maximum random padding added to each packet
- Relationship: Jmax must be greater than Jmin
- Note: Values are constrained by MTU (typically 1280 for basic internet)
- Purpose: Define obfuscation patterns for traffic shaping
- Constraints:
- S1 β€ 1132 (1280 - 148 = 1132)
- S2 β€ 1188 (1280 - 92 = 1188)
- S1 + 56 β S2 (must be different with margin)
- Recommended: 15-150 for effective obfuscation
- Purpose: Unique identifiers for header obfuscation
- Requirement: All four values must be unique
- Recommended: Use random values in range 1000-1000000
- Purpose: Defines maximum packet size
- Standard Internet: 1280 (safe for all connections)
- Better Performance: 1420-1440 (may have compatibility issues)
- Trade-off: Higher MTU = better performance but potential fragmentation
docker exec amnezia-web-ui tail -f /var/log/web-ui/access.log
docker exec amnezia-web-ui tail -f /var/log/web-ui/error.log
docker exec amnezia-web-ui tail -f /var/log/nginx/access.log
docker exec amnezia-web-ui tail -f /var/log/nginx/error.log
docker exec amnezia-web-ui tail -f /var/log/supervisor/supervisord.log
Export Configuration
curl http://localhost/api/config/export > amnezia_backup.json
docker cp amnezia-web-ui:/etc/amnezia ./amnezia-backup/
curl http://localhost/api/system/status
curl "http://localhost/api/system/iptables-test?server_id=wg_abc123"
The app is exposed directly on 80 or custom port with basic authentication.
Important
I strongly recommend protecting endpoints with firewall and/or nginx authentication. Basic auth alone is not strong enough and can be bruteforced.
By default, docker image is built with user admin and password changeme. To change the default behavior you need to provide with docker envs NGINX_USER and NGINX_PASSWORD.
I recommend enabling IP limitation together with other protection methods (see section Protection by IP address above).
The NO support provided as well as no regular updates are planned. Found issues can be fixed if free time permits.
From Russia with Lβ€οΈVE
