Como configurar WireGuard em uma VPS Linux
Aprenda a configurar WireGuard no Linux: geração de chaves, wg0.conf, IP forwarding e NAT. Evite erros de handshake e problemas em virtualização OpenVZ/LXC.
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 a virtualização primeiro
O WireGuard requer um kernel que permita o carregamento de módulos. Em uma VPS KVM, ele funciona nativamente. Em virtualização por containers que compartilham o kernel do host — OpenVZ, LXC — o primeiro comando falha com RTNETLINK answers: Operation not supported. Nesses casos, a alternativa é a implementação em userspace wireguard-go. Verifique primeiro com sudo modprobe wireguard && echo ok.
Gere chaves sem vazamento de dados
Um /etc/wireguard/server.key com permissão de leitura para todos equivale a não ter VPN. A prática comum de usar umask 077 && wg genkey | sudo tee ... não é confiável, pois o sudo aplica o próprio umask ao arquivo que o tee cria. Defina o modo explicitamente.
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.keyGere o par do cliente da mesma forma. O wg genpsk adiciona uma pre-shared key opcional, com uma linha em cada config.
A interface do 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; um aviso de inicialização informando que o arquivo está acessível para todos significa que você pulou essa etapa. Address é o endereço do servidor dentro do túnel, contendo a máscara de toda a sub-rede VPN. Escolha um intervalo que não ocorra em redes comuns — 192.168.1.0/24 colide com metade dos roteadores domésticos onde seus clientes estão conectados, e o túnel perde o tráfego silenciosamente para a rota local.
O AllowedIPs de um peer no lado do server é um /32, o único endereço de túnel que o cliente possui. Se você atribuir o mesmo allowed IP para dois peers, o tráfego será direcionado para o último que foi configurado, e o primeiro parará de receber tráfego sem exibir nenhum erro. Deixe SaveConfig sem configuração, ou o wg-quick down sobrescreverá este arquivo com o estado atual.
Transforme o box em um roteador
Um servidor Linux descarta pacotes que não são endereçados a ele. O encaminhamento (forwarding) e o source NAT não vêm habilitados por padrão.
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_forwardUm sysctl -w puro funciona até o próximo reboot e para de funcionar silenciosamente. O NAT precisa da interface de egress — a NIC que acessa a internet, não a wg0. Não presuma o eth0; obtenha o seu em ip route show default, pois as imagens atuais usam nomes como enp1s0 ou ens3.
Firewall: a porta e o caminho de encaminhamento
Um arquivo nftables cobre o filtro e o NAT. Escreva /etc/nftables.conf — isso limpa o ruleset existente, portanto pule este passo em uma máquina já gerenciada pelo ufw ou 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
}
}Aplique com sudo systemctl enable --now nftables, mantendo uma segunda sessão SSH aberta: policy drop mais um erro de digitação na regra SSH bloqueia seu acesso ao servidor. Observe o que a forward chain não permite — wg0 a wg0. Os peers alcançam a internet, não uns aos outros; adicione iifname "wg0" oifname "wg0" accept para uma VPN peer-to-peer. A mesma chain governa o que um peer pode acessar no próprio servidor, o que é importante quando a máquina também serve como um ambiente de desenvolvimento remoto rodando Claude Code no tmux e você prefere não expor esse lado publicamente.
Em uma máquina com ufw: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" em /etc/default/ufw, e uma regra POSTROUTING MASQUERADE *nat no topo de /etc/ufw/before.rules.
Configure via systemd
sudo systemctl enable --now wg-quick@wg0
sudo wg showwg-quick cria a interface, adiciona os endereços e instala as rotas derivadas de AllowedIPs. enable --now é a parte crítica: uma configuração manual via wg-quick up wg0 é perdida após o próximo reboot, e atualizações de kernel exigem reboots.
A configuração do cliente e o ajuste que todos erram
[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 = 25O AllowedIPs realiza duas funções distintas ao mesmo tempo. Confundir essas funções é a causa da maioria das confusões com WireGuard.
Para saída (outbound), ele é uma tabela de roteamento. Um pacote cujo destino coincide com o AllowedIPs de um peer é criptografado e enviado para esse peer. O 0.0.0.0/0, ::/0 envia tudo pelo túnel — um túnel completo, com o servidor como rota padrão. Um split tunnel é uma lista mais restrita: o AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 transporta o tráfego da VPN mais uma rede privada atrás do servidor, enquanto todo o resto mantém sua rota local. Essa lista restrita é o que permite manter serviços totalmente fora da internet pública — uma instância privada do Nextcloud em um VPS vinculada ao endereço do túnel, ou as VMs de laboratório de virtualização aninhada rodando na mesma máquina, permanecem acessíveis aos peers e invisíveis para todos os outros.
Para entrada (inbound), ele é uma lista de controle de acesso (ACL). Um pacote descriptografado de um peer cujo endereço de origem não está no AllowedIPs desse peer é descartado. É por isso que o servidor lista o 10.8.0.2/32 para o laptop: uma entrada de 0.0.0.0/0 ali permitiria que esse cliente fizesse spoofing de qualquer endereço no túnel.
O PersistentKeepalive serve para clientes atrás de NAT, onde o roteador mantém o mapeamento UDP aberto apenas enquanto há fluxo de pacotes. Quando o tempo expira, o servidor não consegue mais alcançar o cliente. O PersistentKeepalive = 25 mantém o mapeamento aberto — configure-o no cliente, não em um servidor com IP público.
DNS, e o vazamento que ninguém nota
Com AllowedIPs = 0.0.0.0/0 e sem a linha DNS =, o cliente mantém o resolver aprendido na rede local — o roteador do café em 192.168.1.1. Essa rota é mais específica que a rota padrão, então as consultas DNS saem pelo link local em texto claro, enquanto todo o resto passa pelo túnel. O tráfego é privado; a lista de nomes não é.
Duas opções seguras. Aponte DNS para um resolver público (DNS = 9.9.9.9) e as consultas passarão pelo túnel e sairão pelo seu servidor, embora o resolver ainda as veja. Ou execute unbound ou dnsmasq vinculado ao 10.8.0.1, configure DNS = 10.8.0.1 e adicione udp dport 53 iifname "wg0" accept à chain input — configure essa linha e ignore o resolver, e nada será resolvido.
Em clientes Linux, o wg-quick aplica DNS via resolvconf; se estiver ausente, você terá resolvconf: command not found. Instale o openresolv ou configure o PostUp = resolvectl dns %i 10.8.0.1 em um cliente systemd-resolved.
Adicionando e removendo peers sem derrubar o tunnel
Reiniciar a interface para adicionar um usuário desconecta todos os usuários conectados. Adicione o bloco [Peer] ao wg0.conf e recarregue o peer set no local.
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip exibe a configuração sem as chaves wg-quick-only (Address, DNS, PostUp), e syncconf aplica a diferença mantendo as sessões ativas. Ele atualiza apenas os peers: uma alteração no Address ainda exige um down/up completo. Revogue com sudo wg set wg0 peer <public key> remove e depois delete o bloco do arquivo para evitar que ele retorne no próximo reload.
Modos de falha e as strings que você verá
O handshake nunca termina. O wg show lista o peer sem latest handshake, e o log do cliente exibe:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)Nada está chegando ou nada está sendo aceito. Verifique na ordem: a porta UDP 51820 está aberta no firewall da VPS e no firewall da rede do seu provedor (um controle separado na maioria dos painéis); o endereço e a porta do Endpoint estão corretos; as chaves coincidem. A chave no bloco [Peer] do cliente deve ser a chave pública do servidor, e vice-versa — colar uma chave privada ou a própria chave pública do cliente causa exatamente este sintoma. O comando sudo tcpdump -ni any udp port 51820 no servidor mostra se os pacotes estão chegando. O módulo do kernel não gera logs por padrão; mensagens do WireGuard aparecem no dmesg apenas após habilitar o dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) e, com isso ativo, uma incompatibilidade de chave aparece como um descarte de invalid-MAC.
O handshake funciona, mas não há internet. O ping 10.8.0.1 tem sucesso, mas o ping 1.1.1.1 sofre timeout: o encaminhamento (forwarding) ou o NAT está ausente. Verifique se o sysctl net.ipv4.ip_forward lê o 1 e monitore os contadores enquanto o cliente executa um ping, usando sudo nft list ruleset ou sudo iptables -t nat -L POSTROUTING -n -v. Zero pacotes na regra de masquerade significa que o nome da interface de saída está incorreto; um contador que aumenta sem receber respostas indica um problema na política da chain FORWARD.
A internet funciona, mas os nomes não resolvem. O ping 1.1.1.1 tem sucesso e o curl https://example.com retorna Could not resolve host. A linha DNS está ausente ou aponta para um resolver inacessível de dentro do túnel.
Alguns sites HTTPS travam. SSH e ping funcionam normalmente; páginas grandes travam. Isso é o path MTU: o túnel adiciona overhead e algum link no caminho descarta os pacotes superdimensionados sem retornar uma mensagem ICMP. Diminua o MTU no [Interface] do cliente — tente 1420, depois 1380, depois 1280.
A interface recusa a iniciar. Address already in use significa que outro processo já utiliza a porta UDP 51820. Cannot find device wg0 após uma falha no up geralmente significa que a configuração foi rejeitada; verifique o journalctl -u wg-quick@wg0 -n 50.
Migrando do Streisand ou OpenVPN
O Streisand não recebe manutenção e seu repositório foi arquivado. Rodar uma VPN em uma automação abandonada gera problemas de segurança a longo prazo. Não existe upgrade no local (in-place upgrade) e a PKI do OpenVPN não é compatível: o WireGuard não utiliza certificados, CA ou expiração, portanto cada cliente recebe um novo par de chaves.
Realize a migração em paralelo — o WireGuard no UDP 51820 pode coexistir com o OpenVPN na porta 1194 no mesmo servidor. Instale o wg0, migre os clientes um por um e depois pare o serviço antigo. O modelo de usuário/senha e de revogação do OpenVPN não é transferido; se você precisar de contas ou trilhas de auditoria, implemente essa camada acima do WireGuard.
Backups, upgrades, and o que sobrecarrega em escala
/etc/wireguard é o servidor. Faça o backup (sudo tar czf wg-backup.tgz -C /etc wireguard, modo 600, armazenado fora da máquina) e você poderá reconstruir o sistema em um novo VPS em minutos. Se perder a chave privada do servidor, todas as configurações dos clientes precisarão ser reemitidas, pois os clientes validam a chave pública do servidor. Upgrades são um apt upgrade comum mais um reboot para atualizações de kernel, e o wg-quick@wg0 reinicia sozinho se estiver habilitado.
O estado por peer é pequeno e a criptografia roda no kernel, portanto o limite é o CPU e a cota de largura de banda do seu VPS, e não algo nesta configuração — meça com iperf3 através do túnel em vez de confiar em números publicados. O que sobrecarrega em escala é a operação. Cada peer precisa de um IP de túnel único, e editar manualmente sessenta blocos [Peer] é como duplicatas de AllowedIPs surgem: gere as configurações via script. Um servidor é um endpoint UDP e um ponto único de falha, e o WireGuard não possui clustering: redundância significa um segundo servidor com suas próprias chaves. A rotação de chaves continua manual, então registre quem possui cada chave e como revogar uma delas.
Tudo isso exige uma máquina Linux sob seu controle — um IP público, um kernel onde você possa carregar um módulo e um firewall que você controle de ponta a ponta.
FAQ
Por que o handshake do WireGuard nunca termina?
wg show listando um peer sem latest handshake significa que os pacotes não estão chegando ou não estão sendo aceitos. Verifique a porta UDP 51820 no firewall da VPS e no firewall de rede separado do seu provedor. Confirme o host e a porta em Endpoint. Verifique se as chaves não estão trocadas — o bloco [Peer] do cliente deve conter a chave public do servidor. O comando sudo tcpdump -ni any udp port 51820 no servidor mostra se os pacotes chegam. O comando dmesg só reporta falhas de handshake do WireGuard após você habilitar o debug dinâmico (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control); nesse caso, um erro de chave aparece como um drop de invalid-MAC.
O túnel conecta, mas estou sem internet. O que está faltando?
ping 10.8.0.1 funcionando enquanto ping 1.1.1.1 dá timeout indica problemas de forwarding ou NAT. Confirme se sysctl net.ipv4.ip_forward mostra 1 e se isso está configurado em /etc/sysctl.d/, e não apenas com um sysctl -w que se perde após o reboot. Verifique se a regra de masquerade usa o nome da sua interface de saída real de ip route show default — enp1s0 ou ens3, raramente eth0.
Eu preciso da linha DNS = na minha configuração de cliente?
Com um túnel completo e sem a linha DNS =, o cliente mantém o resolver aprendido na rede local. Essas consultas saem em texto claro pelo link local enquanto todo o resto é tunelado. Aponte DNS para um resolver público, ou execute unbound/dnsmasq vinculado a 10.8.0.1 e abra a porta udp dport 53 iifname "wg0" na chain input.
O que o AllowedIPs controla de fato?
Ele possui duas funções. No tráfego de saída, ele é uma tabela de roteamento: o tráfego que coincide com o AllowedIPs de um peer é criptografado e enviado para esse peer. No tráfego de entrada, ele é uma lista de controle de acesso: um pacote descriptografado cuja origem está fora do AllowedIPs daquele peer é descartado. É por isso que o lado do servidor lista um /32 por cliente, enquanto o lado do cliente pode listar 0.0.0.0/0.
O WireGuard funciona em qualquer VPS?
Em uma VPS KVM, ele funciona com o módulo do kernel e sem configuração extra. Em virtualização por containers que compartilham o kernel do host, como OpenVZ ou LXC, o modprobe wireguard falha com Operation not supported e o fallback é a implementação em userspace wireguard-go. Execute sudo modprobe wireguard && echo ok antes de qualquer outra coisa.