Files
serverAppConfigs/docs/Dankclaw/SearXNG.md
T
Frank f488382b08 SearXNG docs: document quadlet boot-persistence for the trio
Update SearXNG, Gluetun, and Valkey to describe the quadlet units instead of
compose: searxng sets Network=gluetun.container, the units install to
~/.config/containers/systemd/, and scripts/deploy-searxng-quadlet.sh deploys
them. Add a Boot Persistence section to the Stack doc. Correct the Gluetun
tunnel check (the :8000 server 404s; verify tun0 instead) and note the pinned
10.89.2.0/24 subnet.
2026-08-18 23:42:45 -07:00

4.3 KiB

SearXNG

SearXNG is a privacy-respecting metasearch engine. It aggregates results from many upstream search engines into one result set and serves both an HTML UI and a JSON API for agents.

Host: Dankclaw — Hostname: search.dankclaw.local — Docs: https://docs.searxng.org

Facts (from research, primary source)

Item Value
Image ghcr.io/searxng/searxng:latest (or DockerHub mirror)
Port 8081 (host, on Gluetun) → 8080 (container)
Config /etc/searxng/settings.yml (bind mount)
Cache Valkey (replaces Redis) — valkey/valkey:9-alpine
Formats html, json (agent API)

VPN Routing (critical)

All SearXNG outbound traffic exits through the Gluetun VPN container. The searxng unit sets Network=gluetun.container, which makes it reuse gluetun's network namespace:

[Container]
ContainerName=searxng
Network=gluetun.container

Port publishing happens on the gluetun unit, not on searxng. See Gluetun and the knowledge base page VPN Routing for SearXNG (KB Pattern 2 is the one used here).

Boot Persistence (Quadlet)

The trio runs as quadlet units, not podman-compose, so it starts at boot. The unit files live in searxng/quadlet/ and install to ~/.config/containers/systemd/.

Unit file Service Role
searxng.network searxng-network.service DNS-enabled bridge, subnet pinned to 10.89.2.0/24
gluetun.container gluetun.service WireGuard tunnel; publishes 8081:8080
searxng-valkey.container searxng-valkey.service Valkey cache, alias valkey
searxng.container searxng.service Reuses gluetun's netns; publishes no port

gluetun and valkey join the searxng network. searxng reuses gluetun's netns and reaches valkey by name on that network. Each unit sets WantedBy=default.target. User linger is on, so the units start at boot.

Deploy with scripts/deploy-searxng-quadlet.sh dankclaw. It copies the units, daemon-reloads, enables them, and starts them in dependency order.

settings.yml Keys

use_default_settings: true
server:
  secret_key: "${SEARXNG_SECRET}"   # from .env
  base_url: "http://search.dankclaw.local/"
  limiter: true
  method: "POST"

search:
  default_lang: "en"
  formats:
    - html
    - json

valkey:
  url: "valkey://searxng-valkey:6379/0"

outgoing:
  request_timeout: 3.0
  max_request_timeout: 10.0
  retries: 3

Volumes

Container path Host path (convention)
/etc/searxng /opt/searxng/config
/etc/searxng/valkey (valkey container data)

Healthcheck

SearXNG responds on /healthz with 200 when ready.

Trusted Group (limiter.toml)

The trusted group lives in shared/searxng/limiter.toml, not in settings.yml. The settings.yml file only turns the limiter on. The file deploys to /opt/searxng/config/limiter.toml, mounted at /etc/searxng/limiter.toml.

Two lists control access:

  • pass_ip — an IP in this list gets unrestricted access and bypasses all bot detection.
  • trusted_proxies — if the request IP is in this list, SearXNG reads the real client IP from X-Forwarded-For (set by Caddy).

The whole homelab LAN 192.168.0.0/24 is in both lists. This lets local agents and browsers poll the JSON API without tripping the 4-per-hour limit. deploy.sh copies the file into place on each deploy.

Access SearXNG through Caddy (search.dankclaw.local). A direct request on the published port has no X-Forwarded-For header. The bot-detection cannot resolve the client and returns 429. The searxng network subnet 10.89.2.0/24 is in trusted_proxies so Caddy's request is trusted. The subnet is pinned in searxng.network, so the value stays stable across reboots.

Gotchas

  • The JSON API is rate-limited hard by the limiter (4 per 3600s per IP for untrusted). Add the agent's IP to pass_ip in limiter.toml if it polls.
  • Reach SearXNG through Caddy, not the raw published port. A direct port hit has no X-Forwarded-For header and the bot-detection returns 429.
  • base_url must match the public address or proxy rewrites break.
  • Keep the valkey cache container on the same podman network as searxng.

See Also