How to Self-Host SearXNG: A Private Metasearch Engine with HTTPS

A step-by-step SearXNG setup using CapRover, with complete configuration files, password protection, HTTPS, search checks, and troubleshooting.

Share
Line-art illustration of a server stack with the top unit highlighted in rust, thin lines fanning out to small dots representing the search services it queries.

SearXNG is a search application that collects results from other search engines and presents them on one page. It does not maintain its own web index. You send a query to your instance; your instance sends requests to the search services it uses.

This guide installs SearXNG using CapRover, a web dashboard for deploying Docker containers. You will create the configuration files, upload them, protect the search page with a password, and enable HTTPS. You do not need prior SearXNG experience, but you do need a working CapRover installation before starting.

What you will build

The request path will look like this:

Your browser
    | HTTPS, with a username and password
    v
CapRover's Nginx reverse proxy
    | HTTP over the internal container network
    v
SearXNG container, listening on port 8080
    | Outbound requests
    v
Upstream search engines

A container image packages the application and its dependencies. A container is a running instance of that image. A reverse proxy receives browser requests and forwards them to the application. Here, CapRover manages the containers and the Nginx reverse proxy; Nginx handles the HTTPS certificate and password prompt.

The result is a password-protected instance for personal use, not a public search service. public_instance: false is a SearXNG behavior setting, not an access restriction. The password protection is a separate step below.

Self-hosting also has limits: upstream engines still receive your queries from the server, and sites you open from the results see your browser. Do not treat SearXNG as an anonymity service.

Before you start

Have these ready:

  • A Linux server with Docker and CapRover installed, and access to its working dashboard over HTTPS. If you do not have this yet, complete the CapRover installation guide first, including its DNS and dashboard HTTPS setup.
  • A domain you control and access to its DNS records.
  • Public access to ports 80 and 443 on the server for the certificate workflow used here. Port 80 handles HTTP and certificate validation; port 443 handles HTTPS. Do not open the SearXNG container's port 8080 to the internet.
  • A computer with a Bash-compatible terminal, nano, tar, curl, and openssl. On Windows, use a Linux environment such as WSL. You will prepare files on this computer and upload an archive through your browser; Docker does not need to be installed on it.
  • Enough server capacity for another container and an image build. Check available RAM and disk rather than relying on a fixed installation-size estimate.

Commands marked local terminal run on the computer where you prepare the files. Commands marked server terminal run after you connect to the CapRover server using SSH. Dashboard steps run in your browser.

Choose the search address

The examples use search.example.com. Replace it everywhere with the hostname you want readers or devices to use.

Your CapRover dashboard has a different address, commonly captain.apps.example.com. CapRover's default hostname for an app named search would then be search.apps.example.com, not search.example.com. You can use that default hostname or add a custom one. This walkthrough adds search.example.com as a custom domain.

In your DNS provider's control panel, create an A record for the search hostname pointing to the server's public IPv4 address. Providers often want only search in the name field when managing example.com. Start with a direct, DNS-only record rather than adding another proxy in front of CapRover.

Do not add an AAAA record unless the server is reachable over IPv6. An incorrect AAAA record can break access or certificate validation even when the A record is correct.

Checkpoint: from your local terminal, run:

nslookup search.example.com

The response should include your server's public IPv4 address. If nslookup is unavailable, use your DNS provider's lookup tool or install your operating system's DNS utilities. Fix incorrect or missing records before continuing.

1. Create the deployment files

In your local terminal, create a directory and enter it:

mkdir -p "$HOME/searxng-deploy"
cd "$HOME/searxng-deploy"

You will create three files in this directory:

searxng-deploy/
    Dockerfile
    settings.yml
    captain-definition

Dockerfile describes the image to build. settings.yml contains your SearXNG settings. captain-definition tells CapRover which Dockerfile to use. The filenames are case-sensitive; captain-definition has no extension.

Create settings.yml

Run:

nano settings.yml

Paste this configuration, replacing the example hostname with your own:

use_default_settings: true

general:
  instance_name: "Personal search"

server:
  base_url: "https://search.example.com/"
  limiter: false
  public_instance: false
  image_proxy: true
  method: "GET"

search:
  safe_search: 1
  autocomplete: ""
  formats:
    - html
    - json

In nano, press Ctrl+O, then Enter to save, and Ctrl+X to exit. Use spaces, not tabs, for YAML indentation.

These settings make the following choices:

  • use_default_settings: true keeps the upstream defaults for options you have not listed. You do not need to copy the entire upstream configuration.
  • base_url is your instance's full address, including https:// and the final slash. SearXNG uses it when generating links to itself.
  • limiter: false leaves SearXNG's bot and request-rate filter disabled. This guide instead restricts visitors through password protection. The built-in limiter requires a Valkey database; it is worth considering for shared instances, but is not a substitute for authentication.
  • public_instance: false leaves public-instance features disabled. It does not prevent strangers from connecting.
  • image_proxy: true lets SearXNG proxy result images through your server instead of having the browser fetch those images directly from their source. That has a bandwidth and resource cost, but privacy value is not determined by how many people use the instance.
  • method: "GET" puts search queries in the URL, which makes bookmarking convenient. Those URLs may appear in browser history and request logs. HTTPS encrypts transport; it does not remove local history or server logs. The server settings documentation explains the GET/POST trade-offs.
  • safe_search: 1 requests moderate filtering where an engine supports it. It is not a guarantee that every result is suitable for every audience.
  • autocomplete: "" disables suggestions, so typing a partial query does not trigger autocomplete requests.
  • formats allows both the web interface (html) and machine-readable responses (json). The search settings reference documents these options.

The application secret will go into CapRover separately, so this file does not contain it.

Create Dockerfile

Run:

nano Dockerfile

Paste:

FROM searxng/searxng:2026.10.16-ff8f5ddcc

COPY settings.yml /etc/searxng/settings.yml
RUN chown searxng:searxng /etc/searxng/settings.yml \
 && chmod 0600 /etc/searxng/settings.yml

Save and exit as before.

This builds on the official SearXNG image and copies your settings into the location SearXNG reads at startup. The ownership and permissions allow the application's user to read the file.

The tag shown is a specific image version, not a command to use the newest release forever. Check the official image tags when setting up a new instance and review updates deliberately. An image digest provides stricter immutability than a tag if you need that guarantee.

Copying configuration into the image is convenient for this deployment method: changing the file means rebuilding and redeploying. A mounted configuration directory is also supported in the official container guide. Do not add a mount over /etc/searxng in this walkthrough, because it would hide the configuration copied into the image.

Create captain-definition

Run:

nano captain-definition

Paste:

{
  "schemaVersion": 2,
  "dockerfilePath": "./Dockerfile"
}

Save and exit. This is the Dockerfile-based definition format CapRover expects.

Package the files

Still in the same directory, run:

tar -cvf search-deploy.tar Dockerfile settings.yml captain-definition
tar -tf search-deploy.tar

Checkpoint: the archive listing should contain those three filenames directly, without a parent searxng-deploy/ directory. Keep this directory for future updates. The archive is the file you will upload.

2. Create and protect the CapRover app

Open your CapRover dashboard in the browser.

  1. Go to Apps and create an app named search. If that name is already taken, choose another and use its app page throughout the remaining steps.
  2. Select Has Persistent Data when creating it.
  3. Open the app's App Configs area. Under Persistent Directories, click Add Persistent Directory. Enter /var/cache/searxng as Path in App and search-cache as Label, leave the host-path field empty, and save. The label creates a Docker-managed named volume rather than mapping a specific directory on the server.
  4. Leave the instance count at one. The cache contains generated data such as favicons; the deployment files remain the source of your application configuration.
  5. In HTTP Settings, set Container HTTP Port to 8080 and save. This tells the reverse proxy where SearXNG listens inside its container. Leave host Port Mapping empty so users cannot bypass the proxy through a separately published port.
  6. In the same HTTP settings, choose Edit HTTP Basic Auth. Set a username such as searchuser and a strong, unique password, confirm the dialog, and save the HTTP settings. Confirm that the authentication status is active.

These are current dashboard labels; their placement may differ between CapRover versions. CapRover documents HTTP routing and port configuration and persistent-directory behavior separately.

Basic authentication creates the browser's username/password prompt. It is sufficient for a small personal deployment, but it is not multi-factor authentication or full user management. Keep CapRover's dashboard restricted and updated, and use a stronger access gateway if your requirements call for one.

Checkpoint: authentication is active and there is no host mapping for port 8080. Do this before uploading SearXNG. If the app has no running application yet, a gateway error at this point does not mean the SearXNG build has failed; it has not been deployed.

3. Add the domain and HTTPS

In the app's HTTP Settings:

  1. Add your chosen custom domain, search.example.com, and save.
  2. Click Enable HTTPS beside that domain. CapRover requests a Let's Encrypt certificate.
  3. Once issuance succeeds, enable Force HTTPS by redirecting all HTTP traffic to HTTPS and save.

CapRover also assigns a default app hostname. Check whether HTTPS is already active for it; if it is not, enable it before using that address. Remove unused hostnames if your version allows it. The dashboard warns you to enable HTTPS for each domain you intend to use before forcing HTTPS. Password protection must cover every hostname that reaches the app, not just the custom one.

Do not enter the search password into an HTTP page. HTTP Basic authentication needs HTTPS to protect credentials in transit.

From your local terminal, test the custom address without supplying credentials:

curl -sS -o /dev/null -w '%{http_code}\n' https://search.example.com/

Checkpoint: expect 401, meaning the proxy requires authentication. curl should not report a certificate error. Do not use -k to suppress certificate validation.

If certificate issuance fails, check DNS and public reachability on port 80 first. This guide uses CapRover's normal HTTP-based validation. A server intentionally unreachable from the internet needs a different certificate workflow, such as DNS-based validation with managed renewal; do not disable your firewall indiscriminately to force this walkthrough to fit.

4. Set the application secret and deploy

In your local terminal, generate a secret:

openssl rand -hex 32

Copy the generated value into your password manager. Do not use the literal command as the value, and do not reuse the Basic Auth password. This secret is for SearXNG's cryptographic functions; it is not a browser login.

In the app's App Configs, add an environment variable:

  • Name: SEARXNG_SECRET
  • Value: the generated hexadecimal string

Save the configuration. The SearXNG server reference documents this environment variable. Keeping it at runtime avoids embedding the secret in the image or upload archive. Administrators with access to CapRover or Docker can still inspect runtime environment values.

Next:

  1. Open the app's Deployment tab.
  2. Find the option to upload a tar archive and choose search-deploy.tar from your local deployment directory.
  3. Start the upload/deployment and watch the build output.
  4. Wait for the build and deployment to finish. Read any reported error instead of repeatedly uploading the same archive.

This uses CapRover's dashboard deployment method. Repository or CLI deployment is useful for ongoing automation, but is not required to get this instance running.

Checkpoint: open https://search.example.com/ in your browser. Enter the Basic Auth username and password. You should reach the SearXNG search page. Search for SearXNG documentation and confirm that results appear. A working landing page alone does not establish that the server can reach upstream engines.

5. Verify access, redirects, and search results

Use your local terminal for these checks, replacing the hostname and username where needed.

First, repeat the unauthenticated HTTPS test from step 3. It must still return 401 after deployment. Check any retained default app hostname too. If an unauthenticated request displays the search page, stop and fix the authentication settings.

Check that HTTP redirects to HTTPS:

curl -sS -o /dev/null -w '%{http_code} %{redirect_url}\n' \
  http://search.example.com/

Expect a redirect status such as 301 or 302, with an https:// destination. Do not send credentials in this HTTP request.

Now request the HTTPS page with authentication:

curl -sS --user searchuser -o /dev/null -w '%{http_code}\n' \
  https://search.example.com/

curl prompts for the password when you provide only a username. This avoids putting the password directly into shell history. Expect 200.

Finally, test the JSON format enabled in settings.yml:

curl --fail-with-body --silent --show-error --user searchuser \
  --get --data-urlencode 'q=SearXNG documentation' \
  --data-urlencode 'format=json' \
  https://search.example.com/search \
  -o search-check.json

If your curl version does not support --fail-with-body, use --fail instead. Inspect search-check.json in your editor. It should be JSON with a results array. An empty array is not proof of a broken installation, but repeated empty results for ordinary queries deserve investigation. The response may also identify unresponsive engines.

The installation is ready for personal use when unauthenticated visitors are blocked, HTTP redirects to HTTPS, the certificate validates, and authenticated searches return useful results. Not every enabled upstream engine has to succeed on every query.

Troubleshooting

Start with the symptom you actually see. Do not apply a networking workaround merely because another deployment needed it.

The deployment cannot find a required file

Check the filenames and archive listing from step 1. A common mistake is uploading an archive with an extra parent directory or naming the definition captain-definition.json. CapRover must find captain-definition at the archive root, and its Dockerfile path must match your actual file.

The browser returns 502 Bad Gateway

This means the proxy could not get a usable response from the application. Verify that Container HTTP Port is 8080, then inspect application logs in the dashboard.

If needed, use the server terminal:

sudo docker service ls

Find the service corresponding to your app. Use the exact name shown; naming differs between CapRover versions. Replace APP_SERVICE_NAME below with that name:

sudo docker service logs --tail 100 APP_SERVICE_NAME

Look for configuration errors, permission failures, or repeated restarts. YAML indentation problems require fixing settings.yml, rebuilding the archive, and redeploying. A replica count of 1/1 alone does not prove the web application is ready.

JSON returns 403, or your settings appear ignored

Check that json is listed under search.formats. Confirm the uploaded archive contains the edited file and that the last build completed.

An environment variable can override a file setting. Also check that you did not mount a volume over /etc/searxng, hiding the baked-in file. The container can generate a configuration when one is missing, so a successful startup does not establish that it loaded your intended settings.

A 401 is different: it means the proxy did not accept your Basic Auth credentials. Fix authentication before debugging the JSON format.

Some engines time out or report network errors

Separate a DNS failure from a connection failure. A message such as Network is unreachable can indicate missing IPv6 connectivity, but seeing an AAAA DNS record is not proof of the cause. Clients differ in how they choose addresses and fall back to IPv4.

In the server terminal, list containers and identify the one for this app:

sudo docker ps --format '{{.ID}}\t{{.Names}}'

Replace APP_CONTAINER_ID and ENGINE_HOSTNAME below with the actual container ID and the upstream hostname mentioned in the error:

sudo docker exec APP_CONTAINER_ID getent ahosts ENGINE_HOSTNAME

That checks name resolution inside the container, not whether an HTTPS request succeeds. Compare it with application logs and the server's own connectivity before changing Docker networking. If you use a proxy, check its DNS and connectivity too.

Avoid blanket edits to /etc/gai.conf: they depend on the image's C library and whether the application's HTTP client uses the system resolver. They are not a portable fix for every SearXNG image. The outgoing-request settings are the relevant reference for supported timeouts, proxy configuration, and source-address options.

An upstream engine returns 429 or a CAPTCHA

An upstream 429 means that service is rate-limiting requests. Causes can include your request frequency, shared server IP reputation, or the engine's bot policy. The status alone does not prove which one applies.

Pause requests, honor a Retry-After header if supplied, and check whether other engines still return results. SearXNG has engine-suspension settings for these failures; repeatedly refreshing can make the situation worse. You can disable an unreliable engine in the web interface's preferences while using the others.

A dedicated IP does not guarantee acceptance, and switching addresses to evade a block is not a reliable maintenance strategy. Review the engine's access rules and SearXNG updates. If the 429 comes from your own reverse proxy or gateway rather than an upstream engine, investigate that layer instead.

Updating, rollback, and removal

Keep a protected backup of the deployment directory and a separate record of the runtime secret, app settings, authentication settings, and volume mapping. The image archive does not contain all of those. Do not put secret values into a public repository.

Update SearXNG

  1. Preserve the currently working files and archive before editing them.
  2. Review the available image tags and upstream changes, then change the FROM line to the version you want.
  3. Check whether that version requires configuration changes.
  4. Recreate search-deploy.tar using the packaging command from step 1 and upload it through the Deployment tab.
  5. Repeat the access-control, HTTPS, and search checks.

Roll back a failed update

In CapRover's Deployment history, use the revert control beside the previous working version. Alternatively, upload your preserved working archive again.

CapRover's rollback documentation makes an important distinction: reverting the image does not revert environment variables, persistent-directory mappings, or other app settings. Restore any settings you changed separately. Generated cache data is also outside the image rollback.

Uninstall the instance

Delete the app through CapRover only when you intend to remove the service. Review its cache-volume deletion options carefully and delete only the volume belonging to this instance. Remove the search DNS record when you no longer need it, and retain configuration backups if you may rebuild later. Uninstalling is not a rollback procedure.

If you do not use CapRover

CapRover is one deployment option, not a SearXNG requirement. The official container installation guide recommends its current Compose templates. Plain Docker with your own reverse proxy or a Kubernetes deployment are other possibilities.

On another platform, you still need to provide configuration, protect access if the instance is personal, arrange HTTPS, and verify actual searches. Use that platform's deployment and secret-management mechanisms rather than copying the CapRover dashboard steps.

Your instance's useful endpoint is now its HTTPS search page. Keep the source files, password, and application secret recoverable, and treat upstream engine failures separately from failures in your own deployment.

## Convertkit Newsletter