21 Oct Cómo montar tu propia VPN con WireGuard, wg-easy, Docker y Caddy en Ubuntu
Una VPN autogestionada puede ser un proyecto excelente para aprender sobre redes, cifrado, Linux y contenedores. También puede servir para conectar dispositivos a través de un túnel cifrado o utilizar un servidor remoto como punto de salida a Internet.
En esta guía desplegaremos una VPN basada en WireGuard, administrada mediante wg-easy, ejecutada con Docker Compose y publicada con HTTPS mediante Caddy. Además de la instalación, revisaremos la arquitectura, los puertos, la seguridad y las pruebas de conectividad.
Alcance y advertencia: el procedimiento está pensado como base para un laboratorio personal o un servicio pequeño. Antes de usarlo en producción, valida las versiones, las reglas de red y los requisitos del proveedor del VPS. Los nombres de variables y el proceso de configuración de wg-easy pueden cambiar entre versiones.
1. ¿Qué es cada componente?
- WireGuard: tecnología VPN que establece túneles cifrados entre pares autorizados. Cada cliente tiene su propia identidad basada en claves.
- wg-easy: interfaz web para configurar y administrar clientes WireGuard, generar perfiles y mostrar códigos QR.
- Docker Engine: ejecuta los servicios en contenedores.
- Docker Compose: declara y administra los contenedores mediante YAML.
- Caddy: servidor web y proxy inverso que puede obtener y renovar certificados TLS automáticamente.
Una VPN no proporciona anonimato absoluto ni protege por sí sola un dispositivo comprometido. La seguridad depende también de la administración de claves, las rutas, el firewall, las actualizaciones y la configuración de los clientes.
2. Arquitectura
El despliegue separa dos flujos de red:
- Administración web: el navegador se conecta a
https://vpn.tudominio.compor TCP 443. Caddy termina TLS y reenvía la solicitud al servicio web interno de wg-easy. - Túnel VPN: los clientes WireGuard se conectan al VPS por UDP 51820. Este tráfico no pasa por Caddy.

Importante: el puerto del panel de wg-easy no se publica en la interfaz del host. Caddy y wg-easy comparten una red Docker privada, y Caddy se conecta al nombre del servicio. Así se evita exponer directamente el puerto de administración en Internet.
3. Requisitos previos
Necesitarás:
- Un VPS con Ubuntu compatible con Docker y arquitectura soportada por la imagen.
- Una IP pública.
- Acceso SSH administrativo.
- Un dominio o subdominio, por ejemplo
vpn.tudominio.com. - Acceso a la administración DNS.
- Permisos para configurar el firewall del sistema y el firewall del proveedor.
- Una aplicación cliente WireGuard en los dispositivos que se conectarán.
Antes de empezar, revisa los límites de transferencia del VPS, porque el tráfico de los clientes puede salir a Internet a través de ese servidor y consumir su cuota de datos.
4. Configurar DNS
Crea en el proveedor DNS un registro A:
Campo Ejemplo
Tipo A
Nombre vpn
Valor IP pública del VPS
TTL 300 o automático
Comprueba la resolución desde tu equipo:
dig +short vpn.tudominio.com
La respuesta debe ser la IP pública correcta. Si tienes un registro AAAA, confirma que la conectividad IPv6 funciona de verdad; un registro IPv6 incorrecto puede causar fallos de acceso o de validación del certificado.
Caddy necesita que el dominio sea resoluble y que el servidor pueda recibir las conexiones necesarias para obtener el certificado público.
5. Actualizar Ubuntu y preparar SSH
Conéctate al servidor:
ssh usuario@TU_IP_PUBLICA
Actualiza el índice de paquetes e instala las actualizaciones disponibles:
sudo apt update sudo apt upgrade -y
En un entorno productivo, revisa los cambios y mantén un procedimiento de recuperación. Antes de habilitar el firewall, confirma que la regla SSH permite tu forma de administración para evitar bloquearte.
6. Instalar Docker
Para un entorno mantenible, sigue la guía oficial de Docker Engine para Ubuntu:
Después, verifica la instalación:
docker --version docker compose version sudo systemctl enable --now docker
No añadas usuarios al grupo docker sin entender sus implicaciones: ese grupo normalmente concede privilegios equivalentes a root sobre el host. Administra quién puede ejecutar comandos Docker.
7. Crear el proyecto
Crea una carpeta para los archivos de configuración:
mkdir -p ~/wireguard cd ~/wireguard
Dentro guardaremos compose.yml y Caddyfile. Docker utilizará volúmenes persistentes para conservar la configuración de WireGuard y los datos de Caddy.
8. Crear el archivo compose.yml
La siguiente composición sigue el patrón documentado por wg-easy para integrar Caddy mediante una red Docker compartida. La imagen de wg-easy usa la etiqueta mayor 15; antes de producción, fija una versión de parche o digest validado y prueba la actualización en un entorno controlado.
Crea el archivo:
nano compose.yml
Contenido:
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
restart: unless-stopped
environment:
# wg-easy v15 sirve el panel en el puerto interno 80
- PORT=80
volumes:
- etc_wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
ports:
# Solo el túnel VPN se publica en el host
- "51820:51820/udp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.all.forwarding=1
- net.ipv6.conf.default.forwarding=1
networks:
- proxy
caddy:
image: caddy:2.10.0-alpine
container_name: caddy
restart: unless-stopped
ports:
- "80:80/tcp"
- "443:443/tcp"
# UDP 443 habilita HTTP/3; es opcional
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- proxy
networks:
proxy:
name: wireguard-proxy
volumes:
etc_wireguard:
caddy_data:
caddy_config:
¿Por qué no se publica el puerto 51821?
En esta configuración, wg-easy escucha en el puerto interno 80 y Caddy lo alcanza a través de la red Docker wireguard-proxy. No se declara una publicación de puerto para la interfaz web en el host. Por eso no es necesario exponer el puerto TCP 51821.
Permisos del contenedor
NET_ADMIN permite operaciones administrativas de red. SYS_MODULE y el montaje de /lib/modules aparecen en el patrón de instalación de wg-easy, pero amplían las capacidades del contenedor. Revisa los requisitos de la versión, el kernel y el proveedor; no elimines ni añadas capacidades a ciegas. En un entorno de mayor exigencia, evalúa alternativas que reduzcan privilegios y valida la compatibilidad con el host.
Sobre IPv6
El ejemplo contempla parámetros de forwarding IPv6, pero eso no significa que la conectividad IPv6 quede configurada automáticamente de extremo a extremo. Si no vas a utilizar IPv6, revisa si necesitas esos parámetros y no configures rutas IPv6 completas en los clientes hasta validar direccionamiento, NAT/rutas y firewall.
9. Configurar Caddy
Crea el archivo:
nano Caddyfile
Contenido:
vpn.tudominio.com {
reverse_proxy wg-easy:80
}
Reemplaza vpn.tudominio.com por tu dominio real.
Caddy y wg-easy comparten la red Docker wireguard-proxy, por lo que Caddy puede resolver wg-easy como nombre del servicio. Caddy gestionará automáticamente el certificado TLS público cuando el DNS, los puertos externos y los permisos de almacenamiento estén correctamente configurados.
No agregues tls internal para este caso si necesitas un certificado público confiable por navegadores sin instalar una autoridad certificadora privada. La opción tls internal sirve certificados emitidos por la CA local de Caddy y tiene un propósito diferente.
Documentación:
– wg-easy: integración con Caddy
10. Configurar el firewall
Si utilizas UFW:
sudo apt install ufw -y # Mantén acceso SSH antes de activar el firewall sudo ufw allow 22/tcp # Caddy: validación HTTP y HTTPS sudo ufw allow 80/tcp sudo ufw allow 443/tcp # WireGuard sudo ufw allow 51820/udp sudo ufw status verbose
Una vez confirmadas las reglas, habilita UFW si aún no está activo:
sudo ufw enable
Si tu proveedor tiene firewall de red, permite también los puertos requeridos allí. La configuración del host y la del proveedor son controles distintos.
Resumen de puertos
Puerto Protocolo Exposición Uso
22 TCP Restringir cuando SSH sea posible
80 TCP Público Caddy, validación y redirección
443 TCP Público Panel HTTPS
443 UDP Opcional HTTP/3 de Caddy
51820 UDP Público Túnel WireGuard
80 interno TCP Solo red Docker Panel wg-easy detrás de Caddy
51821 TCP No publicado en No se usa como el host puerto público en esta composición
11. Validar e iniciar los contenedores
Desde ~/wireguard:
docker compose -f compose.yml config docker compose -f compose.yml up -d docker compose -f compose.yml ps
Consulta los registros:
docker compose -f compose.yml logs -f caddy
Si Caddy no obtiene un certificado, revisa:
- Que el registro DNS apunte al VPS.
- Que los puertos 80 y 443 sean accesibles desde Internet.
- Que no haya otro servicio ocupando esos puertos.
- Que los volúmenes
/datay/configde Caddy sean persistentes y escribibles. - Que el nombre del dominio en
Caddyfilesea correcto.
Después abre https://vpn.tudominio.com. En una instalación inicial de wg-easy v15, sigue el asistente de configuración del panel para completar la configuración inicial y crear el acceso administrativo. Los pasos exactos dependen de la versión; consulta la documentación de wg-easy.
No dejes el panel sin configurar ni compartas el acceso administrativo. Activa las medidas de autenticación disponibles en la versión instalada y, cuando sea posible, añade MFA y restricciones de acceso.
12. Crear un cliente
Desde el panel:
- Completa la configuración inicial.
- Crea un cliente con un nombre reconocible, por ejemplo
mac-personal. - Descarga su archivo
.confo utiliza el QR desde un dispositivo móvil. - Importa el perfil en la aplicación WireGuard oficial.
- Activa el túnel y verifica la conectividad.
Cada dispositivo debe tener su propio perfil. Los archivos de configuración pueden contener claves privadas: trátalos como secretos, no los envíes por canales inseguros y no los subas a repositorios.
Si un dispositivo se pierde o una clave queda expuesta, deshabilita el cliente desde el panel y genera un perfil nuevo cuando corresponda.
13. VPN completa o túnel dividido
AllowedIPs determina qué destinos se enrutan por el túnel desde la perspectiva del cliente.
- Túnel completo IPv4:
0.0.0.0/0dirige las rutas IPv4 predeterminadas por la VPN. - Túnel completo IPv6:
::/0dirige las rutas IPv6 predeterminadas por la VPN, pero solo debe usarse si IPv6 funciona de extremo a extremo. - Túnel dividido: se incluyen únicamente las redes que deben pasar por la VPN, por ejemplo una subred privada.
Una VPN de túnel completo puede hacer que el tráfico de Internet salga usando la IP pública del VPS. Para que funcione, el servidor debe tener forwarding, rutas, reglas de NAT y firewall correctos.
14. Validar la conectividad
Desde el cliente con la VPN activa, prueba:
ping -c 3 1.1.1.1 ping -c 3 google.com
La primera prueba ayuda a revisar la conectividad IP; la segunda también depende del DNS.
En el servidor, consulta el estado de WireGuard usando el procedimiento soportado por la versión instalada. También puedes revisar los registros:
docker compose -f compose.yml logs --tail=100 wg-easy docker compose -f compose.yml logs --tail=100 caddy
Un ping fallido no demuestra por sí solo que el túnel esté roto: algunos destinos bloquean ICMP. Complementa las pruebas con estado del túnel, rutas, DNS, firewall y tráfico real de una aplicación.
Checklist de solución de problemas
- El nombre DNS resuelve a la IP pública correcta.
- UDP 51820 está permitido en UFW y en el firewall del proveedor.
- TCP 80 y 443 llegan a Caddy.
- El contenedor wg-easy está activo.
- El cliente utiliza el endpoint y las claves correctas.
- El forwarding IPv4 está activo si se requiere enrutamiento.
- NAT/rutas y reglas de firewall permiten el tráfico esperado.
- El DNS configurado en el cliente responde.
- IPv6 está configurado de forma coherente o no se enruta por eltúnel.
- No hay conflictos entre el rango VPN y las redes locales del cliente.
15. Operación diaria y actualizaciones
Comandos habituales:
cd ~/wireguard # Estado docker compose -f compose.yml ps # Registros docker compose -f compose.yml logs -f # Reiniciar docker compose -f compose.yml restart # Detener docker compose -f compose.yml down
Antes de actualizar, respalda la configuración persistente y lee las notas de versión. Luego descarga la imagen y recrea los servicios durante una ventana controlada:
docker compose -f compose.yml pull docker compose -f compose.yml up -d docker compose -f compose.yml ps
La etiqueta :15 limita la imagen a la serie mayor 15, pero no fija una versión de parche inmutable. Para despliegues críticos, utiliza una etiqueta concreta o digest validado, registra la versión instalada y prueba la actualización antes de aplicarla.
No elimines los volúmenes al detener el servicio. Un comando como docker compose down -v puede borrar volúmenes y provocar pérdida de configuración.
16. Seguridad y buenas prácticas
Acceso administrativo
- Usa una contraseña fuerte y las medidas de autenticación disponibles, incluida MFA cuando la versión lo permita.
- Restringe SSH a direcciones conocidas o utiliza un acceso administrativo privado si es viable.
- No publiques el puerto interno del panel.
- Revisa periódicamente las cuentas y los clientes que siguen autorizados.
- No compartas archivos
.conf, claves privadas, secretos ni códigos QR de clientes.
Sistema y contenedores
- Instala actualizaciones de seguridad de Ubuntu.
- Fija y valida versiones de imágenes.
- Revisa capacidades de contenedor como
NET_ADMINySYS_MODULE. - Limita quién puede administrar Docker.
- Conserva registros útiles sin almacenar secretos innecesarios.
Respaldos y recuperación
- Respalda los datos persistentes de wg-easy y Caddy.
- Cifra los respaldos y restringe su acceso.
- Mantén copias fuera del VPS.
- Documenta cómo restaurar y prueba la recuperación.
- Define cuánto tiempo conservar las copias y cómo eliminarlas de forma segura.
Monitorización
Supervisa CPU, memoria, espacio en disco, estado de contenedores, errores de Caddy, conectividad del túnel y consumo de transferencia. Para un servicio empresarial, incorpora alertas y un procedimiento de respuesta ante incidentes.
17. Costos y escalabilidad
El costo depende del VPS, el ancho de banda, la transferencia de datos, los respaldos y el nivel de disponibilidad requerido. Revisa la política de tráfico del proveedor: una VPN puede consumir mucha transferencia si varios clientes envían todo su tráfico de Internet a través del servidor.
Una única instancia suele ser suficiente para un laboratorio, pero constituye un punto único de falla. Para un uso empresarial, evalúa disponibilidad, recuperación ante desastres, administración de claves, segmentación, controles de acceso y requisitos de cumplimiento.
18. Conclusión
WireGuard, wg-easy, Docker Compose y Caddy permiten construir una VPN autogestionada con un túnel cifrado, una interfaz de administración y HTTPS para proteger el acceso al panel.
La parte más importante no es solamente conseguir que el contenedor arranque: también hay que comprobar las rutas, el DNS, el firewall, el almacenamiento persistente y la protección de las claves. La seguridad debe mantenerse durante todo el ciclo de vida, incluidas las actualizaciones y la revocación de clientes.
Si trabajas con Linux, redes, DevOps o seguridad en la nube, esta práctica es una buena oportunidad para reforzar conocimientos de conectividad, contenedores y operación segura.