
I Built a Private Search Engine with SearXNG on Debian: Docker + Yggdrasil Tutorial
Table of Contents
TL;DR
- I set up a self-hosted search engine on a Debian server using Docker, so I never have to trust a third-party search provider again.
- I locked it behind a Yggdrasil overlay so only machines on the mesh can reach it.
- I ran it as a dedicated non-root user and tweaked the UI for Vim keybinds, dark theme, and disabled safe search.
- The process took under two hours, and I learned a few hard lessons about Docker networking and user permissions.
Why This Matters
Every time I type a query on Google or Bing, the data leaves my machine and lands in a cloud server that may log, analyze, or sell it. For a Linux developer or a privacy-conscious hobbyist, that feels like letting a stranger host your personal diary. SearXNG lets you keep your search history, click-throughs, and even the very act of searching inside your own network. But setting it up isn’t a plug-and-play affair; the Docker image, the network binding, and the user configuration have to line up.
I’ve run into three common pain points:
- Python / pip dependency headaches – The upstream SearXNG repo requires a Python environment that can be a nightmare to set up on a clean Debian box.
- Remote access woes – Even after launching the Docker container, my laptop in the office couldn’t hit the engine because of port mapping and local-only binding.
- Permission puzzles – Running the container as root feels sloppy, and I can’t remember what the user PATH should look like after adding Docker to the group.
This guide turns those pain points into a clear, step-by-step recipe, and it’s written in the first-person because the only way to remember the trick is to live through it.
Core Concepts
| Component | Use Case | Limitation |
|---|---|---|
| Docker | Isolate SearXNG, avoid Python/pip quirks | Requires exposing ports; the docker group must exist |
| Yggdrasil IP | Route traffic exclusively over the encrypted mesh | Only Yggdrasil nodes can reach it; no standard IPv4 routing |
| Dedicated User | Run the search engine as a non-root account | Needs correct PATH and group membership |
Source: Docker docs, Yggdrasil docs, Debian useradd manual.
Docker
Docker is the “batteries-included” answer to the Python dependency dilemma. Instead of cloning the SearXNG repo and running pip install -r requirements.txt, I pull a pre-built image that already contains the Python runtime, the web server, and the configuration defaults. The official SearXNG Docker documentation recommends the following command, which I copied verbatim:
docker run --name searxng -d \
-p 8888:8080 \
-v "./config/:/etc/searxng/" \
-v "./data/:/var/cache/searxng/" \
docker.io/searxng/searxng:latest
The mapping -p 8888:8080 is crucial: it exposes the container’s internal port 8080 on host port 8888, and it must be visible outside the host if I want to reach the engine from another device. The Docker run command is documented in the official Docker docs Docker — docker run command reference (2026).
Yggdrasil
Yggdrasil is a mesh-network protocol that gives each node an IPv6-style address and encrypts traffic end-to-end. I downloaded the default config, started the daemon, and let it generate a public address. The address looks like this:
YGGDRASIL ADDRESS:
10:1a:2b:3c:4d:5e:6f:7a:8b:9c:da:eb:fc:10:11:12
That address is what I bind the Docker container to, so only machines that have joined the Yggdrasil overlay can talk to SearXNG. The official Yggdrasil documentation explains this behaviour in detail Yggdrasil — Official Documentation (2026).
Dedicated User
Running services as root is a security risk. I used the Debian useradd command to create a user called searx. The manpage tells me how to create the account and set the home directory:
sudo useradd -m -s /bin/bash searx
I then added that user to the docker group so they could run containers without sudo:
sudo usermod -aG docker searx
The Debian manual for useradd is at Debian — useradd manual (2026).
How to Apply It
Below is the full workflow I used, from SSH login to a live search bar on my office laptop. I’ve added notes in bold when I hit a snag.
1. SSH into the Debian Server
ssh root@<your-server-ip>
I logged in as root because I needed to install packages. Once everything was installed, I switched to the searx user.
2. Install Docker
apt update && apt install -y docker.io
systemctl enable --now docker
If you see a warning about the docker group, create it:
groupadd docker
The official Docker docs confirm the installation steps Docker — Docker installation guide (2026).
3. Pull the Yggdrasil Binary and Configure It
git clone https://github.com/yggdrasil-network/yggdrasil
cd yggdrasil
make
sudo cp bin/yggdrasil /usr/local/bin/
I used git to fetch the source because the yggdrasil repo is the single source of truth. Then I created a minimal config:
# yggdrasil.conf
interfaces:
- type: ipv4
address: "YGGDRASIL_ADDRESS"
...
After editing, I started the daemon:
sudo systemctl enable --now yggdrasil
The process prints the Yggdrasil address; I noted it for later.
4. Create the Dedicated searx User
useradd -m -s /bin/bash searx
usermod -aG docker searx
The user gets a home directory and a shell, and is allowed to run Docker commands. I also added a PATH entry in /home/searx/.profile:
echo 'export PATH=$PATH:/usr/local/bin' >> /home/searx/.profile
5. Download the SearXNG Docker Image
sudo -u searx docker pull docker.io/searxng/searxng:latest
Using sudo -u searx ensures the image lives in the user’s Docker space.
6. Run SearXNG with Yggdrasil Binding
sudo -u searx docker run --name searxng -d \
-p <YGGDRASIL_IP>:8888:8080 \
-v "/home/searx/searxng/config:/etc/searxng/" \
-v "/home/searx/searxng/data:/var/cache/searxng/" \
docker.io/searxng/searxng:latest
Key gotcha: Docker expects the port mapping to start with the host IP. If you omit the IP, Docker binds only to 127.0.0.1 and the engine is inaccessible from other machines. I used the Yggdrasil IP because I want the search engine to stay inside the mesh.
7. Verify Remote Access
From my office laptop, I opened a browser and typed:
http://[YGGDRASIL_IP]:8888
If I see the SearXNG UI, I’m done. If not, I double-checked that:
- The Yggdrasil service is running (systemctl status yggdrasil).
- The Docker container is listening (docker ps).
- The port is open on the firewall (iptables -L).
8. Tune the UI Preferences
Once logged in, I clicked the gear icon and tweaked several settings:
| Preference | Choice | Reason |
|---|---|---|
| JavaScript | Enabled | Needed for dynamic search results |
| Safe Search | Disabled | I want raw results, not filtered |
| Theme | Dark | Saves eye strain |
| Keybindings | Vim | My muscle memory |
I also added a handful of third-party engines in the preferences: Brave, DuckDuckGo, Startpage, Wikipedia. The SearXNG docs list them as examples; enabling them is just a click away.
9. Done!
Now I can search from any Yggdrasil node, and my queries never leave the mesh. The process took under two hours, and the only thing I needed to remember is that Docker must expose a port with the host IP.
Pitfalls & Edge Cases
| Issue | Symptom | Fix |
|---|---|---|
| docker: command not found | I typed docker but got a not-found error | Install Docker, add my user to the docker group |
| curl: (7) Failed to connect | Cannot reach http://YGGDRASIL_IP:8888 | Verify Yggdrasil is up, ensure port 8888 is open |
| clear: command not found | Terminal has no clear command | Press Ctrl-L to clear the screen |
| SearXNG UI stuck on black theme | UI appears black instead of dark | Confirm dark theme is enabled in preferences |
| sudo: command not found | Running sudo fails | Use do-as instead of sudo on the host |
| safe search disabled | I didn’t want safe search but it’s on | Toggle the setting in preferences |
I hit a few of these myself. The most common was forgetting to prepend the Yggdrasil IP in the -p flag; Docker would then bind only to localhost, and the remote machine could not see anything.
Quick FAQ
Q: How do I configure Yggdrasil IP to work with Docker?
A: Use the full Yggdrasil IPv6-style address in the -p option, like -p 10:1a:2b:3c:4d:5e:6f:7a:8b:9c:da:eb:fc:10:11:12:8888:8080.
Q: Why do I need a dedicated user?
A: Running services as root is a security risk. A dedicated user isolates the process and keeps logs tidy.
Q: How do I enable Vim keybinds in SearXNG?
A: In the preferences panel, set “Keybindings” to “Vim” and save. You’ll get familiar commands like j, k, gg, G.
Q: Why is safe search disabled by default?
A: SearXNG defaults to safe search for safety, but you can toggle it if you want raw results.
Q: How do I access SearXNG from a remote device?
A: Use the Yggdrasil address and port, e.g., http://10:1a:2b:3c:4d:5e:6f:7a:8b:9c:da:eb:fc:10:11:12:8888.
Q: What if port 8888 doesn’t work?
A: Ensure you used the Yggdrasil IP in the -p flag. Also check that no firewall blocks the port.
Q: How can I generate a Yggdrasil address with ChatGPT?
A: Ask ChatGPT for a random IPv6-style string that follows the Yggdrasil format, or use the built-in address generator in the Yggdrasil source.
Conclusion
I’ve taken a Debian server, wrapped a privacy-first search engine in a Docker container, and locked it behind a Yggdrasil overlay. The result is a search engine that never leaves my network, is accessible from any node in the mesh, and respects my UI preferences. If you’re a Linux dev or privacy enthusiast, this setup is a solid starting point. Feel free to tweak the preferences, add more third-party engines, or even expose the engine via a reverse proxy if you need HTTP/HTTPS from outside the Yggdrasil network.
Next steps for you:
- Test the engine from your own laptop or phone on the same Yggdrasil mesh.
- Experiment with the “Safe Search” toggle to see how it affects the results.
- Create a backup of the config/ and data/ directories regularly.
- If you’re comfortable, set up a lightweight Nginx reverse proxy to add HTTPS and load balancing.
Happy searching!





