- README: new section explaining WG_MEM_LIMIT / WG_MEMSWAP_LIMIT with per-RAM-tier values and host swap configuration for SBC boards - CLAUDE.md: simplify resource limits table, drop device-specific measurements, reference README for per-board guidance - .env.example: update comments with per-tier values and OOM warning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
A Docker Compose setup for a self-hosted WireGuard VPN server using wg-easy, designed to run on a Raspberry Pi or any Linux server. The web UI (wg-easy) handles client management, QR code generation, and key lifecycle.
Common commands
# First-time setup
cp .env.example .env
# Edit .env: set TZ, optionally WG_PORT / WG_UI_PORT
# Start the VPN
docker compose up -d
# Update wg-easy to the latest image
docker compose pull && docker compose up -d
# View logs
docker compose logs -f wg-easy
Configuration
.env (not committed) controls only infrastructure-level settings:
| Variable | Description |
|---|---|
TZ |
IANA timezone (e.g. Europe/Rome) |
WG_PORT |
UDP VPN port (default 51820) |
WG_UI_PORT |
Web UI port (default 51821) |
v15+: Host, password, and DNS are configured through the web UI wizard on first launch — not via environment variables.
Important constraints
wg-data/is auto-generated by the container on first start and holds live WireGuard keys (wg0.conf,wg-easy.db). Never commit it. Permissions are set to700automatically bywg-init..envis gitignored.- The container requires
NET_ADMINandSYS_MODULEcapabilities plusnet.ipv4.ip_forward=1sysctl — these are already set indocker-compose.yml. - The router must forward UDP port 51820 (or
WG_PORT) to the server's local IP. INSECURE=trueis set indocker-compose.ymlto allow HTTP access on the local network.
SBC / resource-constrained devices
Works on any Linux host with a 64-bit kernel and WireGuard support. Tested architectures:
| Hardware | Arch | Notes |
|---|---|---|
| Raspberry Pi 5 | arm64 | kernel 6.12.x+rpt-rpi-2712 |
| Raspberry Pi 4 / 3B+ | arm64 / armv7 | 32-bit kernels need armv7 image |
| Orange Pi, Rock Pi, Banana Pi | arm64 | depends on board BSP kernel |
| Generic x86_64 server / VM | amd64 | standard Debian/Ubuntu/Fedora |
| Intel NUC / mini-PC | amd64 | same as above |
Known issue — ip6tables modules not loaded at boot.
Affects mostly SBC boards with custom BSP kernels, but can occur on any host where ip6_tables and ip6table_nat are not auto-loaded. Without them wg-quick up wg0 fails and rolls back, leaving no wg0 interface. Symptom: every API call returns Command failed: wg show wg0 dump / No such device.
The wg-init service handles this automatically: it runs modprobe ip6_tables ip6table_nat (with SYS_MODULE cap and /lib/modules bind-mounted read-only) before wg-easy starts. Failures are silenced (|| true) so the setup works on kernels where these modules are built-in or unavailable.
Resource limits (override via .env):
| Variable | Default | Notes |
|---|---|---|
WG_MEM_LIMIT |
256m |
Hard cap for the wg-easy container |
WG_MEMSWAP_LIMIT |
256m |
Keep equal to WG_MEM_LIMIT to disable container swap |
WG_CPUS |
1.0 |
0.75 on single-core boards (Pi Zero, Pi 1) |
The limit exists to prevent Node.js from slowly growing over long uptime and triggering the host OOM-killer (symptom: SSH becomes unreachable). Do not go below 96m or the runtime OOM-kills on startup. See README §Dispositivi a risorse limitate for per-board guidance and swap configuration.