Compare commits

...

5 Commits

Author SHA1 Message Date
davide fc5d6209c1 docs: add resource limits guide for low-RAM devices
- 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>
2026-05-07 08:22:43 +02:00
davide 4d8eb01e4c fix: set wg-data ownership to repo user in wg-init
Mount the repo root read-only (/repo) in wg-init and use
`stat -c '%u:%g' /repo` to detect the host user automatically.
chown is applied before chmod 700, so the directory is accessible
without sudo on any machine regardless of UID.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 08:16:35 +02:00
davide 19d03ea624 docs(claude): document hardware compatibility and SBC constraints
Add a dedicated section covering:
- tested architectures: arm64 (RPi 3/4/5, Orange Pi, Rock Pi), armv7,
  amd64 (x86_64 servers, VMs, Intel NUC)
- known ip6tables boot issue on hosts with BSP/minimal kernels and how
  wg-init solves it automatically across all architectures
- resource limit variables (WG_MEM_LIMIT, WG_MEMSWAP_LIMIT, WG_CPUS)
  with guidance for boards under 1 GB RAM

Also corrects stale wg0.json reference to wg-easy.db in constraints list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 08:10:43 +02:00
davide df78675198 docs: update README and .gitignore for automated setup
- .gitignore: remove .gitkeep exception, ignore wg-data/ entirely and
  fix stale comment (directory is generated by the container, not
  created manually)
- README: document init container behaviour in setup step 4, update
  project structure (wg0.json -> wg-easy.db), align security section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 08:07:57 +02:00
davide 8c56e2fc9f feat: add wg-init service and pin image to minor tag
Introduce a wg-init container (Alpine) that runs before wg-easy and:
- loads ip6_tables and ip6table_nat kernel modules (silently skipped if
  already built-in or unavailable), fixing startup on hosts that do not
  auto-load these modules (e.g. Raspberry Pi)
- sets chmod 700 on wg-data/ so private keys are protected from the
  moment the container writes them

wg-easy now depends on wg-init completing successfully, making the setup
portable across hardware without any manual host configuration.

Also pins the image tag from 15.2.2 to the minor tag (15) to receive
patch updates automatically while avoiding breaking changes across majors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 08:07:50 +02:00
5 changed files with 92 additions and 11 deletions
+9 -3
View File
@@ -17,11 +17,17 @@ WG_UI_PORT=51821
# --- OPZIONALI — Limiti risorse container (SBC) ---
# Decommenta e adatta alla RAM disponibile:
# 512 MB RAM → WG_MEM_LIMIT=128m
# 1 GB RAM → WG_MEM_LIMIT=256m (default)
# 2 GB+ RAM → WG_MEM_LIMIT=384m
# 512 MB RAM (RPi 3B) → WG_MEM_LIMIT=96m (reale: ~37 MB, 96 m è già doppio)
# 1 GB RAM (RPi 3B+) → WG_MEM_LIMIT=128m
# 2 GB+ RAM (RPi 4/5) → WG_MEM_LIMIT=256m (default)
# Tenere WG_MEMSWAP_LIMIT = WG_MEM_LIMIT per disabilitare lo swap del container.
# Su single-core (Pi Zero / Pi 1) impostare WG_CPUS=0.75.
#
# ATTENZIONE — RPi 3 con uptime lungo: il processo Node.js può crescere nel
# tempo. Con 1 GB di RAM totale e OS+Docker che occupano ~350 MB, un limite
# di 256 m lascia pochissimo margine; il kernel OOM-killa SSH prima del
# container. Usare 96-128 m e assicurarsi che lo swap host sia >= 512 MB
# (vedi CLAUDE.md §SBC per il comando).
# WG_MEM_LIMIT=256m
# WG_MEMSWAP_LIMIT=256m
# WG_CPUS=1.0
+1 -1
View File
@@ -1,6 +1,6 @@
# Variabili locali — contiene segreti, non committare mai
.env
# Directory WireGuard — auto-generata dal container al primo avvio.
# Directory WireGuard — generata dal container al primo avvio.
# Contiene chiavi crittografiche private. Non committare mai.
wg-data/
+28 -1
View File
@@ -37,8 +37,35 @@ docker compose logs -f wg-easy
## Important constraints
- `wg-data/` is auto-generated by the container on first start and holds live WireGuard keys (`wg0.conf`, `wg0.json`). Never commit it.
- `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 to `700` automatically by `wg-init`.
- `.env` is gitignored.
- The container requires `NET_ADMIN` and `SYS_MODULE` capabilities plus `net.ipv4.ip_forward=1` sysctl — these are already set in `docker-compose.yml`.
- The router must forward UDP port 51820 (or `WG_PORT`) to the server's local IP.
- `INSECURE=true` is set in `docker-compose.yml` to 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.
+32 -5
View File
@@ -47,6 +47,8 @@ Inoltra la porta **UDP 51820** (o quella scelta in `WG_PORT`) verso l'IP locale
docker compose up -d
```
Al primo avvio, un init container crea automaticamente `wg-data/` con permessi `700` prima che wg-easy scriva le chiavi private.
### 5. Completa il setup dalla web UI
```
@@ -66,9 +68,35 @@ vpn/
├── .env # Variabili locali (NON committato)
├── .gitignore
├── README.md
└── wg-data/ # Generato automaticamente dal container (NON committato)
├── wg0.conf # Configurazione WireGuard
└── wg0.json # Stato interno wg-easy (peer, chiavi)
└── wg-data/ # Generato dal container al primo avvio (ignorato da git)
├── wg0.conf # Configurazione WireGuard con chiavi private
└── wg-easy.db # Database interno wg-easy
```
---
## Dispositivi a risorse limitate (SBC)
Su board con poca RAM (≤ 1 GB) il processo Node.js di wg-easy può crescere nel tempo e far sì che il kernel OOM-killi altri processi di sistema — SSH compreso. Per evitarlo, decommenta e adatta questi valori nel tuo `.env`:
```ini
# Board con 512 MB RAM
WG_MEM_LIMIT=96m
WG_MEMSWAP_LIMIT=96m
# Board con 1 GB RAM
WG_MEM_LIMIT=128m
WG_MEMSWAP_LIMIT=128m
```
Tieni `WG_MEMSWAP_LIMIT` uguale a `WG_MEM_LIMIT` per disabilitare lo swap del container: quando il container raggiunge il limite viene riavviato da Docker invece di consumare swap di sistema.
Assicurati anche che lo swap dell'host sia almeno 512 MB. Su Raspberry Pi OS il default è 100 MB — per aumentarlo:
```bash
sudo dphys-swapfile swapoff
sudo sed -i 's/CONF_SWAPSIZE=100/CONF_SWAPSIZE=512/' /etc/dphys-swapfile
sudo dphys-swapfile setup && sudo dphys-swapfile swapon
```
---
@@ -86,6 +114,5 @@ docker compose up -d
## Sicurezza
- `wg-data/` contiene chiavi crittografiche private. Non committarla mai su Git.
- `wg-data/` contiene chiavi crittografiche private ed è completamente ignorata da git. I permessi `700` vengono impostati automaticamente dall'init container al primo avvio.
- `.env` contiene variabili locali specifiche del server. Non committarlo.
- Entrambi sono esclusi da `.gitignore`.
+22 -1
View File
@@ -1,6 +1,27 @@
services:
wg-init:
image: alpine:3.21
cap_add:
- SYS_MODULE
volumes:
- ./wg-data:/data
- ./:/repo:ro
- /lib/modules:/lib/modules:ro
command:
- /bin/sh
- -c
- |
modprobe ip6_tables 2>/dev/null || true
modprobe ip6table_nat 2>/dev/null || true
chown "$(stat -c '%u:%g' /repo)" /data
chmod 700 /data
restart: "no"
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15.2.2
depends_on:
wg-init:
condition: service_completed_successfully
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
restart: unless-stopped