--- type: concept title: VPN Routing for SearXNG description: Send only SearXNG outbound requests through a VPN while leaving all other local traffic alone. tags: - vpn - searxng - networking - proxy - unverified verified: 2026-08-28 timestamp: 2026-08-19 resource: https://docs.searxng.org/admin/settings/settings_outgoing.html --- # VPN Routing for SearXNG SearXNG talks to many upstream engines. Some of them rate-limit or block the source IP. You can send only that outbound path through a VPN and leave every other process on the host untouched. This page gives four working patterns. Choose the one that matches how you run SearXNG. > **Your setup:** SearXNG runs on another host at `192.168.0.143:8081` (dankclaw). Apply the Docker patterns on that host. The proxy pattern works from any host. ## The Goal ```mermaid graph LR A[Local agent] --> B[SearXNG] B -->|through VPN| C[Upstream engines] D[Other local traffic] --> E[Plain internet] ``` The VPN carries only the SearXNG outbound path. Nothing else changes. ## Pattern 1: SearXNG Proxy Setting SearXNG supports HTTP and SOCKS proxies natively. Point the outbound settings at a local proxy that exits through the VPN. ```yaml outgoing: proxies: all://: - socks5://vpn-proxy:1080 extra_proxy_timeout: 10.0 ``` Apply the proxy to one engine only: ```yaml engines: - name: google engine: google proxies: https: - socks5://vpn-proxy:1080 ``` To force the VPN source IP on a multi-homed host, add `source_ips` and pin the engine to IPv4: ```yaml outgoing: source_ips: - 10.8.0.2 engines: - name: my-search engine: duckduckgo network: ipv4 ``` ## Pattern 2: Gluetun (Recommended) Gluetun is a VPN container (WireGuard or OpenVPN). Attach SearXNG to its network namespace. Only SearXNG shares the tunnel. Other containers stay on the default network. ```yaml services: gluetun: image: qmcgaw/gluetun:latest cap_add: [NET_ADMIN] devices: [/dev/net/tun:/dev/net/tun] ports: - "8081:8080" environment: VPN_SERVICE_PROVIDER: mullvad VPN_TYPE: wireguard WIREGUARD_PRIVATE_KEY: restart: always searxng: image: searxng/searxng:latest network_mode: service:gluetun depends_on: - gluetun volumes: - ./core-config:/etc/searxng restart: unless-stopped ``` Put the port mapping on the `gluetun` service. The `searxng` container does not get its own ports. ## Pattern 3: Dedicated Internal Network Create a network that only SearXNG shares with a lighter VPN client. ```yaml services: searxng: image: searxng/searxng:latest networks: - searxng-vpn volumes: - ./core-config:/etc/searxng vpn-client: image: dperson/openvpn-client:latest networks: - searxng-vpn cap_add: - NET_ADMIN networks: searxng-vpn: internal: true ``` An OpenVPN client container is simpler than Gluetun when your provider uses OpenVPN. ## Pattern 4: Run SearXNG Inside the VPN Container ```yaml services: searxng: image: searxng/searxng:latest network_mode: "container:vpn-client" depends_on: - vpn-client volumes: - ./core-config:/etc/searxng vpn-client: image: dperson/openvpn-client:latest cap_add: [NET_ADMIN] environment: VPN: server.address;username;password restart: always ``` This pattern places the whole SearXNG process inside the tunnel namespace. The host sees the tunnel as the only route for SearXNG. ## Compare the Patterns | Pattern | Complexity | Isolation | Per-engine control | |---------|-----------|-----------|--------------------| | Proxy setting | Low | Medium | Yes | | Gluetun | Medium | High | No | | Dedicated network | Medium | High | No | | Inside VPN container | Medium | Highest | No | ## Decisions That Matter - A SOCKS proxy keeps the per-engine control. Use Pattern 1. - Gluetun gives the cleanest full-tunnel setup. Use Pattern 2. - Mullvad ships its own WireGuard keys for Gluetun. - An OpenVPN-only provider works with Pattern 3 or 4. ## Verify the Route 1. Run the instance with the VPN active. 2. Query an IP-echo page through SearXNG. 3. Confirm that the returned IP differs from your host IP. 4. Confirm that unrelated host traffic keeps the host IP. ```bash # Gluetun ships no curl; use its own endpoints or a throwaway container. podman exec gluetun sh -c 'test -e /sys/class/net/tun0 && echo tunnel-up' podman exec gluetun sh -c 'wget -qO- 127.0.0.1:9999/health' # "OK" when up # NOTE: :9999 is Gluetun's HTTP health server (200 "OK" / 500); :8000 is the # CONTROL server (/v1/* API), which 404s on everything else. Do not health- # probe :8000. Egress-IP check needs a real binary — run a companion # container on gluetun's network: docker run --rm --net container:gluetun # curlimages/curl -s ifconfig.me ``` ## Citations - SearXNG outgoing settings: - SearXNG engine settings: - Gluetun: - Connect a container to Gluetun: - Docker Compose networking: ## See Also - [SearXNG Tuning](searxng-tuning.md) — The instance these patterns protect. - [Agentic Search Best Practices](agentic-search-best-practices.md) — Proxy rotation and retries.