SearXNG + Tor in Docker: Your Own Private Search Engine

A step-by-step guide to deploying SearXNG in Docker with Tor integration and Caddy HTTPS reverse proxy. Private, self-hosted search that aggregates 40+ engines without leaking your queries.

SearXNG + Tor in Docker: Your Own Private Search Engine

Every search you type into Google, Bing, or DuckDuckGo gets logged, profiled, and sold. Your search history is a detailed map of your thoughts, fears, medical concerns, financial worries, and political leanings.

SearXNG fixes this. It’s an open-source metasearch engine that queries 40+ search engines on your behalf — without sending your identity along for the ride. Add Tor routing and you’ve got a search engine that doesn’t even reveal your IP to the engines it queries.

This guide gets you from zero to running in about fifteen minutes.


What You’re Building

Browser ──HTTPS:443──► Caddy ──HTTP:8080──► SearXNG (Docker)
                                                │
                                          SOCKS5:9050
                                                │
                                           Tor (systemd)
  • SearXNG aggregates results from Google, Bing, DuckDuckGo, Brave, and dozens more
  • Tor anonymises outgoing queries so search engines can’t see your IP
  • Caddy encrypts browser-to-server traffic with HTTPS (even on a LAN, encrypted is better)
  • Docker keeps everything contained and easy to update

Prerequisites

  • A Linux box (Raspberry Pi, old laptop, VM — anything with Docker)
  • Docker installed (sudo apt install docker.io or official docs)
  • Root/sudo access
  • 512MB RAM minimum (1GB+ recommended)

Step 1: Install and Configure Tor

sudo apt update && sudo apt install -y tor

Edit /etc/tor/torrc and add:

SocksPort 0.0.0.0:9050

Start it:

sudo systemctl enable --now tor

Verify Tor works:

curl -x socks5h://127.0.0.1:9050 https://check.torproject.org 2>&1 | grep -i congratulations

You should see: “Congratulations. This browser is configured to use Tor.”


Step 2: Configure SearXNG

Create the config directory and settings file:

sudo mkdir -p /etc/searxng

Generate a secret key:

python3 -c "import secrets; print(secrets.token_hex(32))"

Create /etc/searxng/settings.yml:

use_default_settings: true

general:
  instance_name: "SearXNG Local"
  enable_metrics: true

server:
  secret_key: "PASTE_YOUR_GENERATED_KEY_HERE"
  limiter: false
  image_proxy: true
  bind_address: "0.0.0.0:8080"

search:
  safe_search: 0
  default_lang: en
  formats:
    - html
    - json

ui:
  default_theme: simple
  theme_args:
    simple_style: light

outgoing:
  request_timeout: 15
  max_request_timeout: 25
  proxies:
    http: socks5h://127.0.0.1:9050
    https: socks5h://127.0.0.1:9050

engines:
  - name: google
    disabled: false
  - name: bing
    disabled: false
  - name: duckduckgo
    disabled: false
  - name: brave
    disabled: false
  - name: startpage
    disabled: false
  - name: wikipedia
    disabled: false
  - name: google news
    disabled: false
  - name: bing news
    disabled: false
  - name: duckduckgo news
    disabled: false
  - name: github
    disabled: false
  - name: stackoverflow
    disabled: false
  - name: arxiv
    disabled: false
  - name: reddit
    disabled: false

The proxies block under outgoing routes all search queries through Tor. Remove it if you prefer direct queries (e.g., when your network already uses a VPN).

Timeout note: Tor adds latency. The 15/25 second timeouts accommodate this. Without Tor, 10/20 is fine.


Step 3: Launch SearXNG

docker run -d \
  --name searxng \
  --network host \
  --restart unless-stopped \
  -v /etc/searxng:/etc/searxng \
  searxng/searxng:latest

Why --network host? SearXNG needs to reach Tor’s SOCKS proxy at 127.0.0.1:9050. Host networking makes this seamless — no Docker bridge routing needed.

Verify it’s running:

curl -s 'http://localhost:8080/search?q=test&format=json' | python3 -m json.tool | head -20

You should see JSON search results. Open http://YOUR_HOST_IP:8080 in a browser for the web interface.


Even on a private network, encrypting traffic is good practice. Caddy makes this trivial with automatic self-signed certificates.

Install Caddy:

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy

Edit /etc/caddy/Caddyfile:

http://YOUR_HOST_IP {
    redir https://{host}{uri} permanent
}

https://YOUR_HOST_IP {
    tls internal
    reverse_proxy localhost:8080
    header {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "no-referrer"
    }
}

Replace YOUR_HOST_IP with the machine’s LAN IP (e.g., 10.0.0.50).

tls internal generates a self-signed certificate — public CAs like Let’s Encrypt won’t issue certs for private IPs.

caddy validate --config /etc/caddy/Caddyfile
sudo systemctl restart caddy

To avoid browser warnings, trust the self-signed CA system-wide:

sudo cp /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt \
    /usr/local/share/ca-certificates/caddy-local-ca.crt
sudo update-ca-certificates

For browser trust, import the same root.crt file into your browser’s certificate authorities (Firefox: Settings → Privacy → Certificates → Import).


Step 5: Verify the Full Stack

# HTTP direct
curl -s 'http://localhost:8080/search?q=privacy&format=json' \
  | python3 -c "import sys,json; r=json.load(sys.stdin); print(f'{len(r.get(\"results\",[]))} results')"

# HTTPS through Caddy
curl -s 'https://YOUR_HOST_IP/search?q=privacy&format=json' \
  | python3 -c "import sys,json; r=json.load(sys.stdin); print(f'{len(r.get(\"results\",[]))} results')"

# Tor verification
docker exec searxng curl -x socks5h://127.0.0.1:9050 \
  https://check.torproject.org 2>&1 | grep -i congratulations

All three should succeed. You now have a private, Tor-routed, HTTPS-encrypted search engine running on your own hardware.


Maintenance

# View logs
docker logs searxng --tail 50

# Restart after settings change
docker restart searxng

# Update SearXNG
docker pull searxng/searxng:latest
docker stop searxng && docker rm searxng
# Re-run the docker run command from Step 3

# Check Tor status
sudo systemctl status tor

Optional: JSON API for Automation

With json in the formats list, SearXNG doubles as a search API:

curl -s 'http://localhost:8080/search?q=linux+hardening&format=json' | jq '.results[:3]'

This is useful for feeding search results into scripts, monitoring tools, or AI pipelines — all without touching a commercial API or leaking your queries.


What You’ve Gained

Before After
Google logs every search Your queries stay on your hardware
One engine’s bias 40+ engines aggregated
Your IP visible to search engines Tor hides your origin
HTTP on LAN HTTPS encrypted
Dependent on external services Self-hosted, yours to control

Your search history is nobody’s business but yours.


Search is thinking out loud. Keep your thoughts private.