VPNSmith
self-host-vpnINFO

Monitoriza o teu VPS VPN com Prometheus + Grafana (2026)

Stack completo de monitorização: node_exporter, wireguard_exporter, dashboards Grafana, alertas de uptime/throughput, integração com webhook do Discord. Configuração em 30 minutos.

Por Eric Gerard · Fondateur · VPNSmith — Spécialiste self-host VPN & VPS GDPR9 min de leituraPhoto via Unsplash

O teu túnel WireGuard está a funcionar. Mas perguntas-te: quantos Mbps atinge no pico? Que peers se conectam e quando? O VPS da Contabo realmente atinge os 200 Mbps anunciados? Poderias abrir um terminal e tail -f journalctl sempre, ou instalar uma stack Prometheus + Grafana em 30 minutos que te dá respostas visuais, alertas sobre incidentes e mantém histórico.

Este guia implementa uma configuração de nível de produção para um VPS S da Contabo. node_exporter para o sistema, wireguard_exporter para os peers, Prometheus para armazenamento, Grafana para visualização, Alertmanager + webhook do Discord para alertas.

Porque monitorizar uma VPN auto-hospedada (e não apenas olhar para wg show)

Executar wg show num terminal dá-te o estado instantâneo: peers conectados, último handshake, total de bytes trocados desde o início do túnel. Útil para um debug pontual, mas nunca te dirá quanto de largura de banda usaste ontem às 22h, qual peer monopoliza o teu upload por três dias, ou se a tua latência Paris-Nuremberga duplicou silenciosamente devido a um peering BGP quebrado no upstream da Contabo. Para isso, precisas de séries temporais: registar uma métrica a cada 15 segundos, manter 30 dias de histórico e ser capaz de consultar "throughput médio entre as 18h e as 22h nas últimas duas semanas úteis". É exatamente isso que o Prometheus faz, e é exatamente por isso que o Grafana existe sobre ele.

Outra razão subestimada são os alertas proativos. Sem monitorização, descobres um túnel quebrado quando um peer te envia uma mensagem no Telegram dizendo "não funciona para mim". Com Alertmanager + um webhook do Discord, recebes a notificação no minuto seguinte à falha, geralmente antes que os teus utilizadores percebam. Os tipos de incidentes que isto deteta precocemente: um OOM no processo WireGuard desencadeado por um peer mal configurado a abrir centenas de conexões paralelas, um disco a encher-se silenciosamente devido a uma rotação de logs quebrada, ou um corte no upstream durante uma manutenção não anunciada. Sem monitorização, diagnosticar esses casos é uma luta.

Finalmente, esta é a base da transparência para os utilizadores. Se hospedas uma VPN para a tua família ou empresa, ser capaz de lhes mostrar um dashboard público de leitura apenas com uptime + largura de banda consumida dá credibilidade que ninguém oferece no lado do consumidor (a NordVPN não publica o seu uptime, a ExpressVPN também não).

Arquitetura

Tudo corre no mesmo VPS da Contabo. Três processos systemd:

  • node_exporter (porta 9100): métricas do sistema (CPU, RAM, disco, rede)
  • wireguard_exporter (porta 9586): métricas dos peers do WireGuard (handshake, bytes_in/out, last_seen)
  • prometheus (porta 9090): recolhe dados dos exporters a cada 15s, retém 30 dias
  • grafana (porta 3000): dashboards
  • alertmanager (porta 9093): regras de alerta + webhook do Discord

Custo: ~250 MB RAM, ~3% CPU constante num VPS de 4 vCPU. Sem impacto perceptível no túnel.

Passo 1 — Instalar node_exporter

# No VPS, com sudo
sudo useradd --no-create-home --shell /usr/sbin/nologin node_exporter

cd /tmp
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
tar xzf node_exporter-1.8.2.linux-amd64.tar.gz
sudo cp node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/
sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter

unidade systemd /etc/systemd/system/node_exporter.service:

[Unit]
Description=Node Exporter
After=network.target

[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter --web.listen-address=127.0.0.1:9100

[Install]
WantedBy=multi-user.target

Importante: ligamos em 127.0.0.1 (não 0.0.0.0) — sem exposição pública. O Prometheus acede via localhost.

sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
curl -s http://127.0.0.1:9100/metrics | head -20
# Deve retornar métricas como "node_cpu_seconds_total"

Passo 2 — Instalar prometheus-wireguard-exporter

O exporter que usamos: github.com/MindFlavor/prometheus_wireguard_exporter (Rust, ~3 MB binário, recolhe a saída de wg show).

cd /tmp
wget https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/download/3.6.6/prometheus_wireguard_exporter_3.6.6_linux_amd64.tar.gz
tar xzf prometheus_wireguard_exporter_3.6.6_linux_amd64.tar.gz
sudo cp prometheus_wireguard_exporter /usr/local/bin/
sudo chmod +x /usr/local/bin/prometheus_wireguard_exporter

unidade systemd /etc/systemd/system/wireguard_exporter.service:

[Unit]
Description=WireGuard Prometheus Exporter
After=network.target wg-quick@wg0.service
Requires=wg-quick@wg0.service

[Service]
User=root
ExecStart=/usr/local/bin/prometheus_wireguard_exporter -a 127.0.0.1 -p 9586 -n /etc/wireguard/wg0.conf
Restart=on-failure

[Install]
WantedBy=multi-user.target

A flag -n /etc/wireguard/wg0.conf permite que o exporter use nomes de peers (comentados no .conf) como etiquetas do Prometheus. Mais legível no Grafana do que chaves públicas.

sudo systemctl daemon-reload
sudo systemctl enable --now wireguard_exporter
curl -s http://127.0.0.1:9586/metrics | grep wireguard_
# Deve retornar wireguard_sent_bytes_total, wireguard_received_bytes_total, etc.

Passo 3 — Instalar Prometheus

sudo useradd --no-create-home --shell /usr/sbin/nologin prometheus
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /etc/prometheus /var/lib/prometheus

cd /tmp
wget https://github.com/prometheus/prometheus/releases/download/v2.55.1/prometheus-2.55.1.linux-amd64.tar.gz
tar xzf prometheus-2.55.1.linux-amd64.tar.gz
sudo cp prometheus-2.55.1.linux-amd64/prometheus /usr/local/bin/
sudo cp prometheus-2.55.1.linux-amd64/promtool /usr/local/bin/
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool

Configuração /etc/prometheus/prometheus.yml:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - 127.0.0.1:9093

rule_files:
  - "alert_rules.yml"

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: [127.0.0.1:9090]

  - job_name: node
    static_configs:
      - targets: [127.0.0.1:9100]
        labels:
          instance: vps-contabo-nuremberg

  - job_name: wireguard
    static_configs:
      - targets: [127.0.0.1:9586]
        labels:
          instance: vps-contabo-nuremberg

Regras de alerta /etc/prometheus/alert_rules.yml:

groups:
  - name: vpn_alerts
    interval: 30s
    rules:
      - alert: HighCPU
        expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        annotations:
          summary: "CPU > 80% em {{ $labels.instance }}"

      - alert: WireGuardPeerDown
        expr: time() - wireguard_latest_handshake_seconds > 600
        for: 5m
        annotations:
          summary: "Peer WG {{ $labels.peer }} não fez handshake em >10min"

      - alert: HighBandwidth
        expr: rate(node_network_transmit_bytes_total{device="eth0"}[5m]) * 8 > 180000000
        for: 10m
        annotations:
          summary: "Largura de banda > 180 Mbps em eth0 — a aproximar-se do limite da Contabo"

      - alert: DiskAlmostFull
        expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 15
        for: 10m
        annotations:
          summary: "Disco < 15% livre em {{ $labels.instance }}"

unidade systemd /etc/systemd/system/prometheus.service:

[Unit]
Description=Prometheus
After=network.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
  --config.file /etc/prometheus/prometheus.yml \
  --storage.tsdb.path /var/lib/prometheus/ \
  --storage.tsdb.retention.time=30d \
  --web.listen-address=127.0.0.1:9090

[Install]
WantedBy=multi-user.target
sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml /etc/prometheus/alert_rules.yml
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
sudo systemctl status prometheus

Passo 4 — Instalar Grafana

sudo apt install -y apt-transport-https software-properties-common
sudo mkdir -p /etc/apt/keyrings/
wget -q -O - https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/grafana.gpg
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt update
sudo apt install -y grafana

Edita /etc/grafana/grafana.ini para ligar apenas ao localhost:

[server]
http_addr = 127.0.0.1
http_port = 3000
sudo systemctl enable --now grafana-server

Passo 5 — Aceder ao Grafana de forma segura

Em vez de abrir a porta 3000 publicamente, túnel SSH a partir do teu portátil:

ssh -L 3000:127.0.0.1:3000 eric@vpn.example.com

Depois abre http://127.0.0.1:3000 no teu navegador local. Login admin / admin (muda imediatamente).

Alternativa: aceder via o túnel WireGuard. Se o teu VPS é 10.66.66.1 no lado da VPN, acede a http://10.66.66.1:3000 a partir de qualquer peer conectado. Nenhuma porta exposta à Internet pública.

Passo 6 — Configurar a fonte de dados do Prometheus

Um corredor de sala de servidores
Um corredor de sala de servidores

No Grafana:

  1. Definições → Fontes de Dados → Adicionar fonte de dados → Prometheus
  2. URL: http://127.0.0.1:9090
  3. Guardar & Testar → "Fonte de dados está a funcionar"

Passo 7 — Importar dashboards

Dashboards prontos a usar:

  • Node Exporter Full: ID 1860 no grafana.com — métricas completas do sistema
  • WireGuard: ID 12557 ou mais recente — peers, largura de banda, last_seen

No Grafana: Dashboards → Importar → colar o ID → escolher a fonte de dados Prometheus → Importar.

Para um dashboard personalizado de VPS-VPN, publicámos o nosso JSON no GitHub (link no repositório VPNSmith). Contém:

  • Visão geral: RAM/CPU/disco/uptime
  • Largura de banda eth0 (in/out, 1h/24h/7d)
  • Peers do WireGuard: nome, último handshake, bytes trocados
  • Principais peers por uso
  • Alertas ativos

Passo 8 — Alertas do Discord via webhook

Instalar Alertmanager:

cd /tmp
wget https://github.com/prometheus/alertmanager/releases/download/v0.27.0/alertmanager-0.27.0.linux-amd64.tar.gz
tar xzf alertmanager-0.27.0.linux-amd64.tar.gz
sudo cp alertmanager-0.27.0.linux-amd64/alertmanager /usr/local/bin/
sudo cp alertmanager-0.27.0.linux-amd64/amtool /usr/local/bin/
sudo useradd --no-create-home --shell /usr/sbin/nologin alertmanager
sudo mkdir -p /etc/alertmanager /var/lib/alertmanager
sudo chown alertmanager:alertmanager /etc/alertmanager /var/lib/alertmanager

Configuração /etc/alertmanager/alertmanager.yml:

global:
  resolve_timeout: 5m

route:
  receiver: discord

receivers:
  - name: discord
    webhook_configs:
      - url: 'https://discord.com/api/webhooks/XXXXXXX/YYYYYYY?wait=true'
        send_resolved: true

Gera o webhook do Discord: Definições do Servidor → Integrações → Webhooks → Novo Webhook. Copia o URL. O formato bruto do Prometheus não é bonito no Discord — usamos a ponte alertmanager-discord ou um parser simples que produz embeds limpos no Discord.

unidade systemd /etc/systemd/system/alertmanager.service:

[Unit]
Description=Alertmanager
After=network.target

[Service]
User=alertmanager
Group=alertmanager
Type=simple
ExecStart=/usr/local/bin/alertmanager \
  --config.file=/etc/alertmanager/alertmanager.yml \
  --storage.path=/var/lib/alertmanager \
  --web.listen-address=127.0.0.1:9093

[Install]
WantedBy=multi-user.target
sudo chown alertmanager:alertmanager /etc/alertmanager/alertmanager.yml
sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager

Consultas úteis para um VPS VPN

No Grafana, estas consultas Prometheus são as mais úteis no dia-a-dia:

Largura de banda total do WireGuard (Mbps):

rate(wireguard_sent_bytes_total[5m]) * 8 / 1e6 + rate(wireguard_received_bytes_total[5m]) * 8 / 1e6

Top 5 peers por bytes recebidos (24h):

topk(5, increase(wireguard_received_bytes_total[24h]))

Último handshake por peer (humanizado):

time() - wireguard_latest_handshake_seconds

Utilização de CPU %:

100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

Uso de disco %:

100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100

Backup e retenção

O Prometheus mantém 30 dias por padrão (--storage.tsdb.retention.time=30d). Além disso, ou aumentas a retenção (adicionas disco) ou fazes downsample com Thanos ou VictoriaMetrics (exagero para um VPS pessoal).

/var/lib/prometheus/ consome ~50-100 MB por dia para esta stack. Em 30 dias, ~2-3 GB no máximo. O teu VPS da Contabo tem 50 GB NVMe, margem de sobra.

Reforço adicional

  • fail2ban no Grafana: cria um filtro para tentativas de login. 5× autenticação falhada → ban de 15 minutos.
  • Proxy reverso com autenticação básica: Caddy ou Nginx à frente do Grafana se quiseres partilhar o dashboard com um colega sem túnel SSH. Mas sempre prefere o túnel WireGuard se possível.
  • TLS: se expuseres o Grafana fora da LAN/túnel, certbot + Caddy em 5 minutos.

Veredicto

Com esta stack, obténs visibilidade em tempo real de tudo o que passa na tua VPN VPS. Vês imediatamente quando um peer transfere 200 GB durante a noite (provavelmente um ISO Linux, ou backup Plex), quando o CPU dispara (frequentemente um OOM mal configurado), quando um peer não fez handshake em 1h (cliente morto ou ausente).

São 30 minutos de configuração uma vez, para uma paz de espírito duradoura num VPS S da Contabo com o seu link anunciado de 200 Mbit/s.

Para começar:

O VPS que recomendamos: Contabo VPS S Cloud — 4.99 €/mês, link anunciado de 200 Mbit/s, jurisdição GDPR alemã. Vê a nossa revisão completa do VPS Contabo.

★ Datacenter GDPR em Nuremberg · ✓ IPv4 dedicado incluído · 200+ Mbps garantidos

Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region