SSD Nodes Learn 8GB de RAM — $66/ano
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-02

wg-easy no Docker: WireGuard com interface web

Configure o wg-easy com Docker Compose, incluindo portas, NET_ADMIN, sysctls necessários e QR codes para conectar celulares à VPN sem editar arquivos.

O que você está configurando

wg-easy é o WireGuard com uma interface web, executado como um único contêiner Docker. Ele gerencia a interface WireGuard para você e adiciona uma interface no navegador para criar clientes. Cada cliente criado recebe um arquivo de configuração e um código QR. Assim, um telefone entra na VPN apontando a câmera para a tela.

O túnel em si é um WireGuard comum. O módulo do kernel transporta os pacotes, portanto a taxa de transferência é a mesma de uma configuração feita manualmente. O benefício é o gerenciamento do ciclo de vida dos clientes: adicionar, desabilitar e excluir peers sem editar um arquivo de configuração via SSH. A desvantagem é não ter controle direto desse arquivo, que é o assunto de da configuração manual do WireGuard em um VPS.

Você precisa de um VPS KVM com um endereço IPv4 público, Docker Engine com o plugin Compose e acesso como root. A virtualização de contêineres que compartilha o kernel do host, como OpenVZ ou LXC, geralmente não consegue carregar o módulo WireGuard, e o contêiner falha ao ativar a interface.

A versão 15 removeu as configurações do ambiente

A maioria dos guias disponíveis foi escrita para o wg-easy 14, no qual você definia WG_HOST como o endereço do servidor e PASSWORD_HASH como um hash bcrypt da senha de administrador, ambos como variáveis de ambiente. A versão 15 foi reescrita. As notas oficiais de migração informam claramente que a v15 não usa as mesmas variáveis de ambiente da v14 e que a maioria delas foi movida para o painel de administração da interface web.

Portanto, WG_HOST e PASSWORD_HASH não fazem mais nada. Se você copiar um arquivo compose antigo, o contêiner será iniciado, ignorará essas linhas e solicitará que você crie uma conta de administrador no navegador. Isso não é um bug. Esse é o novo fluxo de configuração.

Em julho de 2026, a tag principal a fixar é 15. Fixe a versão principal em vez de usar latest, porque uma atualização de versão principal altera o formato da configuração no disco e não será revertida corretamente.

O arquivo do compose

Crie um diretório para a pilha e grave nele o arquivo oficial do compose. Este é o arquivo upstream, sem alterações.

sudo mkdir -p /etc/docker/containers/wg-easy
sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml \
  https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml

O conteúdo é semelhante a este:

volumes:
  etc_wireguard:

services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:15
    container_name: wg-easy
    networks:
      wg:
        ipv4_address: 10.42.42.42
        ipv6_address: fdcc:ad94:bacf:61a3::2a
    volumes:
      - etc_wireguard:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    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:
  wg:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 10.42.42.0/24
        - subnet: fdcc:ad94:bacf:61a3::/64

etc_wireguard é um volume nomeado que armazena a chave do servidor e todos os clientes que você criar. Faça backup desse volume. Caso contrário, uma recriação eliminará todos os seus peers. Se preferir ver esses arquivos no sistema de arquivos do host, substitua-o por uma montagem bind e leia a diferença entre montagens bind e volumes nomeados antes de fazer isso, pois as permissões se comportam de forma diferente.

Por que ele precisa de NET_ADMIN, SYS_MODULE e dos sysctls

Um container não pode acessar a pilha de rede por padrão, e cada uma destas linhas remove um bloqueio específico.

NET_ADMIN permite que o container crie a interface wg0, atribua um endereço a ela e grave rotas. Sem essa permissão, o container inicia e depois falha ao ativar a interface, porque ip link add wg0 type wireguard retorna Operation not permitted.

SYS_MODULE, junto com a montagem somente leitura de /lib/modules, permite que o container carregue o módulo de kernel WireGuard caso o host ainda não o tenha carregado. O módulo reside no kernel do host, não dentro da imagem. Por isso, o diretório do host precisa estar visível. Em um kernel moderno, o módulo geralmente já está integrado, e você pode confirmar isso com sudo modprobe wireguard && echo ok no host.

net.ipv4.ip_forward=1 faz o kernel encaminhar pacotes que não são destinados ao próprio host. Sem essa configuração, o cliente se conecta, o handshake é concluído e, depois, todos os pacotes para a internet são descartados. Assim, ping 1.1.1.1 expira enquanto a VPN aparenta estar conectada.

net.ipv4.conf.all.src_valid_mark=1 é o parâmetro que mais surpreende. O WireGuard marca os próprios pacotes de saída para que eles não sejam roteados de volta para o túnel. A filtragem estrita do caminho reverso identifica um pacote cujo endereço de origem não corresponde à rota esperada e o descarta. Este sysctl instrui o kernel a aceitar pacotes marcados, evitando que um túnel completo interrompa o próprio funcionamento.

Inicie-o e crie a conta de administrador

cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -f

Use docker compose up e docker compose down, não start e stop. O projeto upstream avisa que executar start em um container criado com configurações diferentes deixa a rede em um estado inconsistente. Se quiser que a stack volte após uma reinicialização, restart: unless-stopped já cobre esse caso, e o comportamento de inicialização dos serviços do compose explica o que essa política garante e o que ela não garante.

A interface web escuta na porta TCP 51821. Na primeira visita, ela exibe uma página de configuração na qual você cria a conta de administrador e confirma o endereço do host que os clientes usarão para acessar o servidor. Esse endereço do host termina na linha Endpoint de toda configuração de cliente. Portanto, ele deve ser o IP público ou o nome DNS da VPS. Se estiver incorreto, o código QR fornecido ao telefone apontará para um destino inacessível, e o handshake nunca será concluído.

Há mais um detalhe sobre essa porta: o wg-easy 15 recusa HTTP simples, a menos que você defina INSECURE=true. Acessá-lo por HTTPS com um certificado não confiável ou terminar o TLS em um proxy reverso à frente dele são opções válidas. Acessá-lo por http:// com as configurações padrão não é.

Não publique a porta da interface na internet

O arquivo compose publica a porta 51821 em todas as interfaces. Essa é uma página de login de uma máquina que pode rotear seu tráfego e não deve ficar aberta para a internet. Publicar uma porta no Docker grava regras na cadeia DOCKER, que é avaliada antes do ufw. Por isso, uma regra deny do ufw não fecha essa porta. Vale entender essa armadilha por si só, e por que as portas publicadas pelo Docker ignoram o ufw explica o assunto em detalhes.

A correção simples é vincular a interface ao loopback e acessá-la por um túnel SSH:

    ports:
      - "51820:51820/udp"
      - "127.0.0.1:51821:51821/tcp"
    environment:
      - INSECURE=true

Depois, no seu laptop:

ssh -L 51821:127.0.0.1:51821 youruser@your.server.address

Abra http://127.0.0.1:51821 no navegador do seu laptop. O tráfego é criptografado pelo SSH, a porta não responde a nenhum outro host, e INSECURE=true é seguro neste caso porque o trecho HTTP sem criptografia nunca sai da interface de loopback.

Abra a porta UDP 51820 e verifique os dois firewalls

O WireGuard precisa que a porta UDP 51820 esteja acessível pela internet. O Docker publica essa porta, mas muitos provedores colocam um firewall de rede separado na frente da VPS, que não é conhecido pelo Docker. Abra a porta nos dois locais. Se você gerencia o firewall do host com ufw, as regras básicas do ufw para uma VPS são o caminho mais curto do que escrever regras do nftables manualmente.

Verifique se o contêiner está realmente escutando:

sudo ss -ulnp | grep 51820

Você deverá ver um socket UDP em estado de escuta. Se nada aparecer nessa linha, o contêiner não ativou a interface, e sudo docker compose logs wg-easy informará o motivo.

Crie um cliente e leia o QR code no telefone

Na interface, crie um cliente e dê a ele um nome que você reconhecerá depois, como o dispositivo ao qual ele pertence. O wg-easy aloca o próximo endereço de túnel livre e gera o par de chaves para você. Cada linha de cliente oferece um código QR e um arquivo .conf para download.

Instale o aplicativo oficial do WireGuard no telefone, escolha adicionar um túnel a partir de um código QR e aponte a câmera para o código na tela. O túnel aparecerá com o nome que você digitou. Ative-o. A linha do cliente na interface começará a mostrar contadores de transferência e o horário do handshake mais recente.

Um cliente que não mostra nenhum handshake depois de ser ativado não está alcançando o servidor. Isso indica um problema com o UDP 51820, seja no firewall do provedor, seja no endereço do endpoint incorporado à configuração. Um cliente que mostra um handshake, mas não tem acesso funcional à internet, indica um problema com o encaminhamento ou com o DNS.

Em um desktop, baixe o arquivo .conf e importe-o para o cliente WireGuard em vez de redigitá-lo. A chave privada nesse arquivo é gerada uma vez e exibida uma vez. Trate o arquivo da mesma forma que você trata uma chave privada do SSH.

Quando superar a interface

O wg-easy é a ferramenta certa enquanto seus pares forem pessoas e telefones. A interface é mais rápida do que editar arquivos de configuração, e revogar um telefone perdido exige apenas um clique.

Você atingirá os limites quando quiser algo que a interface não represente. O roteamento de site para site, em que o AllowedIPs de um par abrange uma sub-rede remota inteira em vez de um único endereço, costuma ser o primeiro obstáculo. Túneis divididos com regras de roteamento por par ou uma configuração gerada pela sua ferramenta de provisionamento são os próximos. Nesse ponto, a configuração escrita manualmente não é mais difícil, apenas é diferente, e o guia básico do WireGuard mostra o mesmo túnel criado a partir de wg0.conf. Se preferir deixar de executar completamente o plano de controle, WireGuard em comparação com Tailscale apresenta a opção gerenciada.

Se a sintaxe do compose acima era a parte desconhecida, e não o WireGuard, Noções básicas de Docker Compose em um VPS explica o formato do arquivo e os comandos usados no dia a dia.

FAQ

Por que o wg-easy ignora minhas variáveis WG_HOST e PASSWORD_HASH?

Essas variáveis pertencem ao wg-easy 14. A versão 15 foi reescrita, e o projeto upstream moveu quase toda a configuração para o painel de administração na interface web. O container não lê nenhuma das duas variáveis. Por isso, ele inicia normalmente e solicita a criação de uma conta de administrador no primeiro acesso. Defina o endereço do host voltado para os clientes nessa página de configuração.

Preciso de SYS_MODULE se meu kernel já tiver WireGuard?

Não. SYS_MODULE e a montagem /lib/modules existem para que o container possa carregar o módulo quando o host não o tiver. Em um host no qual sudo modprobe wireguard já funciona, esse recurso não é usado. Removê-lo é uma medida razoável de hardening, e NET_ADMIN continua sendo necessário de qualquer forma.

O cliente se conecta, mas não há internet. O que está errado?

Um handshake sem tráfego quase sempre indica um problema de encaminhamento. Confirme se net.ipv4.ip_forward=1 e net.ipv4.conf.all.src_valid_mark=1 ainda estão no arquivo compose, pois uma cópia editada manualmente costuma perdê-los. Se o encaminhamento estiver habilitado, verifique o servidor DNS recebido pelo cliente. Um túnel que envia todo o tráfego pela VPN, mas aponta para um servidor DNS que não consegue mais alcançar, aparece exatamente como uma conexão inativa no navegador.

Como faço backup dos meus clientes?

Tudo fica no volume nomeado etc_wireguard, em um arquivo wg0.json. A interface também tem um botão de backup que exporta os mesmos dados. Copie esse arquivo para fora do servidor antes de qualquer atualização. A restauração é feita por upload durante a etapa de configuração em um container novo.

Posso executar o wg-easy atrás de um proxy reverso?

Sim. Coloque o proxy na frente da porta TCP 51821, encerre o TLS nele e defina INSECURE=true no container para que ele aceite a conexão HTTP sem TLS do proxy. Mantenha a porta UDP 51820 publicada diretamente, pois o tráfego da VPN usa UDP e não passa por um proxy HTTP.