Privacy intermediate ⏱ 45 minutes

SearXNG: Privacy-Respecting Metasearch

Private search aggregating 70+ engines. Self-hosted Google alternative.

By Homelab Team • 2025-01-15
#searxng#search#privacy#metasearch#docker

Introduction

SearXNG is a free, privacy-respecting metasearch engine that aggregates results from more than 70 search services without tracking or profiling users. As a fork of the original SearX project, SearXNG offers improved performance, modern features, and active development. It acts as a proxy between you and search engines, ensuring your searches remain private while delivering comprehensive results from multiple sources.

By the end of this guide, you’ll have a production-ready SearXNG installation that:

  • Provides completely private and anonymous searching
  • Aggregates results from Google, Bing, DuckDuckGo, and 70+ other engines
  • Offers customizable search categories and engines
  • Includes no tracking, ads, or data collection
  • Features a clean, modern, and customizable interface
  • Supports advanced search operators and filters

Prerequisites

Before starting, ensure you have:

  • A Linux server with at least 1GB RAM (2GB recommended)
  • Docker and Docker Compose installed
  • Basic understanding of web services and privacy concepts
  • At least 5GB free disk space
  • Root or sudo access
  • Optional: Domain name for public access
  • Optional: Redis for caching (recommended for better performance)

What Makes SearXNG Special?

Key Features

Privacy First: No cookies, no tracking, no user profiling. Your searches are completely anonymous.

Metasearch Power: Aggregates results from multiple search engines, providing more comprehensive results than any single source.

Customizable Engines: Choose which search engines to use for different categories like general, images, videos, news, and more.

No Ads: Clean search results without advertisements or sponsored content.

Open Source: Fully transparent code that you can audit, modify, and self-host.

Advanced Features: Instant answers, autocomplete, bang shortcuts, custom themes, and more.

Multi-Language: Interface available in 60+ languages with international search engine support.

Why Self-Host SearXNG?

  • Complete Control: You control which engines are used and how results are processed
  • No Rate Limiting: Public instances often hit rate limits; your instance is just for you
  • Custom Configuration: Tailor the experience to your specific needs
  • Privacy Guarantee: Only you have access to your search patterns
  • Browser Default: Set as your default search engine with full privacy
  • Family/Team Use: Provide private search for your family or organization

Architecture Overview

Our setup consists of:

  1. SearXNG Container: Main search engine application
  2. Redis Container: Caching layer for improved performance
  3. Caddy/Nginx: Optional reverse proxy for HTTPS
  4. Filtron: Optional rate limiting (for public instances)
  5. Morty: Optional result proxy for extra privacy

The search flow:

  • User submits a search query to SearXNG
  • SearXNG queries multiple configured search engines in parallel
  • Results are aggregated, deduplicated, and ranked
  • Clean results are returned to the user without tracking
  • Redis caches results for faster repeat queries

Directory Structure Setup

Create the necessary directory structure:

mkdir -p ~/searxng/{searxng,redis,caddy}
cd ~/searxng

Generate Secret Key

Generate a secure secret key for SearXNG:

# Generate a random secret key
openssl rand -hex 32

Save this key for use in the configuration.

Docker Compose Configuration

Create a docker-compose.yml file:

version: '3.8'

services:
  searxng:
    container_name: searxng
    image: searxng/searxng:latest
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - ./searxng:/etc/searxng:rw
    environment:
      - SEARXNG_BASE_URL=https://search.yourdomain.com
      - SEARXNG_SECRET_KEY=YOUR_SECRET_KEY_HERE
      - SEARXNG_REDIS_URL=redis://redis:6379/0
    networks:
      - searxng_net
    depends_on:
      - redis
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  redis:
    container_name: searxng_redis
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --save 30 1 --loglevel warning
    volumes:
      - ./redis:/data
    networks:
      - searxng_net
    cap_drop:
      - ALL
    cap_add:
      - SETGID
      - SETUID
      - DAC_OVERRIDE

networks:
  searxng_net:
    driver: bridge

Important Notes:

  • Replace YOUR_SECRET_KEY_HERE with the generated secret key
  • Update SEARXNG_BASE_URL to your domain or http://localhost:8080 for local use
  • Redis provides caching for better performance
  • Containers run with minimal capabilities for security

SearXNG Configuration

Create the settings file ~/searxng/searxng/settings.yml:

use_default_settings: true

general:
  debug: false
  instance_name: "My SearXNG Instance"
  privacypolicy_url: false
  donation_url: false
  contact_url: false
  enable_metrics: false

brand:
  new_issue_url: https://github.com/searxng/searxng/issues/new
  docs_url: https://docs.searxng.org
  public_instances: https://searx.space
  wiki_url: https://github.com/searxng/searxng/wiki
  issue_url: https://github.com/searxng/searxng/issues

search:
  safe_search: 0  # 0=None, 1=Moderate, 2=Strict
  autocomplete: "google"  # Options: google, duckduckgo, wikipedia, etc.
  default_lang: "en"
  ban_time_on_fail: 5
  max_ban_time_on_fail: 120
  formats:
    - html
    - json

server:
  port: 8080
  bind_address: "0.0.0.0"
  secret_key: "YOUR_SECRET_KEY_HERE"  # Must match docker-compose
  limiter: true
  image_proxy: true
  method: "GET"
  http_protocol_version: "1.1"

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

ui:
  static_use_hash: true
  default_locale: "en"
  query_in_title: true
  infinite_scroll: false
  center_alignment: false
  cache_url: https://web.archive.org/web/
  default_theme: simple
  theme_args:
    simple_style: auto  # auto, light, dark

# Enabled search categories
categories_as_tabs:
  general:
  images:
  videos:
  news:
  map:
  music:
  it:
  science:
  files:

# Engine preferences
engines:
  - name: google
    engine: google
    shortcut: go
    use_mobile_ui: false

  - name: duckduckgo
    engine: duckduckgo
    shortcut: ddg

  - name: bing
    engine: bing
    shortcut: bi

  - name: brave
    engine: brave
    shortcut: br
    time_range_support: true
    paging: true

  - name: qwant
    engine: qwant
    shortcut: qw
    categories: general
    disabled: false

  - name: wikipedia
    engine: wikipedia
    shortcut: wp
    base_url: 'https://{language}.wikipedia.org/'

  - name: youtube
    engine: youtube_noapi
    shortcut: yt

  - name: github
    engine: github
    shortcut: gh

  - name: stackoverflow
    engine: stackoverflow
    shortcut: st

  - name: reddit
    engine: reddit
    shortcut: re

  - name: docker hub
    engine: docker_hub
    shortcut: dh
    categories: it

  - name: arxiv
    engine: arxiv
    shortcut: arx
    categories: science

  - name: openstreetmap
    engine: openstreetmap
    shortcut: osm
    categories: map

  - name: wikidata
    engine: wikidata
    shortcut: wd
    categories: general

  - name: google images
    engine: google_images
    shortcut: goi
    categories: images

  - name: bing images
    engine: bing_images
    shortcut: bii
    categories: images

  - name: google news
    engine: google_news
    shortcut: gon
    categories: news

  - name: piped
    engine: piped
    shortcut: pp
    categories: videos

enabled_plugins:
  - 'Hash plugin'
  - 'Search on category select'
  - 'Self Information'
  - 'Tracker URL remover'
  - 'Ahmia blacklist'

disabled_plugins:
  - 'Hostname replace'

# Security and privacy settings
outgoing:
  request_timeout: 3.0
  max_request_timeout: 15.0
  pool_connections: 100
  pool_maxsize: 20
  enable_http2: true
  verify: true
  max_redirects: 5
  retries: 1
  proxies:
    http: null
    https: null
  source_ips:
    - 0.0.0.0

Important: Replace YOUR_SECRET_KEY_HERE with your generated secret key.

Starting SearXNG

Start the containers:

cd ~/searxng
docker compose up -d

Verify containers are running:

docker compose ps
docker compose logs -f searxng

Access SearXNG:

  • Open your browser to http://localhost:8080
  • You should see the SearXNG interface

Customizing Search Engines

Edit ~/searxng/searxng/settings.yml to enable/disable engines:

engines:
  # Disable an engine
  - name: google
    engine: google
    disabled: true  # Add this line

  # Add custom engine weights (prefer certain sources)
  - name: duckduckgo
    engine: duckduckgo
    weight: 1.5  # Results ranked higher

  # Configure time range support
  - name: brave
    engine: brave
    time_range_support: true
    time_range_map:
      day: 'day'
      week: 'week'
      month: 'month'
      year: 'year'

  # Add custom headers for rate limit avoidance
  - name: google
    engine: google
    use_mobile_ui: true  # Use mobile version to avoid blocks

Restart after changes:

docker compose restart searxng

Firefox

  1. Navigate to your SearXNG instance
  2. Click the address bar
  3. Click the “Add Search Engine” option
  4. Go to Firefox Settings > Search
  5. Set SearXNG as default

Chrome/Chromium

  1. Go to Settings > Search engine > Manage search engines
  2. Click “Add”
  3. Fill in:
    • Name: SearXNG
    • Keyword: searxng
    • URL: http://localhost:8080/?q=%s
  4. Click the three dots and select “Make default”

Manually Add OpenSearch

Create ~/searxng/searxng/opensearch.xml:

<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
  <ShortName>SearXNG</ShortName>
  <Description>Privacy-respecting metasearch engine</Description>
  <InputEncoding>UTF-8</InputEncoding>
  <Image width="64" height="64" type="image/png">https://search.yourdomain.com/static/themes/simple/img/searxng.png</Image>
  <Url type="text/html" method="get" template="https://search.yourdomain.com/?q={searchTerms}"/>
  <Url type="application/x-suggestions+json" method="get" template="https://search.yourdomain.com/autocompleter">
    <Param name="q" value="{searchTerms}"/>
  </Url>
</OpenSearchDescription>

Bang Shortcuts

SearXNG supports bang shortcuts (like DuckDuckGo):

  • !go query - Search Google
  • !ddg query - Search DuckDuckGo
  • !yt query - Search YouTube
  • !gh query - Search GitHub
  • !wp query - Search Wikipedia
  • !st query - Search Stack Overflow
  • !red query - Search Reddit
  • !osm query - Search OpenStreetMap

Example: !gh docker compose searches GitHub for “docker compose”

Theme Customization

SearXNG includes several themes:

ui:
  default_theme: simple  # Options: simple, oscar, pix-art
  theme_args:
    simple_style: dark  # auto, light, dark

Or create custom CSS in ~/searxng/searxng/custom.css:

/* Custom theme colors */
:root {
  --color-base-font: #ffffff;
  --color-base-background: #0d1117;
  --color-header-background: #161b22;
  --color-footer-background: #161b22;
  --color-sidebar-background: #161b22;
  --color-backtotop-background: #58a6ff;
  --color-backtotop-font: #ffffff;
}

Reference in settings:

ui:
  static_path: /etc/searxng
  custom_css: custom.css

Reverse Proxy with Caddy

Create ~/searxng/Caddyfile:

search.yourdomain.com {
    reverse_proxy searxng:8080

    # Security headers
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "no-referrer"
        Permissions-Policy "geolocation=(), microphone=(), camera=()"
    }

    # Rate limiting (optional)
    rate_limit {
        zone searxng {
            key {remote_host}
            events 100
            window 1m
        }
    }
}

Update docker-compose.yml:

services:
  caddy:
    container_name: searxng_caddy
    image: caddy:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy/data:/data
      - ./caddy/config:/config
    networks:
      - searxng_net

Nginx Alternative

Or use Nginx:

server {
    listen 80;
    server_name search.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name search.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/search.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/search.yourdomain.com/privkey.pem;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header Referrer-Policy "no-referrer" always;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Rate limiting
        limit_req zone=searxng burst=20 nodelay;
        limit_req_status 429;
    }

    # Static files caching
    location /static {
        proxy_pass http://localhost:8080;
        proxy_cache_valid 200 1d;
        expires 1d;
        add_header Cache-Control "public, immutable";
    }
}

# Rate limit zone (add to http block in nginx.conf)
limit_req_zone $binary_remote_addr zone=searxng:10m rate=10r/s;

Performance Tuning

Optimize for better performance:

Redis Configuration

Create ~/searxng/redis/redis.conf:

# Memory optimization
maxmemory 256mb
maxmemory-policy allkeys-lru

# Persistence
save 900 1
save 300 10
save 60 10000

# Performance
tcp-backlog 511
timeout 0
tcp-keepalive 300

Update docker-compose.yml:

redis:
  command: redis-server /usr/local/etc/redis/redis.conf
  volumes:
    - ./redis:/data
    - ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro

SearXNG Optimization

# In settings.yml
outgoing:
  request_timeout: 2.0  # Faster timeout
  pool_connections: 200  # More connections
  pool_maxsize: 50
  enable_http2: true  # HTTP/2 for speed

server:
  limiter: true  # Enable rate limiting
  image_proxy: true  # Proxy images for privacy

API Usage

SearXNG provides a JSON API:

# Basic search
curl "http://localhost:8080/search?q=docker&format=json"

# Search with category
curl "http://localhost:8080/search?q=homelab&category_general=1&format=json"

# Image search
curl "http://localhost:8080/search?q=kubernetes&category_images=1&format=json"

# Paginated results
curl "http://localhost:8080/search?q=docker&pageno=2&format=json"

Example response:

{
  "query": "docker",
  "number_of_results": 150000000,
  "results": [
    {
      "url": "https://www.docker.com/",
      "title": "Docker: Empowering App Development",
      "content": "Docker is a platform for developers...",
      "engine": "google",
      "parsed_url": ["https", "www.docker.com", "/", "", "", ""],
      "template": "default.html",
      "engines": ["google", "bing"],
      "positions": [1, 2],
      "score": 1.5
    }
  ],
  "suggestions": ["docker compose", "docker tutorial"],
  "infoboxes": []
}

Monitoring and Logs

Check SearXNG logs:

# View live logs
docker compose logs -f searxng

# Check Redis logs
docker compose logs -f redis

# View last 100 lines
docker compose logs --tail=100 searxng

Enable metrics in settings:

general:
  enable_metrics: true

Access metrics at: http://localhost:8080/stats

Backup Configuration

Create a backup script:

#!/bin/bash
# ~/searxng/scripts/backup.sh

BACKUP_DIR=~/searxng/backups
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/searxng_backup_$TIMESTAMP.tar.gz"

mkdir -p "$BACKUP_DIR"

echo "Creating backup..."
tar -czf "$BACKUP_FILE"     ~/searxng/searxng/settings.yml     ~/searxng/docker-compose.yml     ~/searxng/Caddyfile

# Keep only last 10 backups
ls -t "$BACKUP_DIR"/searxng_backup_*.tar.gz | tail -n +11 | xargs -r rm

echo "Backup completed: $BACKUP_FILE"

Troubleshooting

No Search Results

# Check if engines are enabled
docker compose exec searxng cat /etc/searxng/settings.yml | grep -A5 "engines:"

# Test specific engine
curl "http://localhost:8080/search?q=test&engines=duckduckgo&format=json"

# Check engine status
# Visit: http://localhost:8080/stats/checker

Rate Limited by Search Engines

# Use mobile user agents
engines:
  - name: google
    use_mobile_ui: true

# Add delays between requests
outgoing:
  request_timeout: 3.0
  retries: 2

High Memory Usage

# Check container memory
docker stats searxng redis

# Reduce Redis memory
# In redis.conf:
maxmemory 128mb

Slow Search Performance

# Enable HTTP/2
# In settings.yml:
outgoing:
  enable_http2: true

# Reduce timeout
outgoing:
  request_timeout: 2.0

# Check Redis connection
docker compose exec searxng redis-cli -h redis ping

Container Restart Loop

# Check logs for errors
docker compose logs searxng

# Validate settings.yml
docker compose exec searxng cat /etc/searxng/settings.yml

# Check permissions
ls -la ~/searxng/searxng/

Security Hardening

Restrict Public Access

For private instances, use authentication:

# In Nginx config
location / {
    auth_basic "Restricted Access";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://localhost:8080;
}

Generate password:

sudo apt install apache2-utils
htpasswd -c /etc/nginx/.htpasswd yourusername

Firewall Rules

# Only allow local access
sudo ufw allow from 192.168.1.0/24 to any port 8080

# Or use SSH tunnel
ssh -L 8080:localhost:8080 user@your-server

Disable Logging

# In settings.yml
server:
  access_log: false

# In docker-compose.yml
logging:
  driver: "none"

Advanced Features

Custom Result Filters

# Filter out certain domains
engines:
  - name: google
    engine: google
    exclude_domains:
      - pinterest.com
      - facebook.com

Language Preferences

search:
  default_lang: "en"
  languages:
    - en
    - fr
    - de
    - es

Auto-Redirect

Automatically redirect to first result:

enabled_plugins:
  - 'Self Information'
  - 'Tracker URL remover'
  - 'Ahmia blacklist'
  - 'Autoredirect'  # Add this

Conclusion

You now have a fully functional, privacy-respecting SearXNG metasearch engine. Your searches are completely private, with no tracking or data collection, while still delivering comprehensive results from multiple search engines.

For more advanced configurations, explore:

  • Public instance setup with Filtron rate limiting
  • Custom engine integration
  • Search result ranking customization
  • Multi-user authentication
  • Advanced privacy plugins

Enjoy your private search experience!