Hi all,
I built a small self-service VPN portal that runs as a native RouterOS container — tested on an L009UiGS (128 MB flash, 512 MB RAM), where it idles at ~1% CPU. Sharing it here in case it's useful to someone else.
What it does
Instead of creating WireGuard peers by hand for every colleague/guest:
- User opens the portal and signs in with Google or Microsoft (OAuth2/OIDC)
- The container generates the WireGuard keypair (never written to disk)
- It adds the peer on the router via the REST API (with preshared key)
- The user gets a QR code + a "Download .conf" button — scan and connected
- The peer is removed automatically when it expires (TTL configurable: 30m, 8h, 7d...)
The expiry timestamp is stored in the peer comment on the router itself, so the cleanup survives container restarts: no database, no volumes, fully stateless.
Why it fits on a router
Single static Go binary in a FROM-scratch Docker image: ~9 MB for arm/v7, arm64 and amd64. No Python, no Node, no reverse proxy needed.
Other features
- Built-in ACME client: gets and renews a Let's Encrypt certificate by itself (TLS-ALPN-01) — works great with the free cloud DDNS (xxxx.sn.mynetname.net), just dst-nat 443 to the container
- Optional Cloudflare dynamic DNS: keeps an A record of your own domain pointed at your public IP via API token
- Email/domain allowlist, one peer per user, instant revoke button
- Hardened: no-store/CSP headers, Entra tenant (tid) verification against email spoofing, serialized IP allocation
Get it
- Source + step-by-step RouterOS guide (device-mode, veth, envs, mounts): GitHub - markeclaudio/mikrotik-wg-portal: WireGuard self-service portal for MikroTik: Google/Microsoft OAuth, peers via REST API, QR codes, automatic expiration. Ultralight RouterOS container (Go, FROM scratch). · GitHub
- Prebuilt multi-arch image: Package mikrotik-wg-portal · GitHub (RouterOS can pull it directly with registry-url=https://ghcr.io)
- Per-arch tar files for /container add file=... are attached to every CI build on the Actions page
Feedback, issues and PRs welcome. If you try it on other boards (hAP ax², RB5009, CHR...) I'd love to hear how it goes.