Overview
SearXNG is a privacy-respecting metasearch engine: it holds no index of its own, forwards each query to roughly 200 upstream engines, and merges the results while keeping no profile and setting no tracking cookies. This template deploys it as a stateless workload on port8080, with the JSON API switched on, plus a small bundled datastore that backs the optional rate limiter.
The JSON API is the reason this template exists. GET /search?q=...&format=json returns merged web results as JSON — no API key to obtain, no per-query bill, and no vendor rate limit — 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 install is private by default: publicAccess.enabled is false and limiter.enabled is false. Those two defaults belong together, and choosing differently is the one real decision this template asks of you — see Choosing an Access Shape.
Architecture
- SearXNG server — A
standard(stateless) workload serving the UI and the API on port8080.replicasinstances run behind the platform load balancer with no session affinity, because there is no server-side session to pin: preferences live in a cookie in the user’s own browser. - Redis and Sentinel (optional, on by default) — The redis template as a subchart, pinned to one master and one sentinel. It holds the rate limiter’s counters and the shared state a multi-replica install needs. It is not a result cache — search results are never stored there.
- Settings — The rendered
settings.ymlis mounted read-only at/etc/searxng/settings.ymlfrom a template-created secret. - Signing key — Supplied by you as a prerequisite secret and injected as an environment variable, so it is never written into the rendered configuration and never enters the Helm release.
- No volume set, no database, and no login. Everything durable is either your prerequisite secret or a cookie in a user’s browser.
What Gets Created
- Standard SearXNG Workload —
{release}-searxng, serving the UI and API on port8080withreplicasreplicas and configurable CPU and memory. - Stateful Redis and Sentinel Workloads —
{release}-redison6379and{release}-sentinelon26379, created only whenredis.enabledistrue. Both are in-memory; neither mounts a volume. - Settings Secret — An opaque secret (
{release}-searxng-settings) holding the renderedsettings.yml. - Start-Script Secret — An opaque secret (
{release}-searxng-startup) holding the container’s start-up script, created only when the bundled datastore is enabled. - Identity & Policy — An identity bound to the SearXNG workload and a policy granting it
revealon exactly the secrets it reads: the settings secret, your prerequisite signing key, the start-up script, and the datastore password whenredis.redis.auth.password.enabledistrue. The Redis subchart brings its own identity, policy, and config secret.
This template does not create a GVC. You must deploy it into an existing GVC.
Prerequisites
One opaque secret must exist before you install. It holdsserver.secret_key, which signs preference cookies and CSRF tokens. Because it is a secret you create and reference by name, the value never passes through Helm values and never lands in the release.
secretKey.secretName to the name you used. Installing with an empty secretKey.secretName fails at render time.
Nothing else is required: no cloud account, no domain, no external database.
Installation
To install, 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
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 the request and rejects anything that does not look like a real browser. A plaincurl, a Python HTTP client, or an AI agent therefore gets 429 Too Many Requests on every route it cares about — /search, /search?format=json, and /config alike — while a browser-shaped request is served normally and then throttled at a burst ceiling of 15 searches. Both halves were measured: with the limiter on, 25 plain-client requests returned 429 every time, and 25 browser-shaped searches returned 200 fifteen times and 429 for the rest.
Turning the limiter off makes the JSON API usable by any client. It 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 respond to the traffic it attracts by throttling or blocking your egress address, and the result is a slow decline in result quality rather than an outright failure. That degradation is real and was observed during testing — after roughly 150 rapid queries, DuckDuckGo, Brave, and Startpage all began answering with CAPTCHA and “too many requests” responses, and result counts fell from 20 to single digits before recovering.
SearXNG has no login of any kind, so the limiter is the only control a public instance has.
Two constraints the chart enforces at render time:
limiter.enabled: true requires redis.enabled: true, and redis.redis.replicas must stay 1.
Reaching the UI
A default install is private, so a port-forward is the operator’s path to it. This works even when the workload is closed to the internet and to the GVC (internalAccess.type: none), because the tunnel does not go through the data-plane firewall:
http://localhost:8080. Note that cpln port-forward is a top-level command, not a cpln workload subcommand.
To publish the UI instead, upgrade with publicAccess.enabled: true and limiter.enabled: true, then read the assigned *.cpln.app address from status.canonicalEndpoint:
Firewall changes are not instantaneous. Publishing the endpoint took 20 seconds to start returning
200 in testing, and closing internal access took about 50 seconds to start refusing in-GVC callers. Allow up to a couple of minutes and re-test rather than concluding the knob did not work. A port-forward keeps serving throughout.A port-forward is unreliable while replicas are being replaced — during a scale-down or a redeployment the tunnel can follow a replica that is going away. Re-run it once the deployment has settled.
Configuration
The defaultvalues.yaml for this template:
Image and resources
image— The SearXNG server image. Pin a concrete tag.replicas— Number of instances.1is the proven single shape;2or more form a load-balanced tier that shares limiter state through the bundled datastore. At two replicas, a rolling restart served 300 of 300 requests and an abrupt replica loss served 75 of 75, with the replacement serving 11 seconds later.resources.minCpu/resources.maxCpu/resources.minMemory/resources.maxMemory— CPU reservation and limit, memory reservation and limit for the SearXNG container.
Search Behavior
instanceName— The name shown in the UI and returned by/config.formats— Output formats the instance will serve. Allowed values arehtml,json,csv, andrss; upstream defaults tohtmlonly, and this template ships[html, json]. A format that is not listed is refused with403, so removingjsondisables the API.imageProxy— Whentrue(default), result images are relayed through this instance instead of being hotlinked from third parties, so a user’s browser never contacts the origin. On a public instance that also makes it an open image relay.baseUrl— Set only when the instance sits behind a custom domain (for examplehttps://search.example.com). Empty derives the URL from the request, which is correct for the canonical*.cpln.appendpoint. It drives theopensearch.xmldocument browsers use to add the instance as a search provider.limiter.enabled— The bot-detection rate limiter. Off by default; requiresredis.enabled. See Choosing an Access Shape before turning it on.extraSettings— Merged verbatim intosettings.ymlover the keys above, so anything in the upstream settings reference is reachable — engine selection,safe_search,outgoingtimeouts. An override of a key this template also sets wins cleanly instead of producing a duplicate YAML key.
Signing Key
secretKey.secretName— The name of your prerequisite opaque secret, whose whole payload is the signing key. Every replica reads the same one, which is what lets any replica verify any user’s preference cookie. The chart grants the workloadrevealon exactly that secret.
Access
publicAccess.enabled— Whentrue, the UI and API are served over HTTPS on the automatically assigned*.cpln.appendpoint, unauthenticated. Whenfalse(default), external requests are refused at the edge with403.internalAccess.type— Internal firewall scope of the workload:
internalAccess.workloads— Workload links (//gvc/GVC_NAME/workload/WORKLOAD_NAME), used only withworkload-list.
0.0.0.0/0 and is not configurable: querying upstream engines over the public internet is the entire function of the service, and narrowing it makes every engine fail.
Redis
The bundled datastore exists for the limiter and for shared state across replicas. It stores no search results and nothing durable.redis.enabled— Whenfalse, no datastore is deployed and SearXNG runs standalone.limiter.enabledmust then stayfalse; the chart refuses to render the combination, because without the datastore SearXNG would install no limiter at all while its configuration still claimed one.redis.redis.replicas— Must stay1, and the chart rejects anything else. SearXNG’s client is not Sentinel-aware and has to reach the master directly, so extra replicas would serve read-only connections and silently break the limiter. One sentinel is required by the Redis chart’s boot-time master discovery and cannot be scaled to zero.redis.redis.auth.password.enabled/redis.redis.auth.password.value— When enabled, the datastore requiresAUTHand the password is wired into the connection URL through a template-created secret. Change the placeholder value if you turn this on.redis.redis.image/redis.sentinel.image/redis.redis.resources/redis.sentinel.resources— Image and CPU and memory bounds for the two datastore workloads.
With the limiter on, a SearXNG container that starts while the datastore is unreachable refuses to start rather than coming up silently unthrottled: the deployment reports a container exit instead of serving. If a fresh install with
limiter.enabled: true never becomes ready, check the {release}-redis workload first. With the limiter off, the same container logs a warning and starts normally.Using the JSON API
From another workload in the same GVC, using the fully-qualified internal name:.cpln.local form is required.
The response carries the merged results, the engines that answered, and an unresponsive_engines list naming any that timed out or returned a CAPTCHA. Useful query parameters include categories (for example general, images, news), language, time_range, and pageno; the full set is in the upstream search API reference.
Keep limiter.enabled: false for this: the limiter rejects requests that do not look like a browser, which includes plain curl and most HTTP client libraries. 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 will still be throttled after 15 searches in the limiter’s burst window.
Connecting
A fresh install typically has all three workloads ready within about half a minute of the install command returning.
Important Notes
- There is no authentication of any kind. With
publicAccess.enabled: truethe UI, the API,/config, and/statsare all served to anyone who finds the endpoint. Turnlimiter.enabledon before you publish. - The limiter blocks the JSON API for non-browser clients. Plain
curland HTTP client libraries get429on/searchwhile the limiter is on. Pick one of the two shapes in Choosing an Access Shape deliberately. /configand/statsare unauthenticated and go public with the UI./configlists your engine and plugin configuration. SettingextraSettings: {general: {enable_metrics: false}}stops collecting the data behind/stats./metricsis not open, and cannot be opened without a password. Upstream gates it ongeneral.open_metrics, which is empty by default, so the route returns404as shipped. Setting it throughextraSettingsenables the route and requires that password over HTTP basic auth.- Create the signing-key secret before installing, and treat it as write-once. A missing secret wedges the deployment with zero log output; rotating it invalidates every saved preference cookie.
redis.redis.replicasmust stay1, andlimiter.enabledrequiresredis.enabled. Both are enforced at render time, so a bad combination fails the install rather than shipping a silently unlimited instance.- The datastore holds no durable data. If it restarts, limiter counters reset and nothing is lost, because search results were never stored there.
- Going public throttles your own results. Upstream engines answer heavy or bot-like traffic with CAPTCHAs and rate limits, which shows up as fewer results rather than an error. A limiter and a modest instance are what keep result quality up.
- The first
helm upgradeafter an install can re-apply resources that did not change, including the bundled datastore, which bounces those workloads once. Values were byte-identical across the re-apply in testing, and later upgrades of the same release report everything unchanged. Atreplicas: 1budget a brief blip on that first upgrade. - Firewall changes take effect after a delay — flipping
publicAccess.enabledorinternalAccess.typecan take from twenty seconds to a couple of minutes to be enforced after the upgrade reports success. - Install into a single-location GVC unless you mean otherwise. A workload runs in every location its GVC has, so extra locations multiply both the instance count and the outbound query volume upstream engines see from you.
- Uninstalling removes everything the template created — workloads, secrets, identity, and policy, with no volume sets left behind. Your own signing-key secret survives, so a reinstall against the same name keeps preference cookies valid.
External References
SearXNG Project
Project home, public instance list, and overview
Settings Reference
Every key that can be set through
extraSettingsServer Settings
The
server.* block, including the limiter and image proxyThe Limiter
How bot detection and rate limiting work upstream
Search API
Parameters and response format for
/searchSearXNG Template
View the source files, default values, and chart definition