Skip to main content
This page provides instructions on how to configure the Helm chart to install the Sidecar in any cloud provider. The chart deploys one Sidecar: a Deployment, the ConfigMap holding its configuration, a Secret holding its environment, a Service for the admin port, and a Service per lane you choose to publish. It depends on no other chart and needs no gateway, no agent and no control-plane database. Every resource is named after the Helm release, so installing the chart several times in one namespace — one release per upstream you front — is the normal shape. See Running Several Sidecars.

Quick Start

A Sidecar in front of a Postgres, in four steps. At the end a normal psql reaches the database through the relay and the email column comes back redacted, with no change to the client, the query or the database.
1

Deploy a Postgres to front

Skip this if you already have a database to point at. This one is a throwaway with a password in plain sight, seeded with two rows.
2

Write a values file

config is the Sidecar’s own configuration document: one listener naming the upstream, and one rule masking email addresses in whatever comes back. laneServices publishes the listener to the cluster — the chart publishes only the admin port on its own.
Clients keep dialling 5432; only the Service name changes. Nothing else is required — no license, no gateway, no agent, no control plane.
3

Install the chart

One lane ready line per listener, naming the upstream it resolved:
A pod reporting 1/1 Running has already answered /healthz on the admin port, so readiness means the relay is serving.
4

Query through it

Point the same command at -h postgres and the addresses come back in the clear: the rows are unchanged in the database, and the relay rewrites them on the way out.Every statement and every mask is on the audit trail, which audit.file: '-' sent to the container log:
A Sidecar can take its whole configuration from the Control Plane handshake instead of a file, in which case no config attribute is set at all. See Control Plane.
Delete everything this created with kubectl delete namespace hoop.

Helm Install

To install the latest version in a new namespace (example: hoop). Issue the command below:
The release name prefixes every resource the chart owns: A release named hoopsidecar collapses to the bare name rather than repeating it.

Overriding values

It is possible to add new attributes or overwrite an attribute from a base values.yaml file. In the example below a specific image tag is pinned and the admin Service is turned off.

Sidecar Configuration

The config attribute is the Sidecar’s own configuration document: protocols, guardrails, masking, audit, PII entities and AI analysis are all decided here, not by chart attributes. The chart renders it into a ConfigMap, mounts it at /etc/hoop-inspect/config.yaml and passes that path in HOOP_SIDECAR_CONFIG.
The Deployment carries a checksum of the rendered ConfigMap, so a change to config rolls the pods on helm upgrade. A policy change is an upgrade, never a rebuild. That checksum only covers what this chart renders — see Using an existing ConfigMap for the case it cannot see.
Without a license the Sidecar caps guardrail and masking at one rule each and says so on its first line of output. See License.

Using an existing ConfigMap

To manage the document outside the chart — a GitOps repository, a sealed secret, an operator — point existingConfigMap at it. The key must be config.yaml.
The name is used verbatim, never rewritten with the release prefix. config and existingConfigMap are mutually exclusive; setting both is refused.
Set configRevision too, or your edits never reach the pods.The ConfigMap lives outside the release, so the chart cannot see its contents and its checksum never changes — helm upgrade produces a byte-identical pod template and rolls nothing. Nothing else covers the gap: the Sidecar reads its configuration file once at startup and watches nothing, because the hot-reload path is Control Plane only. The kubelet updates the mounted file and the process never looks at it again.Put anything that changes with the content in configRevision, and change it in the same commit that changes the ConfigMap:
Or generate it at install time:
With config you do not need it — the rendered checksum already does the job — but it still applies, so it doubles as a way to force a rollout on demand. A GitOps tool that annotates the pods for you (Reloader, Argo CD) makes it unnecessary. Without any of these, kubectl rollout restart deploy/<release>-hoopsidecar is the manual equivalent.

Validating the configuration

Nothing needs to be running. The validator builds every lane and reports every problem in one run:
This takes the configuration document itself — everything nested under config:, with that key stripped — not the values file.

Running as Sidecar

Everything above deploys the relay as a workload of its own, reached over the network. It can instead run as a container inside the pod it protects, reached over loopback. No chart is involved: you add a container to a Deployment you already own, using the raw image. hoophq/hoopsidecar runs hoop start sidecar as its command and already points HOOP_SIDECAR_CONFIG at /etc/hoop-inspect/config.yaml, so mounting the ConfigMap there is the whole wiring. No command, no env.
The application changes one thing: the host and port it dials. Everything else — driver, credentials, queries — is untouched. Containers in a pod share a network namespace, so the relay and the application cannot both bind the same port; the second one to start exits with address already in use.

Why initContainers

A container listed under initContainers with restartPolicy: Always is a native sidecar: the kubelet starts it before any application container and stops it after the last one exits. Put the relay under containers: instead and both ends of the pod’s life are a race — the application can open a connection before the lane is listening, and can lose one while it is still finishing work at shutdown.
Native sidecars are on by default from Kubernetes 1.29 and stable in 1.33. On anything older, list the relay under containers: and give the application a retry on connect.

Probes

The kubelet does not join the pod’s network namespace: an httpGet or tcpSocket probe is dialled from the node against the pod IP. A probe against an admin server bound to 127.0.0.1 fails:
So pick a pair. The lane itself stays on 127.0.0.1 either way — only the admin server moves. An exec probe runs inside the container, so it does reach loopback. The default image has a shell and no HTTP client, which is enough; the -distroless flavour has no shell and cannot run one at all.
The admin server has no authentication. On 0.0.0.0 it is reachable by anything in the cluster that can route to the pod, and it serves /config, /stats, /events and — when audit.query_sessions is set — /api/sessions, a read interface to every statement every user ran. Keep it away from the cluster with a NetworkPolicy, or bind it to loopback and probe with exec.

Sharing a Socket with Envoy

The pattern above puts the Sidecar directly in front of the application. When Envoy sits in front instead — see Command Line — and the lane uses a unix socket rather than a published port, Envoy and the Sidecar need a shared, writable directory. No chart is involved here either; this is the same raw image run as a second container in a pod you already own.
fsGroup on the pod’s securityContext replaces the Docker Compose init-container chown step: the kubelet applies it to the emptyDir before any container starts. Set it to Envoy’s gid and both sides can use the directory. Mount the config as a ConfigMap and set HOOP_SIDECAR_CONFIG instead of passing a flag. The Compose equivalent of this setup, including the exact permission traps to watch for, is in Setting up the socket directory.

Chart Configuration

Everything below is a chart attribute rather than part of the Sidecar’s own configuration document: how the pod is built, what it is published as, and where it is scheduled.

Image Configuration

By default the chart pulls hoophq/hoopsidecar:latest, which is the Ubuntu flavour. To pin a version or switch flavours, use the image attribute section.
The chart sets no command: the image’s own entrypoint runs the relay and reads the configuration from HOOP_SIDECAR_CONFIG. Container Images covers what each flavour contains.

Admin Port and Probes

The chart declares exactly one port: 19000, the admin server. The same number is written into the Deployment, the Service and both probes, and it is not configurable. config.admin.listen therefore has to be 0.0.0.0:19000. A configuration that omits it or moves it is refused while the chart renders, because the Sidecar disables the admin server when the address is empty and the probes would otherwise poll a closed port and fail every pod:
Probe timing is adjustable; the port is not.
Under controlPlane.url or existingConfigMap the chart cannot see the configuration, so that check does not run and putting the admin server on 19000 is yours to get right. Set probe.port: 19000 explicitly in those modes — the chart has no document to read it from.
Everything the admin server serves is reachable on that port:

Service

The chart’s Service publishes the admin port and nothing else.

Exposing lanes

Lane ports are not declared on the pod — a containerPort is informational in Kubernetes, and a lane bound to a unix socket has no port at all. Publishing them is laneServices, a map of Services, one Kubernetes Service per entry:
A map rather than one multi-port Service, because Service type and every cloud load-balancer knob — internal versus internet-facing, LB class, source ranges, traffic policy — are per-Service and not per-port. One lane on an internal LB and another public is two objects, and no single Service can express it. A map also merges across values files and responds to --set laneServices.public.type=LoadBalancer, which a list indexed by position does not. Each entry is named <release>-hoopsidecar-<key> unless you set name, and carries the chart’s selector labels so it reaches that release’s pods and no other’s. name is required on every port once an entry has more than one, and an entry that is enabled with no ports is refused — a Service with no port reaches nothing. Ports here are literal. The chart does not read them from config.listeners, so keep the two in step: a Service pointing at a port nothing binds is accepted by Kubernetes and reaches nothing.
Before setting type: LoadBalancer.The Sidecar terminates no client TLS unless the lane sets downstream_tls, and that is accepted on postgres, grpc and spanner only. On http, mysql, mssql and mongodb it is refused at startup, so those lanes are always plaintext — a public load balancer in front of one of them puts credentials and query results in the clear on the internet.Expose a lane publicly only when the lane terminates TLS itself, or when something in front of it does: Envoy, an ingress controller, a service mesh. loadBalancerSourceRanges narrows who can reach it; it does not encrypt anything.
The admin Service is separate and unaffected: always ClusterIP, always 19000. A hand-written Service still works for anything laneServices does not cover — the selector is app.kubernetes.io/name: hoopsidecar plus app.kubernetes.io/instance: <release>. These are the conventional port numbers, and what the examples in these docs bind:

Control Plane

To take the running configuration from a Control Plane, set the URL and the token from the Sidecar’s registration and leave config unset.
The two sources never merge. A plane-connected Sidecar serves what the plane sent, and listeners left in a local file are ignored out loud once the plane holds a configuration of its own. A plane holding none is seeded with your file’s document on the first handshake, which is how an existing standalone deployment connects: add the URL and the token, change nothing else.
Passing the token with --set writes it into the Helm release and your shell history. Put it in a Secret referenced by the pod instead, and keep it out of values.yaml committed to a repository.
See Connect a Sidecar for issuing the token and what the handshake carries.

License

Without a license, guardrails and masking are capped at one rule each. The value is a path or the document itself — a value starting with { is read as the license — so a mounted Secret and a Helm value are the same field.
What the process concluded is the first line of its startup output:

Extra Environment Variables

The chart sets the four variables the Sidecar reads: HOOP_SIDECAR_CONFIG, HOOP_LICENSE, HOOP_CONTROL_PLANE_URL and HOOP_SIDECAR_TOKEN. Anything else a configuration references — an analyzer provider’s API key, for instance — goes in extraSecret, which becomes a second Secret attached with envFrom.

Extra Volumes

For what the configuration document references by path: an upstream CA, a downstream certificate, an analyzer credential, a license file.
The hop to the database is encrypted and still inspected: the Sidecar is the TLS client there, so it decrypts what it reads.
Mount any Secret holding a credential with defaultMode: 0400. Kubernetes writes secret files 0644 by default and the Sidecar refuses to read a credential at that mode, naming it:
A CA certificate is public and needs no such mode. An analyzer credential does.

Service Account

With create: true the chart makes one named after the release. With create: false, name points at an account that already exists — the shape GKE Workload Identity wants, so an AI risk analyzer reaches Vertex with no credential on disk.
Leaving both unset means the pod uses the namespace default.

Replicas and Deployment Strategy

Raising replicas is safe. Each replica is an independent proxy holding no shared state, and the Service spreads connections across them. Sessions are per-connection, so a replica going away drops the connections it was carrying and not the others.
The strategy defaults to RollingUpdate rather than Recreate. This process sits in the data path between a client and its database, and Recreate would close every connection on every configuration change with nothing listening in between.

Computing Resources

The chart sets no requests or limits by default. The Sidecar’s cost scales with concurrent connections and with what each lane does — detection and masking read the response body, an AI analyzer adds a network call per statement shape.

Node Selector

This configuration describes a pod that has a node selector, disktype: ssd. This means that the pod will get scheduled on a node that has a disktype=ssd label. See this documentation for more information.

Tolerations

See this article explaining how to configure tolerations

Node Affinity

See this article explaining how to configure affinity and anti-affinity rules

Annotations

Running Several Sidecars

Two upstreams, two releases, one namespace:
Every resource carries the release name, and the Deployment and Service selectors carry app.kubernetes.io/instance, so neither release adopts the other’s pods. The alternative is one release with several entries under config.listeners. Pick by blast radius: separate releases give each upstream its own rollout, its own resource limits and its own audit stream; one release is fewer things to operate and one place to read.

Naming

nameOverride changes the base the resource names are built from; fullnameOverride replaces the whole name and drops the release prefix.
Both feed the Deployment’s selector, which is immutable in Kubernetes. Changing either on a live release makes helm upgrade fail. Settle them at install time.

What the Chart Refuses

There is no startable default. Rather than ship a Deployment that crash-loops, the chart fails while rendering:

Generating Manifests

If you prefer using manifests over Helm, we recommend this approach. It allows you to track any modifications to the chart whenever a new version appears. You can apply a diff to your versioned files to identify what has been altered.