SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor

Cómo montar una VPN WireGuard en tu propio VPS

Configura WireGuard en un VPS Linux con wg0.conf, claves, reenvío IP, NAT, DNS y AllowedIPs. Incluye fallos de handshake, permisos y requisitos de virtualización.

Qué va a configurar

Una VPN de WireGuard en un servidor propio requiere unas cuarenta líneas de configuración: un par de claves, un archivo de interfaz, un parámetro de sysctl, una regla NAT y una excepción en el firewall. La instalación es sencilla, así que la mayor parte de esta guía cubre los problemas habituales: permisos de las claves, AllowedIPs, reenvío y DNS.

WireGuard es un túnel de capa 3 implementado en el kernel y forma parte de Linux desde la versión 5.6. Por eso, Ubuntu 24.04 y Debian 13 lo incluyen sin un módulo externo. No hay negociación de cifrado, autoridad certificadora ni paso de usuario y contraseña: un peer se identifica mediante una clave pública y las direcciones IP que esa clave puede usar. Los paquetes que no superan la comprobación MAC se descartan sin respuesta, por lo que el puerto no responde a los escaneos. La contrapartida es que no existe un servidor de autenticación. Para revocar el acceso, hay que eliminar el peer del servidor.

Compruebe primero la virtualización

WireGuard necesita un kernel en el que se pueda cargar un módulo y, en un VPS KVM, funciona de forma inmediata. En una virtualización basada en contenedores que comparte el kernel del host, como OpenVZ o LXC, el primer comando falla con RTNETLINK answers: Operation not supported y la alternativa es la implementación en el espacio de usuario wireguard-go. Compruébelo primero con sudo modprobe wireguard && echo ok.

Generar claves sin exponerlas

Un /etc/wireguard/server.key legible para todos equivale a no tener VPN. La línea habitual de umask 077 && wg genkey | sudo tee ... funciona: salvo que sudoers establezca umask_override, que está desactivado de forma predeterminada, sudo combina tu umask con su valor predeterminado, 0022, y nunca la reduce, por lo que el archivo que crea tee tiene el modo 600. Las claves se exponen cuando se ejecuta esa tubería sin umask 077, con una umask normal de 022, o en un sistema cuyo sudoers activa umask_override. Establece el modo explícitamente y esto deja de importar.

sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.key

Genera el par del cliente de la misma forma. wg genpsk añade una clave precompartida opcional, una línea en cada configuración.

La interfaz del servidor: /etc/wireguard/wg0.conf

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>

[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32

chmod 600; una advertencia de inicio que indica que el archivo es accesible para todos significa que omitió ese paso. Address es la dirección del servidor dentro del túnel y contiene la máscara de toda la subred de la VPN. Elija un rango que no vaya a encontrar en redes externas. 192.168.1.0/24 entra en conflicto con la mitad de los routers domésticos detrás de los que están sus clientes, y el túnel pierde el tráfico silenciosamente frente a la ruta local.

El AllowedIPs de un par en el lado del servidor es un /32: la única dirección del túnel que pertenece a ese cliente. Si asigna la misma IP permitida a dos pares, esta pasa al par configurado en último lugar y el primero deja de recibir tráfico sin que se muestre ningún error. Deje SaveConfig sin definir; de lo contrario, wg-quick down sobrescribe este archivo con el estado activo.

Convierte el equipo en un router

Un servidor Linux descarta los paquetes que no están dirigidos a él. El reenvío y el NAT de origen están desactivados de forma predeterminada.

printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
  | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward

Un sysctl -w básico funciona hasta el siguiente reinicio y después deja de funcionar sin mostrar ningún mensaje. El NAT necesita la interfaz de egress, es decir, la NIC que llega a Internet, no wg0. No des por hecho eth0; obtén el nombre de la interfaz mediante ip route show default, ya que las imágenes actuales usan nombres como enp1s0 o ens3.

Firewall: el puerto y la ruta de reenvío

Un archivo nftables cubre el filtrado y NAT. Escriba /etc/nftables.conf; esto vacía el conjunto de reglas existente, así que no lo ejecute en un equipo que ya administre ufw o Docker.

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    iif lo accept
    tcp dport 22 accept
    udp dport 51820 accept
  }
  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state established,related accept
    iifname "wg0" oifname "enp1s0" accept
  }
}

table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
  }
}

Aplíquelo con sudo systemctl enable --now nftables y mantenga abierta una segunda sesión SSH: policy drop, junto con un error tipográfico en la regla SSH, puede bloquearle el acceso a su propio servidor. Observe qué no permite la cadena forward: wg0 a wg0. Los peers llegan a Internet, pero no entre sí; añada iifname "wg0" oifname "wg0" accept para una VPN entre peers. La cadena forward no determina qué puede tocar un peer en el propio servidor: los paquetes dirigidos al equipo pasan por el hook input, no por forward. Por tanto, decide la cadena input y, tal como está escrita, permite que un peer llegue a SSH y a ningún otro servicio, ni siquiera hacer ping a 10.8.0.1. Añada allí iifname "wg0" icmp type echo-request accept, además de una regla iifname "wg0" por cada servicio al que quiera permitir acceso sólo a través del túnel. Esto es importante cuando el equipo también funciona como una máquina de desarrollo remoto que ejecuta Claude Code en tmux y prefiere no exponer públicamente esa parte.

En un equipo con ufw: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" en /etc/default/ufw y una regla MASQUERADE de POSTROUTING *nat al principio de /etc/ufw/before.rules.

Actívelo con systemd

sudo systemctl enable --now wg-quick@wg0
sudo wg show

wg-quick crea la interfaz, añade las direcciones e instala las rutas derivadas de AllowedIPs. enable --now es la parte importante: un wg-quick up wg0 ejecutado manualmente desaparece tras el siguiente reinicio, y las actualizaciones del kernel requieren reinicios. Una unidad que no vuelve a iniciarse después de uno de esos reinicios permanece en silencio hasta que alguien intenta conectarse, por lo que un archivo de configuración adicional OnFailure= en wg-quick@wg0, dirigido a su propio servidor ntfy, es la forma más económica de recibir el aviso en el teléfono en lugar de enterarse por un usuario que no puede iniciar sesión.

La configuración del cliente y el ajuste que casi todos configuran mal

[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

AllowedIPs cumple dos funciones distintas al mismo tiempo. Confundirlas es la causa de la mayoría de los problemas con WireGuard.

En las conexiones salientes, es una tabla de enrutamiento. Un paquete cuyo destino coincide con AllowedIPs de un peer se cifra y se envía a ese peer. 0.0.0.0/0, ::/0 envía todo por el túnel: es un túnel completo y el servidor actúa como ruta predeterminada. Un túnel dividido usa una lista más limitada: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 transporta el tráfico de la VPN y una red privada situada detrás del servidor, mientras que todo lo demás conserva su ruta local. Esta lista limitada permite mantener servicios completamente fuera de Internet pública. Por ejemplo, una instancia privada de Nextcloud en un VPS vinculada a la dirección del túnel o las máquinas virtuales del laboratorio de virtualización anidada que se ejecutan en el mismo equipo siguen siendo accesibles para los peers y permanecen invisibles para el resto.

En las conexiones entrantes, es una lista de control de acceso. Se descarta un paquete descifrado procedente de un peer cuya dirección de origen no está incluida en AllowedIPs de ese peer. Por eso el servidor especifica 10.8.0.2/32 para el portátil: una entrada 0.0.0.0/0 allí permitiría que ese cliente suplantara cualquier dirección del túnel.

PersistentKeepalive se usa con clientes situados detrás de NAT, donde el router mantiene abierta la asignación UDP sólo mientras circulan paquetes. Cuando la asignación caduca, el servidor ya no puede alcanzar al cliente. PersistentKeepalive = 25 mantiene abierta la asignación. Configúrelo en el cliente, no en un servidor con una dirección IP pública.

DNS y la filtración que nadie detecta

Con AllowedIPs = 0.0.0.0/0 y sin una línea DNS =, el cliente mantiene el resolvedor que aprendió de la red local, el router de la cafetería en 192.168.1.1. Esa ruta es más específica que la ruta predeterminada, por lo que las consultas DNS salen por el enlace local en texto claro mientras todo lo demás se tuneliza. El tráfico es privado; la lista de nombres no.

Hay dos opciones válidas. Apunte DNS a un resolvedor público (DNS = 9.9.9.9); las consultas viajarán por el túnel y saldrán desde el servidor, aunque ese resolvedor seguirá viéndolas. O ejecute unbound o dnsmasq enlazado a 10.8.0.1, configure DNS = 10.8.0.1 y añada udp dport 53 iifname "wg0" accept a la cadena de entrada; establezca esa línea y olvídese del resolvedor, y no se resolverá ningún nombre.

En los clientes Linux, wg-quick aplica DNS mediante resolvconf; si está ausente, obtendrá resolvconf: command not found. Instale openresolv o configure PostUp = resolvectl dns %i 10.8.0.1 en un cliente con systemd-resolved.

Añadir y eliminar peers sin interrumpir el túnel

Reiniciar la interfaz para añadir un usuario desconecta a todos los usuarios conectados. Añada el bloque [Peer] a wg0.conf y, después, vuelva a cargar el conjunto de peers sin reiniciar la interfaz.

sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'

wg-quick strip muestra la configuración sin las claves exclusivas de wg-quick (Address, DNS, PostUp), y syncconf aplica las diferencias mientras las sesiones activas continúan. Sólo actualiza los peers: un Address modificado todavía requiere un down/up completo. Revoque el acceso con sudo wg set wg0 peer <public key> remove y, después, elimine el bloque del archivo; de lo contrario, volverá a aparecer en la siguiente recarga.

Modos de fallo y cadenas que verá

El handshake nunca se completa. wg show muestra el peer sin latest handshake y los registros del cliente contienen:

Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)

No llega ningún paquete o no se acepta ninguno. Compruebe, en este orden: si UDP 51820 está abierto en el firewall del VPS y en el firewall de red del proveedor, que suele ser un control independiente en la mayoría de los paneles; si la dirección y el puerto de Endpoint son correctos; y si las claves están intercambiadas correctamente. La clave del bloque [Peer] del cliente debe ser la clave pública del servidor, y viceversa. Pegar una clave privada o la clave pública del propio cliente produce exactamente este síntoma. sudo tcpdump -ni any udp port 51820 en el servidor muestra si llegan paquetes. El módulo del kernel no registra nada de forma predeterminada. Los mensajes de WireGuard aparecen en dmesg sólo después de activar la depuración dinámica (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). Con ella activada, una clave incorrecta aparece como un descarte por MAC no válida.

El handshake funciona, pero no hay Internet. ping 10.8.0.1 se completa, pero ping 1.1.1.1 agota el tiempo de espera: falta el reenvío o NAT. Compruebe que sysctl net.ipv4.ip_forward muestre 1. Después, observe los contadores mientras el cliente hace ping, con sudo nft list ruleset o sudo iptables -t nat -L POSTROUTING -n -v. Cero paquetes en la regla de masquerade significa que el nombre de la interfaz de salida es incorrecto. Un contador que aumenta sin respuestas apunta a la política de la cadena de reenvío.

Internet funciona, pero no los nombres. ping 1.1.1.1 se completa y curl https://example.com devuelve Could not resolve host. Falta la línea DNS o especifica un resolver que no es accesible desde el interior del túnel.

Algunos sitios HTTPS se quedan bloqueados. SSH y ping funcionan, pero las páginas grandes se detienen. La causa es la MTU de la ruta: el túnel añade sobrecarga y algún enlace intermedio descarta los paquetes demasiado grandes sin devolver un mensaje ICMP. Configure MTU en [Interface] del cliente. En un enlace normal de 1500 bytes, wg-quick ya selecciona 1420, es decir, 1500 menos sus 80 bytes de sobrecarga. Por tanto, escribir 1420 no cambia nada. Pruebe 1380 y después 1280. Si reducir la MTU elimina las detenciones, pero el rendimiento sigue siendo inferior al esperado, no siga probando valores redondos al azar. Siga cómo encontrar la MTU real de la ruta mediante bisección y limitar el MSS de TCP. Este procedimiento también descarta las causas que no corresponden al túnel.

La interfaz se niega a iniciarse. Address already in use significa que otro proceso mantiene ocupado UDP 51820. Cannot find device wg0 después de un up fallido suele significar que se rechazó la configuración. Lea journalctl -u wg-quick@wg0 -n 50.

Migración desde Streisand u OpenVPN

Streisand ya no recibe mantenimiento y su repositorio está archivado. Ejecutar una VPN sobre automatización abandonada genera un problema de seguridad que empeora con el tiempo. No existe una actualización in situ y la PKI de OpenVPN no se puede convertir: WireGuard no usa certificados ni CA, y sus claves no caducan. Por tanto, cada cliente debe recibir un nuevo par de claves.

Realice la migración en paralelo. WireGuard en UDP 51820 puede coexistir con OpenVPN en 1194 en el mismo equipo. Configure wg0, migre los clientes de uno en uno y detenga después el servicio antiguo. El modelo de nombres de usuario, contraseñas y revocación de OpenVPN no se puede trasladar. Si necesita cuentas o un registro de auditoría, añada esa funcionalidad por encima de WireGuard.

Copias de seguridad, actualizaciones y lo que se complica a escala

/etc/wireguard es el servidor. Haga una copia de seguridad (sudo tar czf wg-backup.tgz -C /etc wireguard, con modo 600 y almacenada fuera del equipo) y podrá reconstruirlo en un VPS nuevo en minutos. Si pierde la clave privada del servidor, deberá volver a emitir la configuración de todos los clientes, porque estos fijan la clave pública del servidor. Las actualizaciones consisten normalmente en un apt upgrade y un reinicio para las actualizaciones del kernel, y wg-quick@wg0 vuelve a iniciarse por sí solo si lo habilitó.

El estado de cada peer ocupa poco y las operaciones criptográficas se ejecutan en el kernel. Por tanto, el límite lo marcan la CPU y el ancho de banda disponibles en el VPS, no este archivo de configuración. Mídalo con iperf3 a través del túnel en lugar de confiar en una cifra publicada. Lo que se complica a escala es la operación. Cada peer necesita una IP de túnel única, y editar manualmente sesenta bloques [Peer] es la forma de introducir AllowedIPs duplicadas: genere las configuraciones con un script. Un servidor equivale a un endpoint UDP y a un único punto de fallo, y WireGuard no tiene clustering. La redundancia requiere un segundo servidor con sus propias claves. La rotación de claves sigue siendo manual, así que documente quién tiene cada clave y cómo revocar una. Cuando esa gestión deja de caber en un archivo de texto, la respuesta habitual es añadir un plano de control sobre el mismo plano de datos del kernel. Un servidor NetBird autogestionado se encarga de la asignación de direcciones, la distribución de peers y las claves de configuración que, de otro modo, tendría que gestionar manualmente. Si ejecutar ese plano de control por su cuenta supone añadir un equipo más de lo que desea, Tailscale aloja uno por usted, y su plan Personal gratuito cubre seis usuarios con dispositivos de usuario ilimitados y, para empezar, hasta 50 recursos etiquetados, como servidores, suficiente para que la mayoría de las infraestructuras personales nunca tengan que pagar. A partir de ahí, el precio depende de las personas y no de las máquinas. Por eso, lo que paga realmente una familia o un equipo pequeño depende de cuántas personas tengan credenciales, no de cuántos peers habría tenido que introducir manualmente en wg0.conf. En ese modelo, la gestión del split tunnel mediante AllowedIPs se convierte en anunciar sus rangos privados desde un subnet router, una sola vez desde un VPS y con aprobación central, en lugar de pegarla en cada archivo de cliente. La conveniencia de ese cambio depende de qué recursos pueda alcanzar realmente un plano de control alojado y de que nunca almacena las claves que cifran su tráfico, aunque sí decide qué peers conocen la existencia de los demás.

Todo esto requiere un equipo Linux que controle, una IP pública, un kernel en el que pueda cargar un módulo y un firewall que administre de extremo a extremo.

FAQ

¿Por qué el handshake de WireGuard nunca se completa?

wg show al mostrar un peer sin latest handshake significa que los paquetes no llegan o no se aceptan. Compruebe UDP 51820 en el firewall del VPS y en el firewall de red independiente del proveedor. Confirme el host y el puerto de Endpoint. Después, compruebe que las claves no estén intercambiadas. El bloque [Peer] del cliente debe contener la clave pública del servidor. sudo tcpdump -ni any udp port 51820 en el servidor muestra si llegan paquetes. dmesg sólo informa de los fallos del handshake de WireGuard después de activar la depuración dinámica (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). En ese caso, una discrepancia de claves aparece como un descarte por MAC no válida.

El túnel se conecta, pero no tengo Internet. ¿Qué falta?

Que ping 10.8.0.1 funcione mientras ping 1.1.1.1 agota el tiempo de espera apunta a un problema de reenvío o NAT. Confirme que sysctl net.ipv4.ip_forward lea 1 y que esté configurado en /etc/sysctl.d/, no sólo mediante un sysctl -w que desaparezca al reiniciar. Después, compruebe que la regla de masquerade use la interfaz de salida real indicada por ip route show default, enp1s0 o ens3, y en raras ocasiones eth0.

¿Necesito la línea DNS = en la configuración del cliente?

Con un túnel completo y sin la línea DNS =, el cliente conserva el resolver que aprendió de la red local. Esas consultas salen sin cifrar por el enlace local, mientras que el resto del tráfico se tuneliza. Apunte DNS a un resolver público, o ejecute unbound/dnsmasq asociado a 10.8.0.1 y abra udp dport 53 iifname "wg0" en la cadena de entrada.

¿Qué controla realmente AllowedIPs?

Tiene dos funciones. Para el tráfico saliente, es una tabla de enrutamiento: el tráfico que coincide con AllowedIPs de un peer se cifra y se envía a ese peer. Para el tráfico entrante, es una lista de control de acceso: se descarta un paquete descifrado cuyo origen esté fuera de AllowedIPs de ese peer. Por eso el servidor especifica un /32 por cliente, mientras que el cliente puede especificar 0.0.0.0/0.

¿WireGuard funcionará en cualquier VPS?

En un VPS KVM funciona con el módulo del kernel y sin configuración adicional. En una virtualización basada en contenedores que comparte el kernel del host, como OpenVZ o LXC, modprobe wireguard falla con Operation not supported. La alternativa es la implementación en espacio de usuario wireguard-go. Ejecute sudo modprobe wireguard && echo ok antes de cualquier otra acción.