Imported from sers88/wg-gateway (
AGENTS.md). Install upstream withnpx skills add sers88/wg-gateway. Copyright stays with the author.
AGENTS.md
Project overview
Single Docker image that bundles wg-easy v15.4.0 (WireGuard management), Mihomo v1.19.30 (proxy/routing engine), and metacubexd v1.273.0 (Mihomo UI). No application code — the repo is shell scripts, configs, and a Dockerfile that assembles third-party components.
Build and run
# Build locally
docker compose build
# Run (requires .env with WG_HOST set)
cp .env.example .env && docker compose up -d
No tests, lint, or typecheck exist. The only validation is whether the Docker image builds and the services start.
Directory layout
Dockerfile— multi-stage build: copies wg-easy from upstream image, downloads Mihomo binary + metacubexd UIscripts/— entrypoint and long-running service scripts run by supervisordconfig/supervisord.conf— process manager: starts mihomo → wg-easy → setup-routing daemonconfig/mihomo/config.yaml— default Mihomo config seeded to/data/mihomo/config.yamlon first starttemplates/wg-gateway.xml— Unraid Community Applications template
Key architecture facts
- Mihomo runs with
auto-route: false— routing is managed byscripts/setup-routing.sh, which runs as a persistent daemon under supervisord. This script handles policy routing so only WireGuard client traffic goes through the TUN, not all host traffic. - Policy routing uses table 666, ip rule priority 200. Traffic from the WireGuard subnet is matched and directed to that table, which has a default route through Mihomo's TUN device.
- wg-easy v15 entrypoint — upstream switched to a Nuxt/Nitro compiled app. Entrypoint is
dumb-init node server/index.mjsfrom/app. Thelibsqlnative module is re-installed for Debian/glibc because the upstream image is Alpine/musl-based. - wg-easy v15 uses INIT_ env vars for unattended setup* — on first start the container auto-configures the admin user, WG host/port, DNS and Allowed IPs via
INIT_ENABLED=trueso the Web UI wizard is skipped. - rp_filter must be 0 (not 2) — WG↔TUN traffic has entirely asymmetric paths, so even loose mode (
2) drops packets. - Both iptables backends are handled — Unraid Docker uses
iptables-legacywith FORWARD DROP policy; other systems useiptables-nft. All iptables commands in scripts target both. - metacubexd UI auto-sync — metacubexd assets are bundled at
/opt/metacubexdin the image with a.versionfile. On every container start, the entrypoint reads theexternal-uipath from the user's Mihomo config and syncs the bundled assets there if the version changed. This ensures UI updates are applied automatically when the Docker image is updated, regardless of the user's configuredexternal-uipath.
CI
.github/workflows/docker.yml— builds on push tomain(latest+ timestamp) andfeature/arm64-support(snapshot+snapshot-<sha>). Smoke tests run on bothlinux/amd64andlinux/arm64native runners via matrix strategy..github/workflows/release.yml— onv*tags, builds and pushes semver tags +latest, creates GitHub Release with auto-generated notes- Platforms:
linux/amd64,linux/arm64 - Image:
ksantd/wg-gatewayon Docker Hub,ghcr.io/sers88/wg-gatewayon GHCR
Editing scripts
All scripts in scripts/ are bash and must remain compatible with Debian bookworm-slim. Notable constraint: ip rule replace is not available on Debian bookworm's iproute2 — use ip rule add with existence checks instead (see setup-routing.sh).
Updating Mihomo version
When bumping MIHOMO_VERSION in Dockerfile, the MIHOMO_SHA256_* values must be updated too.
Important: SHA256 must be computed on the uncompressed binary, not the .gz archive. The sha256sum -c check in the Dockerfile runs after gunzip, so it validates the decompressed file.
# Correct: download .gz, decompress, then hash the binary
wget https://github.com/MetaCubeX/mihomo/releases/download/v1.19.30/mihomo-linux-amd64-v1.19.30.gz
gunzip mihomo-linux-amd64-v1.19.30.gz
sha256sum mihomo-linux-amd64
Repeat for both linux-amd64 and linux-arm64 variants.
Mihomo config changes
After editing /data/mihomo/config.yaml, reload without restarting the whole container:
docker exec wg-gateway supervisorctl restart mihomo
The routing daemon automatically re-applies policy routes when the TUN device is recreated.