SearXNG – Your Very Own Search Engine

Google Knows What You Searched Last Summer: How to Self-Host Your Way Out
searxng-private-search.html

Google Knows What You Searched Last Summer: How to Self-Host Your Way Out

Go to myactivity.google.com. Right now. I’ll wait.

Scroll through that list. Every search you’ve made. Every question you were too embarrassed to ask a friend. Every 2 AM medical symptom panic. Every job listing you clicked while still employed. Every weird hobby you explored for a week and abandoned.

That’s not a search history. That’s a psychological profile. And Google has been building it since the day you created your account.

The uncomfortable truth: Your search history reveals more about you than your medical records, your browser history, or your text messages. It’s the unfiltered stream of what you want to know — and you hand it over dozens of times a day.

If you’ve been following along with this site, you already know I take a layered approach to privacy. AdGuard Home handles DNS-level ad and tracker blocking — that was Layer 1. But DNS filtering can’t help you when you voluntarily type your thoughts into a search box owned by an advertising company. That’s Layer 2. And that’s what we’re fixing today.

Already running AdGuard or Pi-hole? Good — DNS filtering is half the battle. This article is the other half. If you haven’t set up DNS-level blocking yet, start with my AdGuard article first.
jdelay@thedelay:~/privacy$ ./deploy_searxng.sh

Quick Deploy: 6 Steps, 15 Minutes

Prerequisites: A Linux box with Docker and Docker Compose installed. That’s it.

Step 1: Create the directory structure

sudo mkdir -p /opt/searxng
cd /opt/searxng

Step 2: Generate a secret key

openssl rand -hex 32
# Save this output -- you'll need it in Step 3

Step 3: Create settings.yml

Paste the production-ready config from the Settings section below. Replace the secret_key with your value from Step 2.

Step 4: Create docker-compose.yml

Paste the compose file from the Stack section below.

Step 5: Launch

docker compose up -d

Step 6: Verify

Open http://your-server:8080 (or your Caddy domain). Search for something. Check that results come back from multiple engines.

That’s it. Your searches are now yours again. Read the rest for the hardening, the daily-driver tips, and the war stories.


$ cat /var/log/google/your_entire_life.log

The Problem: What Google Actually Knows About You

Let’s be specific about what we’re dealing with. When you type a query into Google, here’s what gets logged:

What a Search Engine Knows About You

  • Your IP address (and therefore your approximate location)
  • The exact search query
  • Which results you clicked (and how long you stayed)
  • Your device fingerprint (browser, OS, screen resolution, installed fonts)
  • Your Google account identity (if signed in — and you probably are)
  • The search you made before this one, and the one before that
  • The time of day, day of week, and frequency patterns

Google doesn’t just know what you searched. They know when you searched, where you searched from, what you clicked, and how your search patterns change over time. That last one is the kicker — your search history is a timeline of your evolving interests, concerns, and life events.

Here’s how the major search options compare:

Feature Google Bing DuckDuckGo SearXNG
Logs your IP Yes Yes No (claimed) No (self-hosted)
Tracks search history Yes Yes No (claimed) No
Personalizes results Yes Yes No No
Sells data to advertisers Yes Yes No No
You control the server No No No Yes
Open source No No No Yes
Aggregates multiple engines No No No Yes

Notice the pattern? Even DuckDuckGo — the “privacy” search engine — is a company running servers you don’t control. They say they don’t log. You trust that they don’t. But you can’t verify it.

Why not just use DuckDuckGo? It’s better than Google, genuinely. But “trust us, we don’t log” is not the same as “you can read the source code and see for yourself.” DuckDuckGo is a privacy improvement. SearXNG is privacy sovereignty. The difference is who has to trust whom.

$ man searxng

What SearXNG Actually Does (And What It Doesn’t)

SearXNG is a free, open-source metasearch engine. It doesn’t have its own index — it queries other search engines on your behalf and aggregates the results. The critical difference: the upstream engines see the SearXNG server’s IP, not yours. Your identity stays behind your own infrastructure.

Here’s the request flow:

You (browser)
    |
    | [search query over HTTPS]
    |
    v
Your SearXNG Instance (your server, your network)
    |
    | [same query, but from SearXNG's IP]
    |
    +-----> Google ------+
    +-----> Bing --------+----> Results aggregated,
    +-----> Brave -------+      deduplicated, and
    +-----> Wikipedia ---+      returned to you
    +-----> DuckDuckGo --+
    |
    v
You (clean results, no tracking)

Google sees a query from your server’s IP. It doesn’t see your browser fingerprint, your cookies, your account, or your click behavior. As far as Google is concerned, some random server asked a question. It doesn’t know (or care) who’s behind it.

Critical caveat: SearXNG anonymizes who is searching, not what is being searched. Your queries still reach upstream engines. If you search for your own name, Google still processes that query. What they can’t do is tie it back to you, your device, or your browsing history. SearXNG is an anonymizing proxy for search, not an invisibility cloak.
Engine categories — what SearXNG can search

SearXNG organizes its 70+ supported engines into categories:

Category Example Engines What It Searches
GeneralGoogle, Bing, DuckDuckGo, BraveWeb pages
ImagesGoogle Images, Bing Images, FlickrImage search
VideosYouTube, PeerTube, DailymotionVideo search
NewsGoogle News, Bing News, Yahoo NewsNews articles
MapsOpenStreetMap, PhotonLocation/maps
MusicBandcamp, SoundCloudMusic search
ITStack Overflow, GitHub, MDNDeveloper resources
ScienceGoogle Scholar, Semantic Scholar, PubMedAcademic papers
Files1337x, piratebay, nyaaTorrents/files
Social MediaReddit, MastodonSocial content

You enable and disable engines individually. Don’t use YouTube? Turn it off. Want only privacy-respecting engines? Disable Google entirely and rely on Brave + DuckDuckGo + Mojeek. Your instance, your rules.

What SearXNG Is NOT

Let me save you some disappointment:

  • It’s not a VPN. It doesn’t encrypt your traffic to the SearXNG server. Use HTTPS (Caddy handles this) or access it over your LAN/VPN.
  • It’s not Tor. It doesn’t provide network-level anonymity. Your ISP can see you connecting to your SearXNG instance (which is fine — it’s your server).
  • It’s not magic. If you sign into Google and search from there, SearXNG can’t help you. It only protects searches routed through it.
  • It doesn’t have its own index. If every upstream engine blocks you, you get no results. (More on that in Lessons Learned.)

$ docker compose config –services

The Stack: Three Services, One Privacy Layer

The deployment is three containers:

Service Purpose Port External Exposure
SearXNG The search engine 8080 Yes (behind reverse proxy)
Caddy Reverse proxy + auto TLS 443 Yes (HTTPS frontend)
Valkey Result caching (Redis fork) 6379 No (internal only)
Why Caddy over Nginx or Traefik? Automatic HTTPS with zero configuration. Caddy gets Let’s Encrypt certificates on its own — no certbot cron jobs, no renewal scripts. For a single-service deployment like this, it’s the least amount of moving parts. If you already run Nginx or Traefik as your reverse proxy, skip Caddy entirely and point your existing proxy at SearXNG’s port 8080.

Here’s how they fit together:

Internet / LAN
    |
    v
[Caddy :443]  <-- HTTPS termination, auto TLS
    |
    v
[SearXNG :8080]  <-- Search engine, queries upstream
    |
    v
[Valkey :6379]  <-- Caches results, reduces upstream requests
Why Valkey instead of Redis? Valkey is a community fork of Redis that emerged after Redis changed its license. It’s API-compatible, actively maintained by the Linux Foundation, and genuinely open source (BSD license). SearXNG doesn’t care which one you use — they’re interchangeable. I picked Valkey because open source means open source.

$ docker compose up -d

Deployment: The Full Walkthrough

Here’s the full deployment, step by step. I’m assuming a Linux server with Docker and Docker Compose already installed.

Create the directory structure
sudo mkdir -p /opt/searxng
cd /opt/searxng

// All config files, compose file, and volumes in one place

Generate a secret key
openssl rand -hex 32

Save this output. You’ll paste it into settings.yml in the next step. This key is used by SearXNG internally for session management and CSRF protection.

// Don’t skip this. Running without a secret key means default values, which means predictable tokens.

Configure settings.yml

This is the heart of SearXNG. Create /opt/searxng/settings.yml:

Full annotated settings.yml (production-ready)
# SearXNG Settings - Production Configuration
# Docs: https://docs.searxng.org/admin/settings/

use_default_settings: true

general:
  instance_name: "Search"       # Shows in browser tab
  privacypolicy_url: false       # No external privacy policy link
  donation_url: false            # No donation nag
  enable_metrics: false          # Don't track internal usage stats

server:
  secret_key: "YOUR_SECRET_KEY_FROM_STEP_2"
  bind_address: "0.0.0.0"
  port: 8080
  limiter: true                  # Rate limiting (requires Valkey)
  public_instance: false         # Not a public instance
  image_proxy: true              # Proxy images through SearXNG

ui:
  static_use_hash: true          # Cache-busting for static files
  default_theme: simple          # Clean, fast theme
  default_locale: en             # Interface language
  query_in_title: false          # Don't put search queries in browser tab title
  infinite_scroll: true          # Load more results on scroll
  center_alignment: true         # Center the search bar

search:
  safe_search: 0                 # 0=off, 1=moderate, 2=strict
  autocomplete: "duckduckgo"     # Autocomplete suggestions source
  default_lang: "auto"           # Auto-detect language
  ban_time_on_fail: 5            # Ban failing engines for 5 seconds
  max_ban_time_on_fail: 120      # Max ban time: 2 minutes

engines:
  # Disable engines that are redundant or unreliable
  - name: bing
    disabled: false
  - name: brave
    disabled: false
  - name: duckduckgo
    disabled: false
  - name: google
    disabled: false
  - name: mojeek
    disabled: false
  - name: startpage
    disabled: false
  - name: qwant
    disabled: false
  - name: wikipedia
    disabled: false
  - name: wikidata
    disabled: true               # Rarely useful for general search
  - name: currency
    disabled: false              # Handy for quick conversions

outgoing:
  request_timeout: 3.0           # Don't wait forever for slow engines
  max_request_timeout: 10.0      # Absolute max
  useragent_suffix: ""           # Don't advertise SearXNG in user-agent
  pool_connections: 100
  pool_maxsize: 20

The important bits: limiter: true enables rate limiting (requires Valkey), public_instance: false tells SearXNG this isn’t public-facing, and image_proxy: true routes images through your server so upstream engines can’t track your browser via image loads.

// If you only change one thing from the defaults, make it the secret_key

Create docker-compose.yml
Full annotated docker-compose.yml
version: "3.9"

services:
  searxng:
    image: searxng/searxng:latest
    container_name: searxng
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - ./settings.yml:/etc/searxng/settings.yml:ro
    environment:
      - SEARXNG_BASE_URL=https://search.yourdomain.com/
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
    networks:
      - searxng

  valkey:
    image: valkey/valkey:8-alpine
    container_name: valkey
    restart: unless-stopped
    command: valkey-server --save 30 1 --loglevel warning
    volumes:
      - valkey-data:/data
    cap_drop:
      - ALL
    cap_add:
      - SETGID
      - SETUID
      - DAC_OVERRIDE
    networks:
      - searxng

  caddy:
    image: caddy:2-alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - caddy-config:/config
    networks:
      - searxng

networks:
  searxng:
    driver: bridge

volumes:
  valkey-data:
  caddy-data:
  caddy-config:

The cap_drop: ALL Gotcha

  • cap_drop: ALL strips every Linux capability from the container. This is a security best practice — containers shouldn’t have privileges they don’t need.
  • But SearXNG needs CHOWN, SETGID, and SETUID to start properly (it changes its internal user on boot).
  • If you see Permission denied or the container exits immediately, check that cap_add includes these three capabilities.
  • Valkey needs SETGID, SETUID, and DAC_OVERRIDE for its data directory.
Configure Caddy (or skip this)

If you’re using Caddy as the reverse proxy, create /opt/searxng/Caddyfile:

search.yourdomain.com {
    reverse_proxy searxng:8080
}

That’s the entire Caddyfile. Caddy handles HTTPS certificates automatically.

If you already have Nginx, Traefik, or Cloudflare Tunnel as your reverse proxy, skip Caddy entirely. Just point your existing proxy at http://your-server:8080. Remove the Caddy service from docker-compose.yml.

// If you’re running this behind Cloudflare Tunnel like I do for other services, you don’t need Caddy at all

Launch and verify
cd /opt/searxng
docker compose up -d

Check that all containers are healthy:

docker compose ps

You should see three services running (or two, if you skipped Caddy). Open your browser, navigate to your SearXNG URL, and search for something. Results should come back from multiple engines — you’ll see small engine icons next to each result indicating the source.

If you get no results, check the SearXNG logs:

docker compose logs searxng

// Common first-run issues: incorrect YAML syntax (whitespace-sensitive), missing secret key, or Valkey not ready yet (give it 10 seconds and retry)


$ chmod 600 /etc/searxng/settings.yml

Hardening: Because Running It Isn’t Enough

A default SearXNG installation works fine for personal use on a LAN. But if it’s exposed to the internet — even behind authentication — you should lock it down. An unsecured SearXNG instance is an open proxy that anyone can use to search the web through your server.

Enable the rate limiter

The rate limiter prevents abuse (and protects you from accidentally DDoS-ing upstream engines). It requires Valkey.

In settings.yml, make sure these are set:

server:
  limiter: true

redis:
  url: "redis://valkey:6379/0"

// If limiter: true and Valkey isn’t running, SearXNG will crash on start. The error message isn’t great.

Restrict network access

If this is a personal instance, lock it to your LAN:

# UFW example: allow SearXNG only from local network
sudo ufw allow from 192.168.0.0/16 to any port 8080 proto tcp
sudo ufw deny 8080

Or better: don’t expose port 8080 at all. Access SearXNG through your VPN (WireGuard, Tailscale) or Cloudflare Tunnel with access controls.

File permissions

Your settings.yml contains the secret key. Lock it down:

sudo chown root:root /opt/searxng/settings.yml
sudo chmod 600 /opt/searxng/settings.yml

// Docker reads the file as root, so root ownership is fine

Update strategy

SearXNG releases frequently. The Docker image tagged latest tracks stable releases.

cd /opt/searxng
docker compose pull
docker compose up -d

That’s it. Valkey data persists in a Docker volume, and your settings.yml is mounted from the host. Updates are non-destructive. Set a reminder to update monthly, or set up Watchtower for automatic updates.

Why does this matter for a personal instance? Because an exposed, outdated SearXNG instance is an open proxy. Bots will find it. They’ll use it to search through your server, consuming your bandwidth and potentially getting your IP rate-limited or blocked by upstream engines. Hardening isn’t paranoia — it’s hygiene.

Hardening Checklist

  • Rate limiter enabled (requires Valkey)
  • Network access restricted (UFW, VPN, or tunnel)
  • settings.yml permissions: 600
  • cap_drop: ALL in docker-compose.yml
  • Not running as root inside the container
  • Regular updates (monthly minimum)
  • Logs rotated (Docker handles this by default with json-file driver)

$ alias s=’searxng-search’

Living With SearXNG: Daily Driver Tips

SearXNG is only useful if you actually use it. Here’s how to make it your daily driver without feeling like you downgraded.

Set It As Your Default Search Engine

Browser configuration for default search

Firefox:

  1. Navigate to your SearXNG instance
  2. Right-click the URL bar → “Add Search Engine”
  3. Go to Settings → Search → Default Search Engine → select SearXNG

Chrome/Chromium:

  1. Go to Settings → Search Engine → Manage Search Engines
  2. Add new: Name = SearXNG, Keyword = s, URL = https://your-searxng/search?q=%s
  3. Set as default

Brave:

Same as Chrome — Settings → Search Engine.

Safari:

Safari doesn’t support custom default search engines natively. Use a browser extension like xSearch or just bookmark your SearXNG instance.

Mobile (Android/iOS):

Firefox for Android supports custom search engines (same process as desktop). On iOS, use Firefox or Brave — Safari on iOS does not support custom search engines.

Bang Commands — The Power User’s Shortcut

SearXNG supports bang commands, just like DuckDuckGo. Type a prefix to search a specific engine directly:

Bang Engine Privacy
!gGoogleProtected (routed through SearXNG)
!bBingProtected
!ddgDuckDuckGoProtected
!wWikipediaProtected
!ghGitHubProtected
!soStack OverflowProtected
!!gGoogle (direct)LOST (redirects your browser directly)
!!bBing (direct)LOST

The Double-Bang Trap

  • Single bang (!g) routes through SearXNG. Your privacy is preserved. The upstream engine sees SearXNG’s IP.
  • Double bang (!!g) redirects your browser directly to the engine. Your privacy is gone. Google sees your real IP, your browser fingerprint, everything.
  • Double bang exists for cases where you need engine-specific features (Google Maps integration, YouTube login, etc.). Use it consciously, not accidentally.
  • If you’re not sure, always use single bang.

The JSON API

SearXNG has a JSON API that returns search results as structured data:

curl "https://your-searxng/search?q=homelab+backup&format=json" | jq '.results[:3]'

This is gold for automation. You could build an n8n workflow that searches for a topic daily and sends you a digest. Or a script that checks if your site appears in results. Or a monitoring tool that alerts you when a specific term trends. The API turns search into a building block.


$ diff –color google.conf searxng.conf

SearXNG vs. The Field

Every privacy-focused search option makes trade-offs. Here’s how they stack up:

Feature SearXNG DuckDuckGo Startpage Brave Search Kagi
Self-hosted Yes No No No No
Open source Yes Partially No No No
Own index No Hybrid No (Google proxy) Yes Yes
Multi-engine Yes (70+) No No No No
Cost Free Free Free Free $5-10/mo
Privacy model You verify Trust them Trust them Trust them Trust them
Result quality Good (depends on engines) Good Good (Google-based) Good Excellent
Maintenance You maintain None None None None
The real comparison isn’t features — it’s trust models. Every search engine except SearXNG asks you to trust a company’s privacy policy. Policies change. Companies get acquired. Startpage was bought by an advertising company in 2019 and people are still debating whether it’s trustworthy. SearXNG doesn’t ask you to trust anyone. The code is open. The server is yours. The logs (if any) are yours to delete. That’s not a feature — it’s a fundamentally different relationship with your tools.

$ cat /var/log/mistakes.log

Lessons Learned

Every deployment teaches you something. Here’s what this one taught me.

Stripping Capabilities Means Stripping Convenience

cap_drop: ALL in docker-compose.yml is the right security move. But when I first launched without the corresponding cap_add entries, SearXNG silently failed to start. No helpful error message — just an exit code and a vague permission error in the logs.

The fix: add back the minimum capabilities the container actually needs (CHOWN, SETGID, SETUID for SearXNG; add DAC_OVERRIDE for Valkey). Test after every capability change.

The lesson: Security hardening that breaks functionality isn’t hardening — it’s sabotage. Strip capabilities, but test the result.

Engines Will Fight Back

Google doesn’t love being queried by automated tools. After a few hundred queries, you’ll start seeing CAPTCHAs or empty results from Google. Bing is more tolerant. Brave barely notices.

The fix: diversify your engines. Don’t rely on any single source. I run 8-10 engines simultaneously. If Google blocks me for a few hours, I barely notice because Brave, DuckDuckGo, and Mojeek pick up the slack.

The lesson: A metasearch engine is only as reliable as its weakest dependency. Spread the load.

Anonymity Has Layers

SearXNG is one layer. It anonymizes who is searching. But it doesn’t encrypt your connection to SearXNG itself (use HTTPS), it doesn’t hide that you’re running a search service (your ISP can see traffic to SearXNG’s upstream engines), and it doesn’t protect you if you click a result and browse without protection.

The real privacy stack is defense in depth: AdGuard Home for DNS filtering, SearXNG for search anonymity, a privacy-focused browser (Firefox + uBlock Origin), and a VPN for network-level protection.

The lesson: No single tool is a silver bullet. Layer your defenses.

Results Vary By Engine Mix

With 2-3 engines enabled, results are sparse and biased toward whichever engine responds first. With 20+ engines enabled, results are slow and full of duplicates. The sweet spot I’ve landed on is 8-12 general engines, plus category-specific engines for images, news, and IT.

The lesson: More engines isn’t always better. Tune your mix based on what you actually search for.

CAPTCHAs Are a Feature, Not a Bug

When Google starts throwing CAPTCHAs at your SearXNG instance, that’s not a problem — that’s validation. It means Google can’t distinguish your server from a human browsing the web. The anonymization is working. SearXNG automatically marks engines that return CAPTCHAs as temporarily unavailable and falls back to others.

The lesson: Don’t panic when you see CAPTCHA warnings in the SearXNG stats page. It means the system is working as designed.

Hosting Means Responsibility

If your instance is publicly accessible (no authentication, no VPN), bots will find it within days. They’ll use it as an open proxy, burning through your bandwidth and getting your IP flagged by upstream engines. I’ve seen reports of public instances getting hundreds of thousands of queries per day from automated abuse.

The lesson: If it’s personal, lock it down. If it’s public, be prepared to manage it like a service. There’s no middle ground.


$ tree ~/privacy-stack/

The Privacy Stack: Where SearXNG Fits

Privacy isn’t a product. It’s a stack. Each layer protects against a different threat, and no single layer is sufficient on its own.

Layer 4: Network       [VPN / WireGuard]
          |             Encrypts all traffic, hides destination
          v
Layer 3: Browser       [Firefox + uBlock Origin]
          |             Blocks trackers, fingerprinting, scripts
          v
Layer 2: Search        [SearXNG]         <-- You are here
          |             Anonymizes search queries from engines
          v
Layer 1: DNS           [AdGuard Home]
          |             Blocks ad/tracker domains at network level
          v
Layer 0: Infrastructure [HA DNS]
                         Reliable DNS resolution

Each layer is independent. You can run any combination. But the more layers you stack, the smaller your attack surface becomes.

Layer Tool What It Protects TheDeLay.com Article
DNS (Layer 1) AdGuard Home Blocks tracker/ad domains network-wide Step Aside, Pi-Hole
Search (Layer 2) SearXNG Anonymizes search queries This article
Infrastructure (Layer 0) HA DNS Ensures DNS never goes down The #1 DNS Mistake
Backups Duplicati / n8n Protects your configs (including SearXNG) Backups Let You Sleep

$ exit

Own Your Search

Go back to myactivity.google.com. Look at it one more time.

Now imagine that list is empty. Not because you deleted it — because it was never created. Because your searches never touched Google’s servers with your identity attached. Because the server that processes your queries is sitting in your closet, under your control, logging nothing.

Your search history is a map of your mind. Every question you ask reveals what you’re thinking, what you’re worried about, what you’re curious about. That map has been in Google’s hands for years.

Take it back.

SearXNG won’t make you invisible. It won’t protect you from every threat. But it removes one of the largest, most intimate data collection pipelines from your life. And it does it for free, with open source software, on hardware you already own.

Fifteen minutes of setup. A lifetime of searches that belong to you. Now go deploy it. Then go search for something you’d never Google.

Similar Posts

  • The Hackerlab

    Every family holiday starts with a question. “What should we pack?” “Who’s bringing the good snacks?” For me, it started with a slightly different question: “Could I build a private cell tower for our VRBO?” That ambition, while legally and financially… problematic, sparked a new mission. I would turn our holiday rental into a pop-up,…

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *