Skip to main content

Overview

SearXNG is a privacy-respecting metasearch engine: it holds no index of its own, forwards each query to many upstream engines, and merges the results while keeping no profile and setting no tracking cookies. This template deploys it as a stateless workload serving the web UI and the JSON API on port 8080, with a bundled Redis, on by default, that backs the rate limiter and the shared state a multi-replica install needs. The JSON API is the reason this template exists. GET /search?q=...&format=json returns merged web results as JSON with no API key, no per-query bill and no vendor quota, which makes it a search backend an AI agent can call directly. formats ships as [html, json], so the API is live on a default install alongside the human UI. The signing key SearXNG uses for preference cookies and CSRF tokens is not a template value. The workload reads it from an opaque secret you create before installing, so the key never passes through Helm and never lands in the release.
This template deploys into an existing GVC that you already have. It does not create or manage a GVC.
A default install is private: publicAccess.enabled is false and limiter.enabled is false. Those two defaults belong together — see Choosing an Access Shape before publishing the instance.

What Gets Created

No volume set is created anywhere in this template. Everything durable is either your signing-key secret or a preference cookie in a user’s own browser, so an uninstall leaves no storage behind.

Prerequisites

One secret must exist before you install. It holds the key that signs preference cookies and CSRF tokens. Secrets are org-level, so no GVC flag is involved.
1

Create the signing-key secret

An opaque secret whose whole payload is the signing key. Generate a random value rather than choosing one:
Set secretKey.secretName to this name. Every replica reads the same secret, which is what lets any replica verify any user’s preference cookie.
Create the secret before installing, or the deployment wedges silently. A secretKey.secretName that points at a secret which does not exist installs successfully and then never starts. The container never runs, so cpln logs returns zero lines. The one place the reason appears is status.versions[].message:
Use get-deployments — plain cpln workload get has no versions key. Creating the missing secret repairs the deployment on its own within several minutes, or run cpln workload force-redeployment RELEASE_NAME-searxng --gvc GVC_NAME to skip the wait.
Treat the key as write-once and keep a copy outside Control Plane: replacing it discards every saved preference in every browser, and reinstalling against the same secret loses nothing. See Rotating the Signing Key. Nothing else is required — no cloud account, no domain, no external database.

Installation

Install the chart from the marketplace registry, pointing it at the secret you created:
Or follow the instructions for your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Each block below is the shipped default for that part of values.yaml.

SearXNG Server and Resources

Search Behavior

  • formats — a format that is not listed is refused, so removing json disables the API.
  • imageProxy — relays result images through this instance so a user’s browser never contacts the origin; on a public instance that also makes it an open image relay.
  • baseUrl — leave empty for the canonical *.cpln.app endpoint; set it only behind a custom domain. It drives the opensearch.xml document browsers use to add the instance as a search provider.
  • extraSettings — merged over the keys above, so anything in the upstream settings reference is reachable (engine selection, safe_search, outgoing timeouts). An override of a key this template also sets wins cleanly.
These keys live in the rendered settings.yml, not on the workload — after changing them, follow Changing Settings.

Signing Key

The opaque secret from Prerequisites. The chart grants the workload reveal on exactly that secret and injects it as an environment variable, so the key never appears in the rendered configuration.

Access

SearXNG has no login of any kind, so publicAccess.enabled: true serves the UI, the API, /config and /stats to anyone who finds the endpoint — turn limiter.enabled on first. Outbound access is always open and is not configurable: querying upstream engines over the public internet is the entire function of the service. A change to either access key can take several minutes to take effect; re-test before concluding it did not apply.

Redis

The bundled datastore exists for the limiter and for shared state across replicas. It stores no search results and nothing durable.
  • redis.enabled: false runs SearXNG standalone; limiter.enabled must then stay false, and the chart refuses to render the combination.
  • redis.redis.replicas must stay 1, and the chart rejects anything else: SearXNG’s client is not Sentinel-aware and has to reach the master directly, so extra replicas would be read-only and would silently break the limiter. One Sentinel is required by the redis template and cannot be scaled to zero.
  • Change redis.redis.auth.password.value from the placeholder if you enable AUTH. redis.redis.auth.fromSecret is not supported by this template.

Connecting

SearXNG has no login, so there are no credentials anywhere in this table. A default install is private: nothing outside Control Plane can reach it, and workloads inside the GVC reach it subject to internalAccess.type. To verify from your own machine, open a tunnel and call the API through it:
Then browse http://localhost:8080 for the UI. cpln port-forward is a top-level command, not a cpln workload subcommand.

First Run

There is no login and no setup wizard: open the endpoint and search.

Choosing an Access Shape

The rate limiter and the JSON API pull in opposite directions, and this is the decision to get right before you publish anything. The limiter is bot detection, not a simple request counter: it inspects each request and rejects anything that does not look like a real browser. A plain curl, a Python HTTP client or an AI agent therefore gets 429 Too Many Requests on /search — including format=json — while a browser-shaped request is served normally and then throttled. Turning the limiter off makes the JSON API usable by any client, and also means nothing throttles the instance, which is fine behind a closed firewall and a problem on the open internet. The combination to avoid is public with the limiter off: an open, unthrottled metasearch instance that anyone can drive. Upstream engines answer the traffic it attracts with CAPTCHAs and rate limits, which shows up as fewer results rather than an error. Because SearXNG has no login, the limiter is the only control a public instance has. To publish the instance, set both publicAccess.enabled: true and limiter.enabled: true in your values, upgrade the release, then read the assigned *.cpln.app address:
The chart enforces at render time that limiter.enabled: true requires redis.enabled: true.

Using the JSON API

From another workload in the same GVC, using the fully-qualified internal name:
The response carries the merged results and the engines that answered. Useful query parameters include categories (for example general, images, news), language, time_range, safesearch and pageno; the full set is in the upstream search API reference. Keep limiter.enabled: false for this. If the instance must be public and callable by an agent, the agent has to send browser-shaped headers — a realistic User-Agent plus Accept, Accept-Language and Accept-Encoding — and it is still throttled like any browser.

Operations

Changing Settings

instanceName, formats, imageProxy, limiter.enabled and extraSettings are rendered into settings.yml, which the workload reads from the RELEASE_NAME-searxng-settings secret mounted as a file. A cpln helm upgrade that changes only those keys rewrites the secret but leaves the workload itself unchanged, and a running replica does not pick up a changed secret file on its own. After such an upgrade, force a redeployment so every replica restarts against the new file:
Changes to image, replicas, resources, baseUrl, publicAccess or internalAccess alter the workload spec and roll it out on their own.

Rotating the Signing Key

Rotation is not a routine operation: preferences live in a cookie signed with this key, so a new key discards every saved preference in every browser. Rotate only if the key is compromised. cpln secret update cannot change a secret’s value — it edits only the description and tags. Write the new value with cpln apply, then force a redeployment; running replicas keep the key they started with until they restart:

Scaling and Availability

  • replicas scales the SearXNG tier horizontally. Instances share nothing on disk and there is no server-side session to pin, so any replica can serve any request; with redis.enabled: true the limiter’s counters are shared across replicas.
  • The bundled datastore is a single Redis instance and the chart keeps it that way. If it restarts, limiter counters reset and nothing is lost, because search results were never stored there.
  • With limiter.enabled: true, a SearXNG container that starts while the datastore is unreachable refuses to start rather than coming up silently unthrottled. With the limiter off, it logs a warning and starts normally.
  • The first cpln helm upgrade after an install can re-apply resources that did not change, including the datastore workloads, which restarts them once. At replicas: 1 budget a brief interruption on that first upgrade.
  • A workload runs in every location its GVC has. Extra locations multiply both the instance count and the outbound query volume upstream engines see from you, so install into a single-location GVC unless you mean otherwise.
  • There is nothing to back up: the template holds no durable data of its own. Keep a copy of your signing key outside Control Plane, and a reinstall against the same secret keeps every preference cookie valid.

Troubleshooting

Symptom: the install succeeds, RELEASE_NAME-searxng stays not ready indefinitely, and cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-searxng"}' --limit 50 --since 10m returns zero lines.Cause: secretKey.secretName names a secret that does not exist, so the container is never started. The message is only visible in status.versions[].message:
Fix: create the secret as in Prerequisites. The deployment recovers on its own within several minutes, or run cpln workload force-redeployment RELEASE_NAME-searxng --gvc GVC_NAME.
Symptom: GET /search?q=...&format=json from curl, an HTTP client library or an agent returns 429 Too Many Requests on every call, while the same search works from a browser.Cause: limiter.enabled: true. The limiter is bot detection and rejects requests that do not look like a real browser.Fix: for an agent backend, run the private shape (limiter.enabled: false, publicAccess.enabled: false) — see Choosing an Access Shape. If the instance must stay public, the client has to send browser-shaped headers and accept being throttled.
Symptom: /search?...&format=json returns 403 Forbidden while the HTML UI works.Cause: json was removed from formats. SearXNG refuses any output format that is not listed there.Fix: add json back to formats, upgrade, and force a redeployment as in Changing Settings.
Symptom: with limiter.enabled: true, RELEASE_NAME-searxng restarts instead of becoming ready, and its logs end with:
Cause: the start-up script could not reach the bundled datastore within its wait window, and with the limiter on it refuses to serve an instance that believes it is rate-limited and is not.Fix: check RELEASE_NAME-redis with cpln workload get-deployments RELEASE_NAME-redis --gvc GVC_NAME -o yaml and its logs. If redis.redis.auth.password.enabled is on, make sure redis.redis.auth.password.value is set. Once the datastore is healthy, SearXNG starts on its next restart.
Symptom: cpln helm install or cpln helm upgrade stops before touching any resource:
Cause: your values set limiter.enabled: true with redis.enabled: false.Fix: enable the datastore, or turn the limiter off. The two settings are validated together at render time.
Symptom: cpln helm install or cpln helm upgrade stops before touching any resource:
Cause: your values raise redis.redis.replicas above 1.Fix: leave redis.redis.replicas at 1. Scale SearXNG itself with replicas instead.
Symptom: you changed instanceName, formats, imageProxy, limiter.enabled or extraSettings, the upgrade reported the settings secret updated, but the running instance still shows the old behaviour.Cause: those keys live in a secret mounted as a file, and a running replica does not re-read a changed secret.Fix: cpln workload force-redeployment RELEASE_NAME-searxng --gvc GVC_NAME — see Changing Settings.

Important Notes

  • Create the signing-key secret before installing. A reference to a secret that does not exist wedges the workload with no log output; see Prerequisites for the one command that shows the reason.
  • Treat the signing key as write-once and keep a copy. Rotating it invalidates every saved preference cookie — see Rotating the Signing Key.
  • There is no authentication of any kind. With publicAccess.enabled: true the UI, the API, /config and /stats are served to anyone who finds the endpoint. Turn limiter.enabled on before you publish.
  • The limiter blocks the JSON API for non-browser clients. Pick one of the two shapes in Choosing an Access Shape deliberately; never run public with the limiter off.
  • Settings changes need a forced redeployment. settings.yml is a mounted secret and running replicas do not re-read it — see Changing Settings.
  • redis.redis.replicas must stay 1, and limiter.enabled requires redis.enabled. Both are enforced at render time, so a bad combination fails the install rather than shipping a silently unlimited instance.
  • /metrics is off as shipped and needs a password to open. Upstream gates it on general.open_metrics, which is empty by default; setting it through extraSettings enables the route behind HTTP basic auth with that password. extraSettings: {general: {enable_metrics: false}} stops collecting the data behind /stats.
  • Uninstalling removes everything the template created — workloads, secrets, identities and policies, with no volume sets left behind. Your own signing-key secret is yours and is left alone.

External References

SearXNG Project

Project home, public instance list, and overview

Settings Reference

Every key that can be set through extraSettings

Server Settings

The server.* block, including the limiter and image proxy

The Limiter

How bot detection and rate limiting work upstream

Search API

Parameters and response format for /search

SearXNG Template

Source files, default values, and chart definition