From badd2ce030b8eee7cf79251323ed3e98282be4bf Mon Sep 17 00:00:00 2001 From: gabrieljablonski Date: Mon, 23 Feb 2026 19:18:07 -0300 Subject: [PATCH] docs: add VPN setup documentation for Baileys API using Gluetun and Mullvad --- docs/BAILEYS_VPN_SETUP.md | 170 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 docs/BAILEYS_VPN_SETUP.md diff --git a/docs/BAILEYS_VPN_SETUP.md b/docs/BAILEYS_VPN_SETUP.md new file mode 100644 index 000000000..f9f160c87 --- /dev/null +++ b/docs/BAILEYS_VPN_SETUP.md @@ -0,0 +1,170 @@ +# VPN Setup for Baileys API (Gluetun + Mullvad) + +## Problem + +Hosting providers like Hostinger have their IP ranges mass-blocked by Meta. This causes WhatsApp connections through Baileys to fail. The solution is to route **only** Baileys traffic through a VPN, keeping everything else (Rails, Sidekiq, Postgres, Redis) on the regular network. + +## Architecture + +``` +┌─────────────────────────────────────────────────────┐ +│ Docker Network (coolify) │ +│ │ +│ ┌──────────┐ ┌──────────────────────────────┐ │ +│ │ Rails │ │ Gluetun (VPN tunnel) │ │ +│ │ Sidekiq │───▶│ :3025 ──▶ Baileys API │ │ +│ └──────────┘ │ (network_mode: service) │ │ +│ │ │ │ │ │ +│ │ │ │ VPN (WireGuard) │ │ +│ │ │ ▼ │ │ +│ │ │ Mullvad SP (Brazil) │ │ +│ │ └──────────────────────────────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ Redis │◀───────│ Baileys │ │ +│ │ Postgres│ │ (via VPN)│ │ +│ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +**Gluetun** creates a WireGuard VPN tunnel. Baileys shares Gluetun's network via `network_mode: "service:gluetun"`, so all WhatsApp traffic exits through the VPN IP. Internal Docker traffic (Redis, etc.) is exempted via firewall subnet rules. + +--- + +## Step 1 — Get Mullvad Credentials + +1. Go to and generate an account (no email needed — save the 16-digit account number). +2. Add credit (€5/month, accepts card, PayPal, crypto). +3. Go to , log in, select **Linux**, click **Generate key**, choose **Brazil > São Paulo**, and download the `.conf` file. +4. From the downloaded file, note: + - `PrivateKey` — e.g. `KLaIt4oAaI6Iz4iQhSS9/0UBlbvfmG1LC/NWGXW/DH4=` + - `Address` — use **only the IPv4** address, e.g. `10.67.152.85/32` (discard the IPv6 address) + +> **Important:** Gluetun does not support IPv6. Only use the IPv4 address from the `.conf` file. + +--- + +## Step 2 — Docker Compose Changes + +### 2.1 Add the `gluetun` service (baileys-api compose) + +```yaml +gluetun: + image: qmcgaw/gluetun + restart: always + cap_add: + - NET_ADMIN + ports: + - '3025:3025' + environment: + - VPN_SERVICE_PROVIDER=mullvad + - VPN_TYPE=wireguard + - WIREGUARD_PRIVATE_KEY= + - WIREGUARD_ADDRESSES=/32 + - SERVER_COUNTRIES=Brazil + - SERVER_CITIES=Sao Paulo + - FIREWALL_OUTBOUND_SUBNETS=172.16.0.0/12,10.0.0.0/8,192.168.0.0/16 + - DNS_KEEP_NAMESERVER=on + healthcheck: + test: + - CMD-SHELL + - 'wget -qO- https://ipinfo.io/ip' + interval: 30s + timeout: 10s + retries: 5 + networks: + - coolify +``` + +Key environment variables: + +| Variable | Purpose | +|---|---| +| `FIREWALL_OUTBOUND_SUBNETS` | Allows internal Docker traffic (Redis, etc.) to bypass the VPN. Must cover all private subnets used by Docker. | +| `DNS_KEEP_NAMESERVER` | Keeps Docker's internal DNS so containers can resolve hostnames like `redis`. Without this, you get `getaddrinfo ENOTFOUND` errors. | + +> **Do NOT set `OWNED_ONLY=yes`** — Mullvad does not have owned servers in São Paulo, only rented ones. This filter would match zero servers. + +### 2.2 Modify the `baileys-api` service + +Apply these changes to the existing baileys-api service: + +```yaml +baileys-api: + # ... existing config ... + network_mode: 'service:gluetun' # Route all traffic through Gluetun + depends_on: + gluetun: + condition: service_healthy # Wait for VPN to be up + # REMOVE any 'ports' section — port 3025 is now exposed by gluetun + # REMOVE any 'networks' section — network_mode is incompatible with networks +``` + +> **`condition: service_healthy`** is critical. Without it, baileys-api starts before the VPN tunnel is established, causing Redis connection timeouts. + +### 2.3 Update `BAILEYS_PROVIDER_DEFAULT_URL` in Chatwoot + +In the Rails and Sidekiq services (or Coolify environment variables), change: + +``` +# Before +BAILEYS_PROVIDER_DEFAULT_URL=http://baileys-api:3025 + +# After +BAILEYS_PROVIDER_DEFAULT_URL=http://gluetun:3025 +``` + +Since baileys-api now shares Gluetun's network, external services must address it via `gluetun` hostname. + +### 2.4 Declare the shared network + +If the baileys-api compose is a separate stack, declare the shared network: + +```yaml +networks: + coolify: + external: true + name: coolify +``` + +--- + +## Step 3 — Verify + +After deploying, run from the server's SSH terminal: + +```bash +# Check container health +docker ps | grep -E "gluetun|baileys" + +# Get the VPN exit IP (replace with your actual gluetun container name) +docker exec wget -qO- https://ipinfo.io/ip + +# Compare with the server's real IP +curl -s https://ipinfo.io/ip +``` + +If the two IPs are **different**, the VPN is working correctly. + +--- + +## Troubleshooting + +| Symptom | Cause | Fix | +|---|---|---| +| `Redis client error` / connection timeout on Redis | Baileys starts before VPN is ready | Add `depends_on` with `condition: service_healthy` | +| `Redis client error` / connection refused | Internal Docker traffic blocked by VPN firewall | Add `192.168.0.0/16` to `FIREWALL_OUTBOUND_SUBNETS` | +| `getaddrinfo ENOTFOUND redis` | Docker DNS not working inside VPN | Set `DNS_KEEP_NAMESERVER=on` in gluetun | +| `no server found: ... city sao paulo; owned servers only` | Mullvad has no owned servers in São Paulo | Remove `OWNED_ONLY=yes` from gluetun env | +| `interface address is IPv6 but IPv6 is not supported` | IPv6 address in `WIREGUARD_ADDRESSES` | Use only the IPv4 address (remove the `fc00:...` part) | +| `REDIS_URL` with hardcoded IP (e.g. `172.19.0.2`) | Docker internal IPs change on restart | Always use hostnames (e.g. `redis://redis:6379`) | + +--- + +## VPN Expiration + +If the Mullvad subscription expires: +- **WhatsApp stops working** (Baileys can't connect through the VPN). +- **Everything else keeps running** normally (Rails, Sidekiq, Redis, Postgres are not affected). +- To restore: renew Mullvad, or revert the docker-compose changes to bypass the VPN entirely.