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.
105 lines
4.3 KiB
Markdown
105 lines
4.3 KiB
Markdown
# 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:
|
|
|
|
```ini
|
|
[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
|
|
|
|
```yaml
|
|
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
|
|
|
|
- [[Dankclaw Stack]]
|
|
- [[Gluetun]] — the VPN container
|
|
- Knowledge base: [[VPN Routing for SearXNG]], [[SearXNG Tuning]], [[Agentic Search Best Practices]]
|