Deploy SearXNG on Docker: Self-Host a Private Metasearch Engine for Your HomeLab

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

Privacy Search: Comparing Your Options

Every query you plug into a commercial search engine leaks data. Providers tie your public IP address, browser headers, and search strings together to build extensive advertising dossiers. If you run a home lab, re-evaluating your search habits matters just as much as deploying DNS sinkholes like Pi-hole or tuning your firewall rules.

Most home network setups rely on three main strategies for web search privacy:

  • Hosted Privacy Engines (DuckDuckGo, Brave Search, Startpage): These sites offer quick, zero-config alternatives to Google. They discard tracking cookies and mask your IP. Still, you place complete trust in a third-party server that can modify rankings, serve sponsored snippets, or quietly log requests under subpoena.
  • Browser Isolation and Disposable Profiles: You can spin up ephemeral browser containers via Kasm Workspaces or route traffic through ephemeral WireGuard tunnels. It stops cross-site profiling cold. The trade-off? High memory overhead (often 1–2 GB per browser session) and noticeable page-load latency.
  • Self-Hosted Metasearch (SearXNG on Docker): SearXNG acts as a self-managed proxy between your LAN and more than 70 search backends, including Google, Bing, DuckDuckGo, Wikipedia, and Reddit. Upstream platforms only see requests originating from your server’s IP, never your workstation or phone. SearXNG aggregates the responses, scrubs out tracking tokens, and delivers clean, unmonetized results back to your browser.

Self-hosting SearXNG delivers the best balance of query speed, ad-free readability, and direct control over outbound telemetry.

Pros and Cons of Self-Hosting SearXNG

Before deploying the containers, weigh the operational realities against the privacy gains.

The Advantages

  • Consolidated Search Feeds: Instead of trusting a single index, SearXNG queries multiple engines in parallel. A single search surfaces hits from mainstream web crawlers, GitHub repositories, and community forums side-by-side.
  • Zero Ads or Redirect Trackers: Sponsored listings, tracker-heavy affiliate redirects, and SEO content farms vanish before HTML renders. You get direct, clean target links.
  • Network-Wide Reach: Deploy the container stack once on your server, and you can point every phone, tablet, and workstation on your subnet to it in seconds.
  • Granular Engine Control: You select the upstream sources. Disable low-signal platforms entirely, or prioritize specialized endpoints for scientific papers, torrents, or source code.

The Trade-offs

  • Upstream Rate Limits and CAPTCHAs: Major providers like Google and Bing aggressively throttle cloud VPS IP ranges. Running the instance from a residential ISP connection bypasses most blocks, but high search volumes still demand conservative rate limiting.
  • Ongoing Upstream Scraper Maintenance: Search providers frequently tweak their markup and APIs, which can break individual engine scrapers. You will need to pull updated SearXNG container images once or twice a month to keep parsing scrapers healthy.
  • Memory Footprint: Fast response times require an in-memory key-value cache (Redis or Valkey), adding roughly 80–150 MB of baseline RAM consumption to the stack.

Recommended Architecture

A resilient SearXNG deployment pairs two lightweight containers through Docker Compose:

  1. SearXNG Core: The Python application that aggregates external queries, applies content filters, and renders the frontend interface.
  2. Valkey (or Redis): An in-memory cache running on port 6379. It caches identical search responses, stores temporary user sessions, and throttles rapid outbound calls to keep search providers from blacklisting your IP.

Decoupling your configuration files from the container runtime makes future software updates painless. This setup keeps your configuration portable and clean.

Organize your project directory like this:

~/searxng-docker/
├── docker-compose.yml
├── searxng/
│   └── settings.yml
└── .env

Implementation Guide: Deploying SearXNG Step-by-Step

Step 1: Create Directories and Base Files

SSH into your Docker host and set up a dedicated directory structure for the stack:

mkdir -p ~/searxng-docker/searxng
cd ~/searxng-docker

Step 2: Generate an Encryption Key

SearXNG requires an encryption key to sign session cookies and mitigate CSRF attacks. Generate a 32-byte hexadecimal secret and write it directly into your local .env file:

echo "SEARXNG_SECRET=$(openssl rand -hex 32)" > .env

Verify that the file contains a 64-character hex string using cat .env.

Step 3: Configure SearXNG Settings

SearXNG parses runtime configuration from a central YAML file. Create searxng/settings.yml with your text editor:

nano searxng/settings.yml

Paste the following production configuration. It hooks into the Valkey caching container, activates image proxying, and tunes timeouts across essential search backends:

use_default_settings: true

general:
  debug: false
  instance_name: "HomeLab Search"

server:
  # Bind to all interfaces inside the container
  bind_address: "0.0.0.0"
  port: 8080
  secret_key: "ultrasecretkey" # Overridden at runtime by SEARXNG_SECRET
  limiter: true
  image_proxy: true
  method: "GET"

redis:
  url: redis://redis:6379/0

search:
  safe_search: 0
  autocomplete: "duckduckgo"
  default_lang: "en"

engines:
  - name: google
    engine: google
    use_mobile_ui: false
    timeout: 3.0
  - name: duckduckgo
    engine: duckduckgo
    timeout: 3.0
  - name: bing
    engine: bing
    timeout: 3.0
  - name: wikipedia
    engine: wikipedia
    timeout: 2.0
  - name: github
    engine: github
    timeout: 3.0

Save and close the file (in nano, press Ctrl + O, tap Enter, then exit with Ctrl + X).

Step 4: Define the Docker Compose Stack

Now write your service definitions in docker-compose.yml:

nano docker-compose.yml

Add the following configuration. Remember to replace 192.168.1.50 with your server’s actual LAN IP address:

services:
  redis:
    image: valkey/valkey:8-alpine
    container_name: searxng-redis
    restart: unless-stopped
    command: valkey-server --save "" --appendonly no
    networks:
      - searxng-net
    tmpfs:
      - /var/lib/valkey
    cap_drop:
      - ALL
    cap_add:
      - SETGID
      - SETUID
      - DAC_OVERRIDE

  searxng:
    image: searxng/searxng:latest
    container_name: searxng
    restart: unless-stopped
    networks:
      - searxng-net
    ports:
      - "8080:8080"
    volumes:
      - ./searxng:/etc/searxng:rw
    environment:
      - SEARXNG_BASE_URL=http://192.168.1.50:8080/
      - SEARXNG_SECRET=${SEARXNG_SECRET}
    depends_on:
      - redis
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  searxng-net:
    name: searxng-net

Step 5: Launch and Inspect the Containers

Spin up the stack in detached mode:

docker compose up -d

Verify that both containers show healthy running states:

docker compose ps

Tail the application logs to ensure the configuration parsed without syntax errors or missing keys:

docker compose logs -f searxng

Press Ctrl + C to detach from the log stream once initialization completes. Next, point your web browser to http://<YOUR_SERVER_IP>:8080. You should see a stripped-down, responsive search bar ready to handle queries.

Step 6: Configure Your Default Browser Engine

Integrate your instance into daily browsing via the OpenSearch standard:

  1. Firefox: Navigate to your SearXNG URL. Right-click the browser address bar, then click Add Search Engine.
  2. Chromium Browsers (Chrome, Brave, Edge): Go to Settings > Search engine > Manage search engines and site search.
  3. Under Site search, click Add and fill in the fields:
    • Search engine: SearXNG HomeLab
    • Shortcut: @sx
    • URL with %s in place of query: http://<YOUR_SERVER_IP>:8080/search?q=%s
  4. Click the three dots beside the new entry and choose Make default.

Searches entered into your address bar now route straight to your local Docker host. Responses arrive fast, unmonetized, and completely untracked.

Share: