Skip to content

Instantly share code, notes, and snippets.

@northboot
Last active March 14, 2025 08:45
Show Gist options
  • Save northboot/e4e5f73748262267ff4fdda10491e574 to your computer and use it in GitHub Desktop.
Save northboot/e4e5f73748262267ff4fdda10491e574 to your computer and use it in GitHub Desktop.
Installation guide for SearXNG (FreeBSD / HardenedBSD)

Table of contents

  1. Overview
  2. Basic installation
    1. Installing packages
    2. Adding user
    3. Installing searXNG
    4. Configuring searXNG
    5. Setting up uWSGI
    6. Starting searXNG
  3. Setting up a reverse proxy
  4. Public server setup
    1. Nginx configuration
    2. (Optional) Enable built-in rate limiting
    3. (Optional) Additional bot blocking
    4. (Optional) Improve performance
  5. Additional hardening (OS-level)
    1. Installation inside Jail
    2. Limit system resource usage
  6. Update and Maintain

Overview

This guide describes how to install searXNG on a private or public server running FreeBSD or HardenedBSD, whereby installing searXNG on HardenedBSD is no different than installing it on vanilla FreeBSD.

This guide is based on the official docs.

⚠️ Don't just copy-paste any of the configuration files that are provided here, make sure you understand what they are doing by yourself. No guarantee is given for their correctness.

Basic installation

Installing packages

Install the python runtime, uWSGI, git and other mandatory tools from the ports collection:
pkg install python311 py311-sqlite3 uwsgi-py311 git-tiny libxslt ca_root_nss

⚠️ This will force to install the Python 3.11.x series, which is EOL 2027-10. Make sure to upgrade before reaching that date.

Adding user

Create a new unprivileged user to later run searXNG as:
pw useradd -n searx -m -M 750 -s csh -w no

Installing searXNG

Clone the git repository to the newly created users home directory:
su -l searx -c 'git clone "https://github.com/searxng/searxng" searxng'

Create a new virtual environment where we will install the required python modules into:
su -l searx -c 'python3.11 -m venv venv'

Make the virtual environment activates automatically as soon as the user is logging in:
su -l searx -c 'echo "source /home/searx/venv/bin/activate.csh" >>~/.cshrc'

ℹ️ To test if the virtual environment has been set up properly, execute the following command:
su -l searx -c 'pip -V'
Output should look like this:
pip 24.0 from /usr/home/searx/venv/lib/python3.11/site-packages/pip (python 3.11)

Update/Install some basic needed packages via pip:
su -l searx -c 'pip install -U pip setuptools wheel pyyaml pybind11'

Install searXNG itself:
su -l searx -c 'cd searxng && pip install --use-pep517 --no-build-isolation -e .'

Configuring searXNG

Under FreeBSD, configuration files from third-party applications belong to /usr/local/etc/, so we change the environment variable read by searXNG for that file:
su -l searx -c 'echo "setenv SEARXNG_SETTINGS_PATH /usr/local/etc/searx.yml" >>~/.cshrc'

Create empty configuration file with appropriate permissions:
touch /usr/local/etc/searx.yml && chown root:searx /usr/local/etc/searx.yml && chmod 640 /usr/local/etc/searx.yml

Now edit the file /usr/local/etc/searx.yml we just created and paste some basic sample configuration in there:

# SearXNG settings
# For further configuration, see https://docs.searxng.org/admin/installation-searxng.html#configuration

use_default_settings: true

general:
  debug: false
  instance_name: "change_me" # <<< CHANGE THIS

server:
  secret_key: "ultrasecretkey" # <<< CHANGE THIS
  public_instance: false
  limiter: false
  image_proxy: false # Change to true if you plan to create a public instance 

redis:
  url: false

Do not forget to create a new secret key, e.g. by running cat /dev/urandom | env LC_CTYPE=C tr -dc a-zA-Z0-9 | head -c 50 ; echo

⚠️ This will configure a basic searXNG setup, which is NOT intended to be used as a public instance. If you want to make your instance public, read the chapter Public server setup afterwards.

Setting up uWSGI

Create the directory that will contain the configuration file:
mkdir /usr/local/etc/uwsgi

Create the configuration file /usr/local/etc/uwsgi/searx.ini and paste the following config in there:

# -*- mode: conf; coding: utf-8  -*-
[uwsgi]

pidfile = /var/run/searx/uwsgi/searx.pid

disable-logging = true

uid = searx
gid = searx

env = LANG=C.UTF-8
env = LANGUAGE=C.UTF-8
env = LC_ALL=C.UTF-8

chdir = /home/searx/searxng/searx

env = SEARXNG_SETTINGS_PATH=/usr/local/etc/searx.yml

chmod-socket = 660

single-interpreter = true
master = true
lazy-apps = true
enable-threads = true

module = searx.webapp
virtualenv = /home/searx/venv
pythonpath = /home/searx/searxng

socket = /var/run/searx/nginx/searx.socket
buffer-size = 8192

static-map = /static=/home/searx/searxng/searx/static
static-expires = /* 31557600
static-gzip-all = True
offload-threads = %k

Starting searXNG

SearXNG itself will be started using uWSGI which we just configured. To start uWSGI, create the file /usr/local/etc/rc.d/searx and paste the following content in there:

#!/bin/sh

# PROVIDE: searx
# REQUIRE: LOGIN FILESYSTEMS
# KEYWORD: shutdown

. /etc/rc.subr

name="searx"
rcvar="searx_enable"
: ${searx_enable:=NO}

pidfile="/var/run/searx/uwsgi/searx.pid"
procname="/usr/local/bin/uwsgi"

PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"

command="/usr/local/bin/uwsgi"
command_args="--ini /usr/local/etc/uwsgi/searx.ini --daemonize /dev/null"

stop_cmd="${name}_stop"
searx_stop()
{
/usr/local/bin/uwsgi --stop /var/run/searx/uwsgi/searx.pid
while [ "$(ps -x -U searx | grep  $(cat /var/run/searx/uwsgi/searx.pid))" != "" ]; do
  echo "Waiting for UWSGI to shut down..."
  sleep 1
done
echo "Finished shutting UWSGI down."
}

start_precmd="${name}_prestart"
searx_prestart()
{
if [ -f /var/run/searx/uwsgi/searx.pid ] || [ -f /var/run/searx/nginx/searx.socket ]; then
  echo "Warning: Service was not shut down properly!"
  rm -f -- /var/run/searx/uwsgi/searx.pid /var/run/searx/nginx/searx.socket
fi
if ! [ -d /var/run/searx ]; then
  mkdir -m 0775 /var/run/searx && chown root:wheel /var/run/searx
fi
if ! [ -d /var/run/searx/uwsgi ]; then
  mkdir -m 0770 /var/run/searx/uwsgi && chown root:uwsgi /var/run/searx/uwsgi
fi
if ! [ -d /var/run/searx/redis ]; then
  mkdir -m 0770 /var/run/searx/redis && chown searx:redis /var/run/searx/redis 2>/dev/null
fi
if ! [ -d /var/run/searx/nginx ]; then
  mkdir -m 0770 /var/run/searx/nginx && chown searx:nobody /var/run/searx/nginx
fi
}

stop_postcmd="${name}_poststop"
searx_poststop()
{
rm -f -- /var/run/searx/uwsgi/searx.pid /var/run/searx/nginx/searx.socket
}

load_rc_config ${name}
run_rc_command "$1"

ℹ️ The reason we don't use the provided rc script by the package uwsgi-py311 is for improving security by not making any UNIX socket accessible by a user not intended for accessing it and to make sure any runtime-related files are unified in the directory /var/run/searx.

Now make the rc script executable:
chmod a+x /usr/local/etc/rc.d/searx

To start/stop searXNG use the well-known commands:
service searx onestart
service searx onestop

To confirm searXNG is running as intended, confirm the existance of the UNIX domain socket that will be used by a reverse proxy that will be configured in Setting up a reverse proxy:
sockstat -lu | grep searx

Output should look like this:

searx    uwsgi-3.11 2059  3  stream /var/run/searx/nginx/searx.socket
searx    uwsgi-3.11 1676  3  stream /var/run/searx/nginx/searx.socket

ℹ️ In case searXNG is not working properly, enable debug mode in /usr/local/etc/searx.yml and run uWSGI manually via command line (/usr/local/bin/uwsgi --ini /usr/local/etc/uwsgi/searx.ini) and observe its output.

Setting up a reverse proxy

We will use Nginx as our reverse proxy:
pkg install nginx

Edit /usr/local/etc/nginx/nginx.conf by removing its existing configuration and replace it with the following, recommended one:

user nobody;
worker_processes auto;

events {
        worker_connections 2048;
        multi_accept off;
}

http {
        sendfile on;
        tcp_nopush on;

        include /usr/local/etc/nginx/mime.types;
        default_type application/octet-stream;

        access_log /dev/null;
        error_log /dev/null;

        gzip on;

        server_tokens off;

        include /usr/local/etc/nginx/searx.vhost;
}

Also create the file /usr/local/etc/nginx/searx.vhost with the following content:

server {
    listen xxx.xxx.xxx.xxx:80; # <<< CHANGE THIS
    server_name _;

    root /dev/null/;

    if ($request_method !~ ^(GET|HEAD|POST)$ ) {
        return 405;
    }

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-DNS-Prefetch-Control "off";

    add_header Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self'; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self' https://overpass-api.de; img-src 'self' data: https://*.tile.openstreetmap.org; frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://www.dailymotion.com https://www.deezer.com https://www.mixcloud.com https://w.soundcloud.com https://embed.spotify.com";

    add_header Permissions-Policy "accelerometer=(); ambient-light-sensor=(); autoplay=(); battery=(); camera=(); display-capture=(); encrypted-media=(); fullscreen=(); geolocation=(); gyroscope=(); idle-detection=(); magnetometer=(); microphone=(); midi=(); payment=(); picture-in-picture=(); screen-wake-lock=(); serial=(); usb=(); web-share=(); xr-spatial-tracking=(); clipboard-read=(); clipboard-write=(); gamepad=(); speaker-selection=()";
    add_header Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; idle-detection 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture 'none'; screen-wake-lock 'none'; serial 'none'; usb 'none'; web-share 'none'; xr-spatial-tracking 'none'; clipboard-read 'none'; clipboard-write 'none'; gamepad 'none'; speaker-selection 'none'";

    location / {
        include uwsgi_params;
        uwsgi_pass unix:///var/run/searx/nginx/searx.socket;

        uwsgi_param    HTTP_HOST             $host;
        uwsgi_param    HTTP_CONNECTION       $http_connection;
        uwsgi_param    HTTP_X_SCHEME         $scheme;
        uwsgi_param    HTTP_X_REAL_IP        $remote_addr;
        uwsgi_param    HTTP_X_FORWARDED_FOR  $proxy_add_x_forwarded_for;
    }
}

Nginx can be simply started with the command service nginx onestart.
After starting Nginx, searXNG should be accessible using your webbrowser by navigating to the address configured in the server directive in /usr/local/etc/nginx/searx.vhost.

Public server setup

Nginx configuration

Public instances should be using (and enforcing) modern TLS encryption. For your convenience, the relevant configuration files for Nginx are provided as a sample here:

nginx.conf
user nobody;
worker_processes auto;

events {
        worker_connections 2048;
        multi_accept off;
}

http {
        sendfile on;
        tcp_nopush on;

        include /usr/local/etc/nginx/mime.types;
        default_type application/octet-stream;

        access_log /dev/null;
        error_log /dev/null;

        gzip on;

        server_tokens off;

        # Recommended TLS options disabling some weak ciphers
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
        ssl_session_cache shared:le_nginx_SSL:10m;
        ssl_session_timeout 1440m;
        ssl_session_tickets off;
        ssl_prefer_server_ciphers off;

        # Uncomment the following limit_* options if you want to use rate limiting on all client queries
        # (this is independent of the searXNG built-in rate limiter)
        #limit_req_zone $binary_remote_addr zone=limitReq:10m rate=80r/s;
        #limit_conn_zone $binary_remote_addr zone=limitConn:10m;

        # Uncomment the following line only if you are using "Nginx ultimate bad bot blocker" (see chapter 4.3)
        #include /usr/local/etc/nginx/conf.d/*.conf;
        include /usr/local/etc/nginx/searx.vhost;
}
searx.vhost
server {
  listen xxx.xxx.xxx.xxx:80; # <<< CHANGE THIS
  server_name example.com; # <<< CHANGE THIS

  root /dev/null/;

  # Uncomment the following line only if you are using "Nginx ultimate bad bot blocker" (see chapter 4.3)
  #include /usr/local/etc/nginx/bots.d/blockbots.conf;

  return 301 https://$host$request_uri; # Redirect to https
}

server {
    listen xxx.xxx.xxx.xxx:443 ssl http2; # <<< CHANGE THIS
    server_name example.com; # <<< CHANGE THIS

    root /dev/null/;

    # Uncomment the following line only if you are using "Nginx ultimate bad bot blocker" (see chapter 4.3)
    #include /usr/local/etc/nginx/bots.d/blockbots.conf;

    ssl_certificate /path/to/certificate.crt; # <<< CHANGE THIS
    ssl_certificate_key /path/to/private.key; # <<< CHANGE THIS

    if ($request_method !~ ^(GET|HEAD|POST)$ ) {
        return 405;
    }

    add_header Strict-Transport-Security "max-age=31536000; preload" always;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-DNS-Prefetch-Control "off";

    add_header Content-Security-Policy "upgrade-insecure-requests; default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self'; font-src 'self'; frame-ancestors 'self'; base-uri 'self'; connect-src 'self' https://overpass-api.de; img-src 'self' data: https://*.tile.openstreetmap.org; frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://www.dailymotion.com https://www.deezer.com https://www.mixcloud.com https://w.soundcloud.com https://embed.spotify.com";

    add_header Permissions-Policy "accelerometer=(); ambient-light-sensor=(); autoplay=(); battery=(); camera=(); display-capture=(); encrypted-media=(); fullscreen=(); geolocation=(); gyroscope=(); idle-detection=(); magnetometer=(); microphone=(); midi=(); payment=(); picture-in-picture=(); screen-wake-lock=(); serial=(); usb=(); web-share=(); xr-spatial-tracking=(); clipboard-read=(); clipboard-write=(); gamepad=(); speaker-selection=()";
    add_header Feature-Policy "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; encrypted-media 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; idle-detection 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; picture-in-picture 'none'; screen-wake-lock 'none'; serial 'none'; usb 'none'; web-share 'none'; xr-spatial-tracking 'none'; clipboard-read 'none'; clipboard-write 'none'; gamepad 'none'; speaker-selection 'none'";

    # Uncomment to block additional robots from indexing / archiving your site
    # Also comment the automatically set header by searXNG in its config file searx.yml, otherwise there will be two headers
    #add_header X-Robots-Tag "noindex, noimageindex, nosnippet, notranslate, noarchive, nofollow";

    location / {
        # Uncomment the following limit_* options if you want to use rate limiting on all client queries (see nginx.conf)
        # Don't forget to also uncomment the relevant options in nginx.conf
        #limit_req zone=limitReq burst=80;
        #limit_conn limitConn 80;
        #limit_rate 2m;
        #limit_rate_after 4m;

        include uwsgi_params;
        uwsgi_pass unix:///var/run/searx/nginx/searx.socket;

        uwsgi_param    HTTP_HOST             $host;
        uwsgi_param    HTTP_CONNECTION       $http_connection;
        uwsgi_param    HTTP_X_SCHEME         $scheme;
        uwsgi_param    HTTP_X_REAL_IP        $remote_addr;
        uwsgi_param    HTTP_X_FORWARDED_FOR  $proxy_add_x_forwarded_for;
    }
}

(Optional) Enable built-in rate limiting

While being optional, it is highly recommended to enable the built-in rate limiting capability from searXNG. The rate limiter requires a running redis instance, which will be installed like that:
pkg install redis62

⚠️ This will force to install the Redis 6.2.x series. Make sure to upgrade before this version reaches EOL.

Edit /usr/local/etc/redis.conf by removing its existing configuration and replace it with the following, recommended one:

daemonize yes
pidfile /var/run/searx/redis/redis.pid
supervised no
cluster-enabled no
gopher-enabled no
latency-monitor-threshold 0
syslog-enabled no
crash-log-enabled no
loglevel warning
logfile /var/log/redis/redis.log

always-show-logo no
set-proc-title no

protected-mode yes
save ""
appendonly no
acllog-max-len 0

port 0
unixsocket /var/run/searx/redis/redis.socket
unixsocketperm 777

timeout 0
databases 1

maxmemory 20M
maxmemory-policy allkeys-lfu
maxmemory-samples 5
oom-score-adj no

Also create the rc script for our redis instance by creating the file /usr/local/etc/rc.d/redis_searx with the following content:

#!/bin/sh

# PROVIDE: redis_searx
# REQUIRE: LOGIN FILESYSTEMS
# KEYWORD: shutdown

. /etc/rc.subr

name="redis_searx"
rcvar="redis_searx_enable"
: ${redis_searx_enable:=NO}
: ${redis_searx_user:=redis}
: ${redis_searx_group:=redis}

pidfile="/var/run/searx/redis/redis.pid"
procname="/usr/local/bin/redis-server"

command="/usr/local/bin/redis-server"
command_args="/usr/local/etc/redis.conf"

start_precmd="${name}_prestart"
redis_searx_prestart()
{
if [ -f /var/run/searx/redis/redis.pid ] || [ -f /var/run/searx/redis/redis.socket ]; then
  echo "Warning: Service did not shut down properly!"
  rm -f -- /var/run/searx/redis/redis.pid /var/run/searx/redis/redis.socket
fi
if ! [ -d /var/run/searx/redis ]; then
  mkdir -p -m 0775 /var/run/searx && chown root:wheel /var/run/searx
  mkdir -m 0770 /var/run/searx/redis
fi
chown searx:redis /var/run/searx/redis
}

stop_postcmd="${name}_poststop"
redis_searx_poststop()
{
rm -f -- /var/run/searx/redis/redis.pid /var/run/searx/redis/redis.socket
}

load_rc_config ${name}
run_rc_command "$1"

Make the rc script executable:
chmod a+x /usr/local/etc/rc.d/redis_searx

To start/stop the redis instance use the well-known commands:
service redis_searx onestart
service redis_searx onestop

⚠️ Make sure to start redis using our custom rc script, not the default one.

Now edit the searXNG configuration file and make sure the following options are set:

server:
  limiter: true
  public_instance: true
  image_proxy: true # Recommended to prevent clients from connecting directly to third-party websites

redis:
  url: unix:///var/run/searx/redis/redis.socket?db=0

If not done already, restart searXNG.

To confirm the limiter plugin is working properly, connect to the redis instance and output its current keys:

redis-cli -s /var/run/searx/redis/redis.socket
keys *

After performing a sample search query (in your browser), the output of keys * should look roughly like this:

1) "SearXNG_limiter.token"
2) "SearXNG_ddg_AXjaR2HXxgpsli1AYtaXaR9CrsIUZFpKN0m640lv6Lqc0rugRUe0KDZa0TbyZnBXJKO"

(Optional) Additional bot blocking

To increase the efforts in blocking bots, one can use nginx-ultimate-bad-bot-blocker (for Nginx) or apache-ultimate-bad-bot-blocker (for Apache).

Follow the install instructions and, if wanted, block some additional bots which are only rate-limited by default by the bot blocker by editing the file /usr/local/etc/nginx/bots.d/blacklist-user-agents.conf and add the following lines under the section "MY BLACKLIST":

# ------------
# MY BLACKLIST
# ------------

"~*(?:\b)AdsBot-Google(?:\b)"           3;
"~*(?:\b)Applebot(?:\b)"                3;
"~*(?:\b)DoCoMo(?:\b)"          3;
"~*(?:\b)Feedfetcher-Google(?:\b)"              3;
"~*(?:\b)Google-HTTP-Java-Client(?:\b)"         3;
"~*(?:\b)Googlebot(?:\b)"               3;
"~*(?:\b)Googlebot-Image(?:\b)"         3;
"~*(?:\b)Googlebot-Mobile(?:\b)"                3;
"~*(?:\b)Googlebot-News(?:\b)"          3;
"~*(?:\b)Googlebot-Video(?:\b)"         3;
"~*(?:\b)Googlebot/Test(?:\b)"          3;
"~*(?:\b)Gravityscan(?:\b)"             3;
"~*(?:\b)Jakarta\ Commons(?:\b)"                3;
"~*(?:\b)Kraken/0.1(?:\b)"              3;
"~*(?:\b)LinkedInBot(?:\b)"             3;
"~*(?:\b)Mediapartners-Google(?:\b)"            3;
"~*(?:\b)SAMSUNG(?:\b)"         3;
"~*(?:\b)Slackbot(?:\b)"                3;
"~*(?:\b)Slackbot-LinkExpanding(?:\b)"          3;
"~*(?:\b)TwitterBot(?:\b)"              3;
"~*(?:\b)Wordpress(?:\b)"               3;
"~*(?:\b)adidxbot(?:\b)"                3;
"~*(?:\b)aolbuild(?:\b)"                3;
"~*(?:\b)bing(?:\b)"            3;
"~*(?:\b)bingbot(?:\b)"         3;
"~*(?:\b)bingpreview(?:\b)"             3;
"~*(?:\b)developers.facebook.com(?:\b)"         3;
"~*(?:\b)duckduckgo(?:\b)"              3;
"~*(?:\b)facebookexternalhit(?:\b)"             3;
"~*(?:\b)facebookplatform(?:\b)"                3;
"~*(?:\b)gsa-crawler(?:\b)"             3;
"~*(?:\b)msnbot(?:\b)"          3;
"~*(?:\b)msnbot-media(?:\b)"            3;
"~*(?:\b)slurp(?:\b)"           3;
"~*(?:\b)teoma(?:\b)"           3;
"~*(?:\b)yahoo(?:\b)"           3;
"~*(?:\b)Presto(?:\b)"          3;
"~*(?:\b)Wget/1.15(?:\b)"               3;
"~*(?:\b)jetmon(?:\b)"          3;
"~*(?:\b)libwww-perl(?:\b)"             3;
"~*(?:\b)munin(?:\b)"           3;
"~*(?:\b)Alexa(?:\b)"           3;
"~*(?:\b)ArchiveTeam(?:\b)"             3;
"~*(?:\b)BUbiNG(?:\b)"          3;
"~*(?:\b)Baidu(?:\b)"           3;
"~*(?:\b)FlipboardProxy(?:\b)"          3;
"~*(?:\b)MSIE\ 7.0(?:\b)"               3;
"~*(?:\b)Proximic(?:\b)"                3;
"~*(?:\b)R6_CommentReader(?:\b)"                3;
"~*(?:\b)R6_FeedFetcher(?:\b)"          3;
"~*(?:\b)RED/1(?:\b)"           3;
"~*(?:\b)RPT-HTTPClient(?:\b)"          3;
"~*(?:\b)Spaidu(?:\b)"          3;
"~*(?:\b)UptimeRobot/2.0(?:\b)"         3;
"~*(?:\b)YandexBot(?:\b)"               3;
"~*(?:\b)YandexImages(?:\b)"            3;
"~*(?:\b)archive.org(?:\b)"             3;
"~*(?:\b)ia_archiver(?:\b)"             3;
"~*(?:\b)sfFeedReader/0.9(?:\b)"                3;

(Optional) Improve performance

On public instances, especially when bots are targeting your instance, you may want to increase the default limit on FreeBSD regarding the maximum number of pending connections awaiting acception on a single socket:
sysctl kern.ipc.somaxconn=2048

It is highly recommended to also make this change persistent across reboots:
echo "kern.ipc.somaxconn=2048" >>/etc/sysctl.conf

Now make uWSGI use the new limit by inserting the following line in /usr/local/etc/uwsgi/searx.ini:

[...]
listen = 2048
[...]

Additional hardening (OS-level)

Installation inside Jail

If it fits your threat model, searXNG (and redis and nginx) can be installed in a jail.

There are no specific installation steps required for that, the above guides do also apply for an installation inside a jail. If you are unsure about the configuration of the jail itself, use the following restrictive configuration, which has been proven working on HardenedBSD 13-STABLE:

/etc/jail.conf
allow.set_hostname = 0;
allow.chflags = 0;
allow.quotas = 0;
allow.read_msgbuf = 0;
allow.socket_af = 0;
allow.mlock = 0;
allow.unprivileged_proc_debug = 0;
allow.raw_sockets = 0;
allow.reserved_ports = 1;
allow.suser = 1;

allow.mount = 0;
allow.mount.devfs = 0;
allow.mount.fdescfs = 0;
# Kernel module disabled by default on HardenedBSD:
#allow.mount.fusefs = 0;
allow.mount.nullfs = 0;
allow.mount.procfs = 0;
# Kernel modules disabled by default on HardenedBSD:
#allow.mount.linprocfs = 0;
#allow.mount.linsysfs = 0;
allow.mount.tmpfs = 0;
allow.mount.zfs = 0;

# Kernel module disabled by default on HardenedBSD:
#linux = "new";
sysvmsg = "new";
sysvsem = "new";
sysvshm = "new";

enforce_statfs = 2;
# Also change /etc/devfs.rules
#devfs_ruleset 99;
mount.devfs;

nopersist;
securelevel = 3;
children.max = 0;

exec.clean;
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";

host = "new";
ip4 = "new";
ip6 = "disable";

searx {
    path = "change_me";
    stop.timeout = 15;
    host.hostname = "searx";
    interface = "change_me";
    ip4.addr = "change_me";
}
/etc/devfs.rules
[devfsrules_jail_hardened=99]
add hide
add path null unhide
add path zero unhide
add path crypto unhide
add path random unhide
add path urandom unhide

One can also apply some restrictions regarding system resource usage to the corresponding jail. Also useful if you want to prevent a buggy application from hogging your whole system in case of a memory leak for example.

/etc/rctl.conf

ℹ️ Sample values, change them as needed.

jail:searx:pcpu:deny=50
jail:searx:memoryuse:deny=4G
jail:searx:memorylocked:deny=0
jail:searx:maxproc:deny=32
jail:searx:openfiles:deny=25000
jail:searx:readbps:throttle=50M
jail:searx:writebps:throttle=15M

Limit system resource usage

The resource usage of the searXNG executable (or all executables running under the "searx" user respectively) can be limited by adding the following content to the file /etc/login.conf:

searx:\
        :coredumpsize=0:\
        :filesize=8G:\
        :maxproc=32:\
        :memorylocked=0:\
        :memoryuse=4G:\
        :tc=default:

Rebuild the database after changing the configuration file:
cap_mkdb /etc/login.conf

To make the new changes apply to the user "searx", change its login class like that:
pw usermod -n searx -L searx

Update and Maintain

Updating searXNG is pretty straigthforward:
su -l searx -c 'cd searxng && git pull'
su -l searx -c 'cd searxng && pip install --use-pep517 --no-build-isolation -e .'

Don't forget to restart searXNG afterwards: service searx onerestart

@northboot
Copy link
Author

I got ModuleNotFoundError: No module named 'pybind11'.

Added pybind11 to the pip and system packages, and it worked: su -l searx -c 'pip install -U pybind11 pkg install pybind11

Thanks for figuring this out. Installing the package in the venv should be enough, I've updated the gist accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment