SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-10-25

SearXNGを自己ホストする方法とDocker Compose設定

自分のVPSでSearXNGを運用する手順です。settings.yml、limiter、nginxのTLS設定、スクリプトから呼べるJSON検索APIまで、Docker Composeで構築します。

構築するもの

SearXNG を自己ホストすると、自分のサーバー上で動作するプライベート検索エンジンを利用できます。SearXNG はメタ検索エンジンです。検索クエリを受け取り、Google、Bing、DuckDuckGo、Wikipedia などの検索エンジンに問い合わせ、返された結果を1つの結果ページにまとめます。クエリを保持するマシンは自分のものだけなので、プロファイルは作成されず、追跡用 Cookie も設定されません。単に Searx と呼ばれていた古いガイドを見つけた場合、それがこのプロジェクトのフォーク元です。Searx は、独自のリポジトリでメンテナンス終了が告知された September 2023 以降、メンテナンスされていません。その後のコミットも May 2026 のドキュメント更新が1件あるだけで、状況は変わっていません。したがって、どちらを利用するか決める前に 両方の状態を確認してください。

構成は小規模です。コンテナ2つ、設定ファイル1つ、リバースプロキシ1つで構成します。小規模な VPS でも問題なく運用できます。ただし、すべての自己ホストサービスがそうとは限りません。PhotoPrism と Immich の比較で扱った写真ライブラリでは、必要な RAM の下限を Web アプリではなくインデクサーが決めます。実際に決めるべきなのは、インスタンスをプライベートにするか、パブリックにするかです。プライベートとは、自分と自分のスクリプトだけがアクセスできる状態です。パブリックとは、インターネット上の誰でも検索できる状態です。この選択によってセキュリティ設定が変わるため、何かを入力する前に決めてください。デフォルトではプライベートを選びます。

もう1つ、運用する理由があります。SearXNG のインスタンスは JSON を扱うため、作成したスクリプトや AI エージェントから、自分で管理する検索 API を利用できます。API key は不要で、クエリごとの課金もなく、クォータに関する通知メールも届きません。

Docker Compose で SearXNG をインストールする

このプロジェクトはコンテナイメージと Compose ファイルを公開しています。Docker Engine と Compose plugin がすでにインストールされている新しい Ubuntu 24.04 サーバーに、両方を取得します。Docker に慣れていない場合は、まず VPS での Docker Compose の基本 を読んでから戻ってきてください。

sudo install -d -o "$USER" -g "$USER" -m 750 /opt/searxng
cd /opt/searxng
mkdir -p core-config
curl -fsSL \
  -O https://raw.githubusercontent.com/searxng/searxng/master/container/docker-compose.yml \
  -O https://raw.githubusercontent.com/searxng/searxng/master/container/.env.example
cp -i .env.example .env

Compose ファイルでは、2 つのサービスを定義しています。core は SearXNG 自体で、valkey はレート制限と短時間だけ保持する状態情報に使うインメモリデータストアです。コンテナ内の /etc/searxng/ にホスト上の ./core-config/ をマウントするため、設定するすべての内容がホスト上の 1 つのディレクトリに集約されます。

次に .env を編集します。配布されたサンプルではすべての行がコメントアウトされています。そのため、コンテナはすべてのアドレスの port 8080 で起動します。次の 3 つの設定をコメント解除して指定します。

SEARXNG_VERSION=latest
SEARXNG_HOST=127.0.0.1
SEARXNG_PORT=8080

重要なのは SEARXNG_HOST=127.0.0.1 です。これにより、公開ポートは単独の 8080:8080 ではなく 127.0.0.1:8080:8080 になります。8080:8080 は Docker によってすべてのアドレスで公開されるため、SEARXNG_HOST=127.0.0.1 を設定するとコンテナは loopback アドレスでのみ応答し、インターネットから直接到達できなくなります。これを省略すると、コンテナの起動直後から外部に公開されます。公開された Docker port は、ファイアウォールのルールより前段に挿入されるためです。この落とし穴については、公開された Docker port は ufw を迂回する で詳しく説明しています。

SEARXNG_VERSION=latest は、学習中であれば問題ありません。重要なサーバーでは、tag を固定してください。2026 年 10 月時点では、release tag は日付ベースで、2026.10.4-d48c4b555 のような形式です。そのため、固定したデプロイメントは、registry の変更時ではなく、自分で決めたタイミングでアップグレードできます。サーバー上で長期間稼働させる他のソフトウェアにも、同じ方針が有効です。そのため、自己ホスト型 RustDesk relay でも image tag を固定しています。リモートアクセスサービスが無人でアップグレードされると、最悪のタイミングで変更に気付くことになるためです。

settings.yml: 重要な設定項目

初回起動の前に core-config/settings.yml を作成します。use_default_settings: true を指定すると、SearXNG は標準のデフォルト設定を読み込んだ後、記述したキーのみを上書きします。これにより設定ファイルが簡潔に保たれ、新しいオプションが追加されるアップグレードにも対応できます。

値が直接ファイルに書き込まれるため、最初にシークレットを生成してください。

openssl rand -hex 32
use_default_settings: true

general:
  instance_name: "search.example.com"

server:
  base_url: "https://search.example.com/"
  secret_key: "paste-the-openssl-output-here"
  limiter: false
  public_instance: false
  image_proxy: true

valkey:
  url: valkey://valkey:6379/0

search:
  safe_search: 0
  autocomplete: "duckduckgo"
  formats:
    - html
    - json

secret_key はセッションとトークンのデータに署名します。標準のデフォルト値は文字列 ultrasecretkey ですが、このままではデフォルト値を知る第三者がトークンを偽造できるため、必ず一度変更してください。一度設定した後は変更しないでください。変更すると保存済みの設定がすべて破棄されます。

base_url は末尾のスラッシュを含む公開 HTTPS アドレスである必要があります。これは SearXNG が生成するリンクに反映されます。localhost を指定したままにすると、リモートブラウザー上の「次のページ」リンクが閲覧者自身のマシンを指すことになり、リンクが機能しません。ブラウザーが検索エンジンの登録に使う OpenSearch の記述もこの値から生成されるため、SearXNG をブラウザーの既定の検索エンジンにする前に、正しいアドレスになっているか確認してください。

formats は Web エンドポイントが生成する出力形式を決定します。json はデフォルトのリストに含まれていないため、追加しない限り JSON リクエストは 403 エラーを返します。image_proxy: true は結果のサムネイルをサーバー経由で転送するため、画像をホストしているサイトが閲覧者の IP アドレスを直接知ることはありません。

valkey.url はホスト名 valkey を使用します。これは Compose ファイル内のサービス名であり、Compose は両方のコンテナを同一ネットワーク上に配置してサービス名で名前解決できるようにするためです。これを localhost に向けるとリミッターは機能しません。core コンテナ内部における localhost は、そのコンテナ自身を指すためです。

シークレットはプレーンテキストファイルに保存されるため、ディレクトリの保護も行ってください。chmod 750 /opt/searxng を設定することで、他のホストユーザーによるアクセスを防げます。また、core-config/settings.yml を 600 に変更しても問題ありません。公式コンテナのエントリポイントは root で起動し、FORCE_OWNERSHIP がデフォルトの true のままであれば、SearXNG の起動前にマウントされた設定ディレクトリをコンテナ内の searxng ユーザーに引き渡すため、ファイルは読み取り可能です。副作用として、ホスト側では core-config/ の所有者がコンテナ内のユーザー ID に変更されます。そのため、以降の編集には sudo が必要になる点に注意してください。

スタックを起動し、状態を確認します。

cd /opt/searxng
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080/

docker compose ps を実行し、両方のコンテナが running 状態であることを確認します。curl は HTTP/1.1 200 OK を返すはずです。何も返答がない場合は docker compose logs core を確認してください。settings.yml 内の YAML 記述ミスは、行番号を明示したパースエラーとしてそこに記録されます。

nginx の背後に置いて TLS を有効にする

コンテナは loopback でのみ待ち受けるため、nginx によって外部からアクセスできるようにします。nginx は transport layer security (TLS) も追加します。/etc/nginx/sites-available/searxng を記述します。

server {
    listen 80;
    server_name search.example.com;

    location / {
        proxy_pass http://127.0.0.1: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;
    }
}
sudo ln -s /etc/nginx/sites-available/searxng /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d search.example.com

nginx -t は、reload 前に syntax is ok と test is successful を出力します。Certbot は同じファイルを書き換え、証明書を使用して 443 番ポートで待ち受ける設定にし、80 番ポートからのリダイレクトも追加します。認証局は HTTP 経由でファイルを取得して所有権を証明するため、search.example.com の DNS レコードは事前にこのサーバーを指している必要があります。更新手順を含む詳細な手順は、Ubuntu 24.04 向けの Certbot と nginx のガイドにあります。

2 つの転送用ヘッダーは飾りではありません。X-Forwarded-For と X-Real-IP がない場合、SearXNG に到着するすべてのリクエストにはプロキシのアドレスが設定されます。そのため、レートリミッターは 1 つのクライアントがすべてのトラフィックを送信していると判断し、訪問者を区別できません。

スクリプトやエージェントが JSON 検索 API を必要とする理由

formats 内の json を有効にすると、Web ページをレンダリングするのと同じエンドポイントから構造化データを取得できます。

curl -s 'http://127.0.0.1:8080/search?q=wireguard+mtu&format=json' \
  | jq -r '.results[0:5][] | .url'

返されるオブジェクトには results 配列が含まれます。各エントリには url、title、content、および結果を提供したエンジンが含まれ、さらに answers、infoboxes、suggestions も併せて取得可能です。これは要約ツール、リンクチェッカー、あるいは調査ループに渡すデータとして十分な内容です。これらの結果を言語モデルに渡す作業は見た目以上に慎重さが必要です。検索結果は信頼できないテキストであり、それ自体が命令を含んでいる可能性があるためです。この点については SearXNG インスタンスを AI エージェントに接続する で詳しく解説しています。

これはエージェント型のあらゆるツールにとって重要です。言語モデルには学習データのカットオフがあるため、現在に関する質問に答えるにはライブ検索が必要です。商用の検索 API はクエリごとに課金され、厳しいレート制限が課されます。ローカルインスタンスであれば、すでに料金を支払っているサーバー上のコンテナ 1 つ分で済み、クエリが外部へ流出することもありません。モデルにツールを組み込む際の考え方は VPS で MCP サーバーを運用する 場合と同様であり、検索ツールは通常、最初に導入される機能です。

API 利用には 2 つのルールがあります。インスタンスを非公開に保つため、API 側はループバックアドレスかプライベートネットワークにバインドし、自身のホストからのみアクセスできるようにしてください。これは セルフホスト型の位置情報トラッカー が、スマートフォンから位置情報を報告するエンドポイントに対して行うべき対策と同じです。また、クエリは控えめに行ってください。SearXNG はリクエストを実際の検索エンジンへ転送するため、スクリプトで毎秒 100 回ものクエリを実行すると、Google からサーバーがブロックされる原因となります。

公開インスタンスでの limiter と設定の違い

limiter は SearXNG のボット対策です。リクエストヘッダー、アドレス、リクエスト頻度を監視し、自動化されたものに見えるネットワークトラフィックを破棄します。この状態を保持するために Valkey が必要なので、Compose ファイルに含まれています。

プライベートインスタンスでは limiter: false のままにします。独自のスクリプトは定義上、自動化されたトラフィックです。そのため、インスタンスを用意した目的である JSON 呼び出しを limiter がブロックします。アクセス制御は代わりにリバースプロキシで行います。nginx の location に設定する allow と deny の組み合わせ、HTTP basic authentication、または他のサーバーからの接続だけを許可するファイアウォールを使います。ネットワークを移動する laptop からプライベートインスタンスへ接続する必要がある場合は、v3 onion アドレスを前段に置く方法もあります。tor はインターネットに新たな公開を行わず、同じ loopback ポートへ接続できるためです。

他の利用者向けにインスタンスを公開する場合は、両方のスイッチを有効にします。

server:
  limiter: true
  public_instance: true

より細かい制御は core-config/limiter.toml で行います。このファイルをコンテナが /etc/searxng/limiter.toml から読み込みます。変更するキーだけを記述します。プロキシの背後で運用する場合は、プロキシを宣言する必要があります。宣言しないと、limiter は nginx のアドレスを不正なクライアントのアドレスとして扱います。

[botdetection]
trusted_proxies = [
  '127.0.0.0/8',
  '::1',
]

[botdetection.ip_limit]
link_token = true

link_token = true を有効にすると、SearXNG は実際のブラウザーセッションだけが取得するトークンを発行します。これにより、単純な scraper の大半を阻止できます。公開インスタンスには数日以内に scraper が集まると考えてください。engine エラーも発生します。転送するトラフィックが増えるほど、upstream engine はサーバーのアドレスに対して早く CAPTCHA を返し始めるためです。公開 SearXNG インスタンスは継続的な運用が必要です。プライベートインスタンスにはその必要がありません。そのため、2026 年に self-hosting する価値があるものの短い一覧の多くに含まれています。ただし、そのような一覧のすべてがインフラというわけではありません。Jellyfin のライブラリを歩いて回れる 90s のレンタル店として再構築する場合も、同じ nginx ブロックの背後に同じ 1 つのコンテナを置き、workflow ではなく夕方の時間帯を指し示す構成です。

検索結果が返らない場合

インスタンス上で /stats を開きます。ここには各エンジンのエラー率と応答時間が一覧表示されます。結果が少ないと感じたときは、まずここを確認します。

「Access denied」または「CAPTCHA」エラーが表示されるエンジンは、サーバーのアドレスをブロックしています。データセンターのアドレス範囲では、検索エンジンがスクレイパーのものと判断するため、この問題がよく発生します。SearXNG はそのエンジンへの再試行を続けず、一定期間停止します。そのため、1 つのエンジンがブロックされるだけで、結果からひそかに外れます。settings.yml で無効にするか、その影響を受け入れます。ただし、対応はこの 2 つだけではありません。再起動後も有効な CAPTCHA ブロックの解決策がある場合もあります。残りのエンジンは引き続き応答します。429 は判断が難しいケースです。自分の rate limiter が原因の場合と、上流のエンジンがサーバーからの要求を拒否している場合があるためです。設定を変更する前に、ログの行からどちらの原因かを確認できます。

すべてのエンジンが同時に失敗する場合、コンテナ内で外向きの名前解決が機能していないか、インターネットへの経路がありません。コンテナ内からテストします。

docker compose exec core wget -qO- https://duckduckgo.com > /dev/null && echo ok

このチェックが失敗し始めても、box 上のどの機能も通知しません。そのため、cron から実行し、結果が少なくなったことに気付くまで待つのではなく、自分の ntfy サーバーからスマートフォンにアラートを送信させます。

FAQ

SearXNG を使うと検索は匿名になりますか?

検索先のエンジンから利用者を隠せます。エンジンからはブラウザーではなく、あなたのサーバーがリクエストを送信しているように見えるためです。ただし、検索内容があなたのサーバーから隠れるわけではありません。また、あなたのサーバーが検索先のエンジンから隠れるわけでもありません。単一ユーザー用のインスタンスでは、そのアドレスからのすべてのトラフィックがあなたのものになるため、アドレス自体が識別子になります。ブラウザーとインスタンス間のトラフィックは TLS 証明書によって保護されます。ISP、公開インスタンスの運用者、検索先のエンジンから何が隠れるかについては、SearXNG が実際に隠すもので説明しています。

JSON リクエストが 403 Forbidden を返すのはなぜですか?

原因は2つあり、どちらも設定に関係します。settings.yml の search: にある formats リストに json がないことが原因です。これはデフォルトの状態です。もう1つの原因は limiter が有効で、スクリプトが bot と判定されたことです。最初に format を追加し、docker compose restart core で再起動してから、もう一度試してください。それでも失敗する場合は limiter: false を設定し、reverse proxy でアクセスを制御してください。

limiter を無効にしている場合、Valkey コンテナは必要ですか?

起動したままにしてください。SearXNG は Valkey なしでも動作しますが、Valkey がないと後から limiter を有効にできません。また、他の短時間だけ保持する状態も保存します。コンテナは小さく、cached data だけを保存するため、削除しても節約できる容量はごくわずかです。その一方で、将来 limiter を使う選択肢を失います。

SearXNG を更新するにはどうすればよいですか?

/opt/searxng で docker compose pull、続いて docker compose up -d を実行します。Compose は image が変更されたコンテナを再作成し、core-config/ ディレクトリはそのまま保持するため、settings.yml は維持されます。use_default_settings: true は出荷時のデフォルト設定に独自のキーをマージするため、upstream で追加されたオプションもファイルを壊さず、適切な値で利用できます。

複数の人で1つのインスタンスを共有できますか?

はい。共有する場合は limiter を有効にし、public_instance: true を設定します。設定は各利用者自身のブラウザーに保存されるため、管理するアカウントはありません。公開後の1週間は /stats を監視してください。upstream のエンジンは、検索結果が欠落していることに気付くよりもずっと早く、あなたのサーバーからのリクエストを拒否し始めることがあります。

この記事は役に立ちましたか?