Cuando monté mi homelab hace unos años, lo primero que quería era acceder a mis servicios desde fuera de casa sin abrir puertos al mundo. Empecé con OpenVPN, que funcionaba pero era una pesadilla de configurar. Luego me pasé a Tailscale, que sigue siendo mi opción favorita para redes mesh complejas. Pero hace unos meses descubrí wg-easy, y para casos simples es lo que uso ahora.
WireGuard en sí lleva años disponible. El problema siempre ha sido la configuración. Generar claves, editar archivos de configuración manualmente, distribuir los perfiles a los clientes… no es complicado, pero tiene fricción suficiente como para que muchos lo dejen a medias. wg-easy resuelve exactamente ese problema: es un contenedor Docker que levanta WireGuard con una interfaz web desde la que puedes gestionar clientes en dos clics.
En este artículo te cuento cómo lo tengo montado, qué tal rinde en la práctica y dónde tiene sus límites.
Por qué WireGuard y no otra cosa#
Antes de entrar en wg-easy, vale la pena entender por qué WireGuard merece tu atención frente a OpenVPN o IPSec.
WireGuard usa criptografía moderna (ChaCha20, Poly1305, Curve25519) y tiene una base de código de unas 4.000 líneas. Para comparar, OpenVPN tiene más de 100.000. Menos código significa menos superficie de ataque y, en la práctica, menos bugs. Está en el kernel de Linux desde la versión 5.6.
En velocidad la diferencia es notable. En mi servidor (un mini PC con i5 de 11a generación y 16 GB de RAM corriendo Debian 12), WireGuard satura mi conexión de 600 Mbps sin despeinarse. Con OpenVPN la cifra caía a unos 300-350 Mbps y la CPU subía claramente durante transferencias grandes.
La desventaja principal de WireGuard es que mantiene estado de forma diferente a otros protocolos. No tiene un concepto de “conexión” como TCP, lo que puede hacer que detectar si un cliente está activo sea menos inmediato. Para VPN de acceso a homelab esto no suele ser un problema real.
Qué es wg-easy exactamente#
wg-easy es un proyecto open source de Emile Nijssen (ahora mantenido por la comunidad bajo el repo wg-easy/wg-easy en GitHub). Es una imagen Docker que encapsula WireGuard junto con un servidor web en Node.js que expone una interfaz de usuario para gestionar clientes.
Desde esa interfaz puedes:
- Crear nuevos clientes con un nombre descriptivo
- Descargar el archivo de configuración o escanear un QR con el móvil
- Ver qué clientes están conectados y cuándo fue la última conexión
- Pausar o eliminar clientes
No hace más que eso, y en esa sencillez está su valor. No es una herramienta para redes complejas con múltiples subredes o políticas de acceso granulares. Para eso ya tienes Tailscale o Headscale. wg-easy es para el caso habitual: quiero conectarme a mi red desde el portátil o el móvil cuando estoy fuera.
Requisitos previos#
Necesitas:
- Un servidor con IP pública estática (o dinámica con un dominio y DNS dinámico)
- Docker y Docker Compose instalados
- El puerto UDP que uses (por defecto 51820) accesible desde internet
El último punto es el que más gente omite. WireGuard funciona sobre UDP, así que si tienes un firewall delante de tu servidor necesitas abrir ese puerto. En mi caso el servidor está detrás de un router, así que tengo una regla de NAT que redirige el 51820/UDP a la IP interna del servidor.
Si tu ISP te da CGNAT y no puedes abrir puertos, esta opción no te va a funcionar directamente. Para ese caso te recomiendo mirar Cloudflare Tunnels o Headscale combinado con Tailscale.
Instalación con Docker Compose#
Crea un directorio para el proyecto y dentro un archivo docker-compose.yml:
| |
Algunas notas sobre la configuración:
WG_HOST debe ser la IP pública o dominio que usarán los clientes para conectarse. Si tienes un dominio con DNS dinámico, ponlo aquí.
PASSWORD_HASH es un hash bcrypt de la contraseña de la interfaz web. En versiones recientes de wg-easy ya no se acepta la contraseña en texto plano por seguridad. Para generar el hash puedes usar este comando (necesitas tener Docker):
| |
Copia el resultado, pero ojo: en el docker-compose.yml necesitas escapar los $ duplicándolos ($$). Si el hash es $2y$10$abc..., en el YAML escribe $$2y$$10$$abc....
WG_ALLOWED_IPS con 0.0.0.0/0 enruta todo el tráfico del cliente por la VPN (full tunnel). Si solo quieres acceder a tu red local, cambia esto a la subred de tu homelab, por ejemplo 10.0.0.0/8,192.168.1.0/24.
WG_DEFAULT_DNS indica el servidor DNS que usarán los clientes. Si tienes Pi-hole o AdGuard Home en tu red, ponle aquí la IP de ese servicio (la IP interna en la red de WireGuard, que por defecto sería algo como 10.8.0.1).
Con el compose listo, levanta el servicio:
| |
Accede a http://tu-servidor:51821 con la contraseña que configuraste y deberías ver la interfaz.
Añadir el primer cliente#
En la interfaz web verás un botón para crear un nuevo cliente. Le pones un nombre descriptivo (yo uso nombres del dispositivo: “portatil-trabajo”, “movil-personal”) y listo. wg-easy genera automáticamente el par de claves y te muestra un QR.
En el móvil, abre la app oficial de WireGuard (disponible en iOS y Android), pulsa el botón de añadir y escanea el QR. En cuestión de segundos tienes el túnel configurado.
En el portátil, descarga el archivo .conf desde la interfaz web e impórtalo en el cliente de WireGuard para tu sistema operativo. En Linux también puedes copiarlo directamente a /etc/wireguard/ y activarlo con wg-quick up wg0.
Rendimiento real#
He estado usando wg-easy desde hace unos cuatro meses. El servidor VPN corre en el mismo mini PC que tengo como servidor principal de homelab, junto con varios contenedores Docker.
El consumo de recursos es casi nulo cuando no hay tráfico activo. WireGuard tiene la propiedad de no enviar paquetes de keepalive por defecto, lo que lo hace muy eficiente en batería para los clientes móviles.
Cuando hago transferencias reales, como copiar archivos a un servidor NAS a través de la VPN, alcanzo tranquilamente los 50-60 MB/s sobre mi conexión de fibra. El cuello de botella siempre es la línea de internet, no el protocolo.
La latencia es notablemente baja comparada con OpenVPN. En pruebas con ping desde el móvil conectado por 4G a un servidor de mi homelab, obtengo entre 15 y 25 ms habitualmente. Con OpenVPN estaba entre 30 y 50 ms.
Actualizar wg-easy#
Al ser un contenedor Docker, actualizar es sencillo:
| |
Los datos de configuración (clientes, claves) se guardan en el volumen ./data, así que no se pierden entre actualizaciones. Yo tengo Watchtower configurado para notificarme cuando hay imagen nueva, pero no actualizo automáticamente porque prefiero revisar el changelog antes.
Hablando de Watchtower: si lo tienes en modo automático y actualiza wg-easy, todos los clientes conectados perderán la conexión durante el reinicio del contenedor. Son 5-10 segundos, pero tenlo en cuenta si alguien depende de la VPN para trabajar.
Limitaciones y cuándo no usar wg-easy#
wg-easy no es para todo. Hay casos en los que no es la herramienta adecuada:
Si necesitas múltiples subredes o políticas de acceso por usuario, wg-easy no tiene esa granularidad. Puedes definir qué IPs puede alcanzar cada cliente con el parámetro AllowedIPs individual, pero hay que editarlo a mano en los archivos de configuración.
Si tienes varios servidores en distintas ubicaciones y quieres que se comuniquen entre sí, Tailscale o Headscale son mucho más apropiados. wg-easy es para el patrón hub-and-spoke clásico, donde un servidor central conecta con varios clientes.
Si tu ISP usa CGNAT y no puedes recibir conexiones entrantes, este enfoque directamente no funciona. En ese caso necesitas un servidor con IP pública como relay (VPS barato) o usar Tailscale con sus servidores de relay (DERP).
Si quieres audit logs detallados o gestión de usuarios con autenticación, wg-easy es demasiado básico. Para entornos más serios mira Netbird o algo basado en OpenZiti.
Seguridad: algunas consideraciones#
La interfaz web de wg-easy solo debería ser accesible desde tu red local o detrás de un proxy con autenticación adicional. Por defecto el puerto 51821 queda expuesto, lo que no es ideal si tu servidor tiene IP pública directa.
Yo lo tengo detrás de Nginx Proxy Manager con autenticación básica adicional y solo accesible vía un subdominio interno que no tiene registro DNS público.
El puerto UDP de WireGuard (51820 por defecto) puedes cambiarlo a un puerto no estándar para reducir el ruido de escanners automáticos. No mejora la seguridad de forma significativa, pero sí reduces logs de intentos de conexión aleatorios.
Mantén las claves privadas bien guardadas. El volumen ./data contiene todo: claves del servidor, configuraciones de clientes. Inclúyelo en tu estrategia de backup, pero asegúrate de que los backups estén cifrados.
¿Merece la pena sobre Tailscale?#
Depende del caso de uso. Tailscale es más versátil, más fácil de gestionar en redes con muchos nodos y funciona mejor cuando los dispositivos están detrás de CGNAT. Pero tiene el inconveniente de depender de los servidores de Tailscale para la coordinación (aunque existe Headscale como alternativa self-hosted).
wg-easy es completamente tuyo. Ningún tercero ve tus claves ni tu tráfico. Si lo que buscas es acceso VPN desde el móvil o el portátil a tu homelab, y tienes control sobre los puertos de tu router, wg-easy es más simple y tiene menos piezas que mantener.
En mi caso tengo los dos: Tailscale para la red mesh entre servidores y wg-easy para conectar dispositivos de uso personal. Cada uno hace lo que mejor sabe hacer.
Conclusión#
wg-easy hace exactamente lo que promete: quita la fricción de configurar WireGuard. En 10 minutos tienes una VPN funcional con interfaz web, generación de QR para móviles y gestión básica de clientes. Es open source, liviano y sin dependencias externas.
No reemplaza herramientas más completas para redes complejas, pero para el caso de uso habitual del homelab (conectarse desde fuera de casa, acceder a servicios internos) cumple de sobra.
Si llevas tiempo queriendo montar una VPN propia y lo has ido aplazando por la complejidad de la configuración, wg-easy es el empujón que necesitabas.