instalar wireguard en vps linux
Guía para configurar WireGuard en Linux. Incluye generación de llaves, archivo wg0.conf, IP forwarding, reglas NAT y solución de errores de handshake.
What you are building
A WireGuard VPN on a server you own is about forty lines of config: one key pair, one interface file, one sysctl, one NAT rule, one firewall hole. The install is trivial, so most of this guide covers what breaks — key permissions, AllowedIPs, forwarding and DNS.
WireGuard is a Layer 3 tunnel in the kernel, mainline since Linux 5.6, so Ubuntu 24.04 and Debian 13 ship it with no external module. No cipher negotiation, no certificate authority, no username/password step: a peer is a public key plus the IP addresses that key may use. A packet failing its MAC check is dropped with no reply, so the port does not answer scans. The flip side: no auth server exists, so removing access means deleting a peer on the box.
Verifique la virtualización primero
WireGuard requiere un kernel que permita la carga de módulos. En una VPS KVM, funciona directamente. En virtualización basada en contenedores que comparten el kernel del host —OpenVZ, LXC—, el primer comando falla con RTNETLINK answers: Operation not supported. En ese caso, se utiliza la implementación en userspace wireguard-go. Verifique primero con sudo modprobe wireguard && echo ok.
Generar claves sin filtrarlas
Un /etc/wireguard/server.key con permisos de lectura para todos equivale a no tener VPN. La configuración estándar de umask 077 && wg genkey | sudo tee ... no es fiable, ya que sudo aplica su propio umask al archivo que crea tee. Establezca el modo de forma explícita.
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.keyGenere el par de claves del cliente de la misma manera. 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/32chmod 600 it; 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, con la máscara de toda la subred VPN. Elija un rango que no se encuentre en redes públicas; 192.168.1.0/24 colisiona con la mitad de los routers domésticos detrás de los cuales se conectan sus clientes, y el túnel perderá la conectividad silenciosamente ante la ruta local.
El AllowedIPs de un peer en el lado del server es un /32, la única dirección de túnel que posee ese cliente. Si asigna la misma IP permitida a dos peers, la configuración se moverá al último que se haya configurado y el primero dejará de recibir tráfico sin mostrar ningún error. Deje SaveConfig sin configurar, o wg-quick down sobrescribirá este archivo desde el estado actual.
Convierta el equipo en un router
Un servidor Linux descarta los paquetes que no están dirigidos a él. Por defecto, el reenvío (forwarding) y el NAT de origen (source NAT) no están habilitados.
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_forwardUn sysctl -w básico funciona hasta el próximo reinicio y luego deja de funcionar sin avisar. El NAT requiere la interfaz de egress —la NIC que tiene acceso a internet, no wg0. No asuma que es eth0; obtenga el nombre de su interfaz mediante ip route show default, ya que las imágenes actuales utilizan nombres como enp1s0 o ens3.
Firewall: the port, and the forward path
One nftables file covers filter and NAT. Write /etc/nftables.conf — it flushes the existing ruleset, so skip this on a box already managed by ufw or 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
}
}Apply it with sudo systemctl enable --now nftables, keeping a second SSH session open: policy drop plus a typo in the SSH rule locks you out of your own server. Note what the forward chain does not allow — wg0 to wg0. Peers reach the internet, not each other; add iifname "wg0" oifname "wg0" accept for a peer-to-peer VPN. The same chain governs what a peer may touch on the server itself, which matters when the box doubles as a remote development box running Claude Code in tmux and you would rather not expose that side of it publicly.
On a ufw box: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" in /etc/default/ufw, and a *nat POSTROUTING MASQUERADE rule at the top of /etc/ufw/before.rules.
Configúrelo con systemd
sudo systemctl enable --now wg-quick@wg0
sudo wg showwg-quick crea la interfaz, añade las direcciones e instala las rutas derivadas de AllowedIPs. enable --now es la parte crítica: una ejecución manual de wg-quick up wg0 se pierde tras el siguiente reinicio, y las actualizaciones del kernel requieren reiniciar el sistema.
La configuración del cliente y el ajuste que 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 = 25AllowedIPs realiza dos funciones distintas a la vez; confundirlas es la causa de la mayoría de las dudas sobre WireGuard.
En sentido de salida (outbound) es una tabla de enrutamiento. Un paquete cuyo destino coincide con el AllowedIPs de un peer se cifra y se envía a dicho peer. 0.0.0.0/0, ::/0 envía todo a través del túnel —un túnel completo, con el servidor como ruta predeterminada—. Un túnel dividido (split tunnel) es una lista más restringida: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 transporta el tráfico VPN más una red privada detrás del servidor, y todo lo demás mantiene su ruta local. Esa lista restringida es lo que permite mantener servicios fuera de internet pública por completo —una instancia privada de Nextcloud en un VPS vinculada a la dirección del túnel, o las VMs de un laboratorio de virtualización anidada que se ejecutan en la misma máquina— accesibles para los peers e invisibles para todos los demás.
En sentido de entrada (inbound) es una lista de control de acceso. Se descarta un paquete descifrado de un peer cuya dirección de origen no esté en el AllowedIPs de ese peer. Por eso el servidor incluye 10.8.0.2/32 para el portátil: una entrada de 0.0.0.0/0 allí permitiría que ese cliente suplantara cualquier dirección en el túnel.
PersistentKeepalive es para clientes detrás de un NAT, donde el router mantiene el mapeo UDP abierto solo mientras fluyen paquetes. Cuando este expira, el servidor ya no puede alcanzar al cliente. PersistentKeepalive = 25 mantiene el mapeo abierto; configúralo en el cliente, no en un servidor con IP pública.
DNS, y la filtración que nadie nota
Con AllowedIPs = 0.0.0.0/0 y sin la línea DNS =, el cliente mantiene el resolver obtenido 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 por defecto, por lo que las consultas DNS salen por el enlace local en texto plano mientras el resto del tráfico viaja por el túnel. El tráfico es privado; la lista de nombres no lo es.
Dos opciones válidas. Apuntar DNS a un resolver público (DNS = 9.9.9.9) para que las consultas viajen por el túnel y salgan desde su servidor, aunque ese resolver las seguirá viendo. O ejecutar unbound o dnsmasq vinculados a 10.8.0.1, configurar DNS = 10.8.0.1 y añadir udp dport 53 iifname "wg0" accept a la cadena input —si configura esa línea y olvida el resolver, nada se resolverá.
En clientes Linux, wg-quick aplica DNS mediante resolvconf; si falta, obtendrá resolvconf: command not found. Instale openresolv o configure PostUp = resolvectl dns %i 10.8.0.1 en un cliente systemd-resolved.
Agregar y eliminar peers sin desconectar 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 luego recargue el conjunto de peers en ejecución.
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip muestra la configuración sin las claves wg-quick-only (Address, DNS, PostUp), y syncconf aplica los cambios manteniendo las sesiones activas. Solo actualiza los peers: si cambia un Address, seguirá siendo necesario realizar un down/up completo. Revoque con sudo wg set wg0 peer <public key> remove y luego elimine el bloque del archivo para evitar que se recupere en la próxima recarga.
Modos de fallo y los mensajes que verá
El handshake nunca se completa. wg show muestra al par sin latest handshake, y el cliente registra:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)No llega nada o no se acepta nada. Verifique lo siguiente: ¿está el puerto UDP 51820 abierto en el firewall de la VPS y en el firewall de red de su proveedor (un control separado en la mayoría de los paneles)? ¿son correctos la dirección y el puerto de Endpoint? ¿están las llaves cruzadas? La llave en el bloque [Peer] del cliente debe ser la llave pública del servidor, y viceversa; pegar una llave privada o la propia llave pública del cliente produce este síntoma. sudo tcpdump -ni any udp port 51820 en el servidor indica si los paquetes llegan. El módulo del kernel no registra nada por defecto; los mensajes de WireGuard aparecen en dmesg solo tras activar el debug dinámico (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control), y con esto activo, un error de llave se muestra como un descarte por invalid-MAC.
El handshake funciona, pero no hay internet. ping 10.8.0.1 tiene éxito pero ping 1.1.1.1 da timeout: falta el forwarding o el NAT. Verifique que sysctl net.ipv4.ip_forward lea 1, luego observe los contadores mientras el cliente hace ping, usando sudo nft list ruleset o sudo iptables -t nat -L POSTROUTING -n -v. Un contador en cero en la regla de masquerade significa que el nombre de la interfaz de salida es incorrecto; un contador que aumenta sin recibir respuestas indica un problema con la política de la cadena forward.
Internet funciona, pero los nombres no resuelven. ping 1.1.1.1 tiene éxito y curl https://example.com devuelve Could not resolve host. La línea DNS falta, o indica un resolver que no es alcanzable desde el túnel.
Algunos sitios HTTPS se bloquean. SSH y ping funcionan bien; las páginas pesadas se detienen. Esto es el MTU de la ruta: el túnel añade overhead y algún enlace intermedio descarta los paquetes sobredimensionados sin devolver un mensaje ICMP. Reduzca MTU en el [Interface] del cliente — pruebe con 1420, luego 1380, y finalmente 1280.
La interfaz no arranca. Address already in use significa que otro proceso usa el puerto UDP 51820. Cannot find device wg0 tras un fallo en up suele significar que la configuración fue rechazada; revise journalctl -u wg-quick@wg0 -n 50.
Migración desde Streisand o OpenVPN
Streisand no tiene mantenimiento y su repositorio está archivado. Ejecutar una VPN con automatización abandonada genera problemas de seguridad a largo plazo. No existe una actualización directa (in-place upgrade) y la PKI de OpenVPN no es compatible con WireGuard. WireGuard no utiliza certificados, CA ni fechas de expiración; cada cliente obtiene un par de claves nuevo.
Realice la migración en paralelo: WireGuard en UDP 51820 puede coexistir con OpenVPN en 1194 en el mismo servidor. Instale wg0, migre los clientes uno por uno y luego detenga el servicio antiguo. El modelo de usuario/contraseña y de revocación de OpenVPN no se transfiere; si requiere cuentas o un registro de auditoría, implemente esa capa sobre WireGuard.
Backups, upgrades, and what strains at scale
/etc/wireguard es el servidor. Realice una copia de seguridad (sudo tar czf wg-backup.tgz -C /etc wireguard, modo 600, fuera del equipo) y podrá reconstruirlo en un VPS nuevo en minutos. Si pierde la clave privada del servidor, deberá reemitir la configuración de cada cliente, ya que los clientes validan la clave pública del servidor. Las actualizaciones son un apt upgrade ordinario más un reinicio para actualizaciones del kernel, y wg-quick@wg0 se reinicia automáticamente si lo ha habilitado.
El estado por par es pequeño y el cifrado se ejecuta en el kernel, por lo que el límite es la CPU y el ancho de banda de su VPS en lugar de cualquier parámetro de esta configuración; mídalo con iperf3 a través del túnel en vez de confiar en una cifra publicada. Lo que se satura al escalar es la gestión operativa. Cada par necesita una IP de túnel única, y editar manualmente sesenta bloques [Peer] es como se introducen duplicados AllowedIPs: genere las configuraciones mediante un script. Un servidor es un endpoint UDP y un punto de fallo único, 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 registre quién posee cada clave y cómo revocar una.
Todo esto requiere un equipo Linux bajo su control: una IP pública, un kernel donde pueda cargar un módulo y un firewall que gestione de extremo a extremo.
FAQ
¿Por qué el handshake de WireGuard nunca se completa?
Si wg show muestra un peer sin latest handshake, los paquetes no están llegando o no se están aceptando. Verifique el puerto UDP 51820 tanto en el firewall del VPS como en el firewall de red externo de su proveedor. Confirme el host y el puerto en Endpoint. Asegúrese de que las llaves no estén cruzadas: el bloque [Peer] del cliente debe contener la llave public del servidor. sudo tcpdump -ni any udp port 51820 en el servidor indica si los paquetes llegan. dmesg solo reporta fallos de handshake de WireGuard tras activar el debug dinámico (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control); en ese caso, un error de llaves aparece como un drop de invalid-MAC.
El túnel conecta pero no tengo internet. ¿Qué falta?
Si ping 10.8.0.1 funciona pero ping 1.1.1.1 da timeout, el problema es el forwarding o el NAT. Confirme que sysctl net.ipv4.ip_forward muestra 1 y que está configurado en /etc/sysctl.d/, no solo mediante un sysctl -w que se pierde al reiniciar. Luego, verifique que la regla de masquerade use la interfaz de salida real de ip route show default (enp1s0 o ens3, raramente eth0).
¿Necesito la línea DNS = en mi configuración de cliente?
Con un túnel completo y sin la línea DNS =, el cliente mantiene el resolver de la red local. Esas consultas salen en texto plano por el enlace local mientras el resto del tráfico va por el túnel. Apunte DNS a un resolver público, o ejecute unbound/dnsmasq vinculado a 10.8.0.1 y abra el puerto udp dport 53 iifname "wg0" en la cadena input.
¿Qué controla realmente AllowedIPs?
Tiene dos funciones. En el sentido de salida (outbound) actúa como tabla de enrutamiento: el tráfico que coincide con el AllowedIPs de un peer se cifra y se envía a dicho peer. En el sentido de entrada (inbound) actúa como una lista de control de acceso: se descarta cualquier paquete descifrado cuya fuente esté fuera del AllowedIPs de ese peer. Por esto, el servidor lista un /32 por cliente, mientras que el cliente puede listar 0.0.0.0/0.
¿Funcionará WireGuard en cualquier VPS?
En un VPS KVM funciona con el módulo del kernel sin configuración adicional. En virtualización por contenedores que comparten el kernel del host, como OpenVZ o LXC, modprobe wireguard falla con Operation not supported y la alternativa es la implementación en userspace wireguard-go. Ejecute sudo modprobe wireguard && echo ok antes que cualquier otra cosa.