At a glance
- Level
- Advanced
- Time
- 10 min
Prerequisites
- A Kubernetes cluster with a CNI that enforces NetworkPolicy
- Helm 3.8+ (or Helm 4) and kubectl access to create a namespace
- A read-only database role reachable from the cluster
- A group enrollment token from the Kimo Bridge console
You will end up with
A production Kimo Bridge group on Kubernetes: three replicas spread across zones, restricted Pod Security, protected credentials, default-deny egress, metrics and audit shipped to your monitoring, and a tested upgrade path.
Connectors used
What does the Helm chart deploy?
The chart packages the same ghcr.io/getkimo/bridge image used in the Docker install, wrapped in the objects a platform team expects. Nothing in it accepts traffic from outside the cluster: the bridge dials out to Kimo over an outbound-only tunnel, and the only port it opens is an in-cluster health and metrics port.
| Object | Purpose | Default |
|---|---|---|
| Deployment | Runs the bridge pods | 2 replicas, restricted security context, read-only root filesystem |
| ServiceAccount | Pod identity | API token not mounted; the bridge never talks to the Kubernetes API |
| ConfigMap | kimo-bridge.yaml: sources and policy | Rendered from config in values |
| Secret (optional) | Group token and database DSNs | Off when existingSecret is set |
| PodDisruptionBudget | Keeps at least one pod during drains | minAvailable: 1 |
| NetworkPolicy | Default-deny egress plus three allow rules | Enabled; you supply CIDRs |
| Service | Exposes :9090 metrics inside the cluster | ClusterIP, metrics only |
| ServiceMonitor (optional) | Prometheus Operator scraping | Off |
| Requirement | Details |
|---|---|
| Kubernetes | A supported release; Linux nodes (amd64 or arm64) |
| Helm | Helm 3.8+ or Helm 4 |
| CNI | A network plugin that enforces NetworkPolicy (for example Calico or Cilium) |
| Egress | From the bridge namespace to Kimo's regional bridge endpoint on TCP 443, and to your database |
| Database | A read-only role, as in Step 1 of the Docker guide |
| Kimo | Workspace admin, to create a group enrollment token in the Bridge console |
Step 1: Create a namespace with the restricted profile
Give the bridge its own namespace. That keeps its Secrets away from other workloads and lets you apply the strictest Pod Security Standard. The restricted profile requires containers to run as non-root, forbids privilege escalation, requires dropping ALL capabilities and requires an explicit seccomp profile of RuntimeDefault or Localhost.4Source 4 · Kubernetes DocumentationPod Security Standardskubernetes.io The chart's defaults satisfy all four.
kubectl create namespace kimo-bridge
kubectl label namespace kimo-bridge \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restrictedStep 2: Store the token and credentials safely
Production deployments use a group enrollment token (prefix kbg_) rather than the one-time token used on a single host. Each pod uses it at startup to enroll its own identity: it generates a private key in memory, obtains a short-lived client certificate, and joins the group. Pods that restart enroll again, and old certificates simply expire. Create the group token in the Bridge console and turn on Require approval for new members if you want a human to confirm each new pod identity.
Those are the four steps Kubernetes itself recommends for using Secrets safely.2Source 2 · Kubernetes DocumentationSecretskubernetes.io If you already run the Secrets Store CSI Driver or an external secrets operator, point the chart at the resulting Secret with existingSecret and skip creating one by hand.
kubectl -n kimo-bridge create secret generic kimo-bridge-credentials \
--from-file=token=./secrets/group-token \
--from-file=crm_dsn=./secrets/crm_dsnStep 3: Write values.yaml
The config block is the same kimo-bridge.yaml you would write on a VM. Keep it in version control and review policy changes like code — the policy decides what Kimo can ever ask your database.
image:
repository: ghcr.io/getkimo/bridge
tag: 1.4.2
replicaCount: 3
existingSecret: kimo-bridge-credentials
bridge:
group: prod-eu
region: eu
config:
sources:
- id: crm_pg
type: postgres
dsn_file: /run/secrets/kimo/crm_dsn
mode: bridge
cache_ttl: 0s
policy:
default: deny
tables:
analytics.accounts:
columns: [id, region, plan, mrr_cents, created_at, churned_at]
row_filter: "region = ANY({{user.attributes.regions}})"
analytics.invoices:
columns: [id, account_id, amount_cents, currency, paid_at]
min_group_size: 5
limits: { max_rows: 50000, timeout: 30s, rate: 20/s }
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { memory: 512Mi }
podDisruptionBudget:
minAvailable: 1
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
networkPolicy:
enabled: true
kimoEndpointCIDRs: [203.0.113.0/26] # copy from the Bridge console
databases:
- { cidr: 10.20.0.15/32, port: 5432 }
metrics:
serviceMonitor:
enabled: falseResource numbers are a starting point for a few hundred queries a minute. The bridge streams results instead of buffering them, so memory stays flat as long as max_rows is sensible.
Step 4: Install with Helm
helm repo add getkimo https://charts.getkimo.com
helm repo update
helm install kimo-bridge getkimo/kimo-bridge \
--namespace kimo-bridge \
--version 1.4.2 \
-f values.yaml \
--wait--namespace, --version, -f/--values and --wait are standard helm install flags; pin --version so upgrades are deliberate.6Source 6 · Helm Documentationhelm installhelm.sh The chart refuses to render if existingSecret is missing or if the policy has no default: deny, so a misconfigured release fails at install time rather than at query time.
Step 5: Enforce outbound-only with NetworkPolicy
The bridge never needs inbound connections from outside the cluster, and it needs exactly three outbound destinations. Kubernetes can enforce that. Pods are non-isolated for egress until a NetworkPolicy selects them with Egress in policyTypes; after that, only the listed egress is allowed. Policies are additive, and a default-deny egress policy blocks DNS too, so DNS must be allowed explicitly.1Source 1 · Kubernetes DocumentationNetwork Policieskubernetes.io
Two limits shape the rules. Standard NetworkPolicy cannot target destinations by name, so Kimo's endpoint is expressed as IP ranges with ipBlock, which is intended for cluster-external IPs.1Source 1 · Kubernetes DocumentationNetwork Policieskubernetes.io And the whole mechanism depends on your network plugin: creating a NetworkPolicy without a controller that implements it has no effect.1Source 1 · Kubernetes DocumentationNetwork Policieskubernetes.io If your CNI offers DNS-aware egress policies as an extension, you may prefer a hostname rule for bridge.eu.getkimo.com.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: kimo-bridge-egress
namespace: kimo-bridge
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: kimo-bridge
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- { protocol: TCP, port: 9090 }
egress:
- to: # cluster DNS
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- { protocol: UDP, port: 53 }
- { protocol: TCP, port: 53 }
- to: # your database
- ipBlock: { cidr: 10.20.0.15/32 }
ports:
- { protocol: TCP, port: 5432 }
- to: # Kimo regional bridge endpoint
- ipBlock: { cidr: 203.0.113.0/26 }
ports:
- { protocol: TCP, port: 443 }How do you make the bridge highly available?
Run at least two replicas in the same bridge group. Every replica holds its own tunnel, and Kimo balances queries across connected members; if one disappears, in-flight queries on it are retried once on another member. Three settings make this hold up during real operations:
- Spread across zones. Topology spread constraints control how pods are distributed across failure domains such as zones and nodes;
maxSkewbounds the imbalance andwhenUnsatisfiabledecides whether to keep a pod pending or schedule it anyway.5Source 5 · Kubernetes DocumentationPod Topology Spread Constraintskubernetes.io - Budget for disruptions. A PodDisruptionBudget limits how many replicas are down at once from voluntary disruptions such as
kubectl drain. It does not protect against involuntary failures like a lost node, and rolling updates are governed by the Deployment's own strategy, not the PDB.3Source 3 · Kubernetes DocumentationDisruptionskubernetes.io - Drain gracefully. On
SIGTERMthe bridge stops accepting new queries, finishes in-flight ones for up to 25 seconds, then closes its tunnel. The chart setsterminationGracePeriodSeconds: 30to match.
- Replica A
- Replica B
- Replica C
Step 6: Verify the deployment
kubectl -n kimo-bridge get pods -o wide
kubectl -n kimo-bridge exec deploy/kimo-bridge -- kimo-bridge doctor
kubectl -n kimo-bridge exec deploy/kimo-bridge -- kimo-bridge status --group
# A query on a non-allowed column must be denied and logged
kubectl -n kimo-bridge exec deploy/kimo-bridge -- \
kimo-bridge test-source crm_pg --sql "select email from analytics.accounts"
# Egress to anything else must fail (prints "blocked")
kubectl -n kimo-bridge exec deploy/kimo-bridge -- \
kimo-bridge netcheck https://example.com --timeout 3sVerification checklist
- All replicas are Running and spread across at least two zones.
- The Bridge console shows the group with every replica Connected (and approved, if approval is on).
doctorreports TLS 1.3 with mutual authentication and a read-only database role.- The
emailquery is denied, and the denial appears in Activity. kimo-bridge netcheckreports arbitrary internet hosts as blocked.kubectl drainon one node keeps at least one bridge pod serving queries.
Several databases, segments and regions
One release can serve several databases: add entries under config.sources and one egress rule per database in networkPolicy.databases. Give each source its own role and its own policy — a shared role would let a mistake in one policy expose another database.
Split into separate releases when the network says so. If two databases live in segments that must not be connected, install one release per segment, each in its own namespace with its own egress policy and bridge group; Kimo routes each source to the group that serves it. For data that must stay in a given country, install a release per region with bridge.region set accordingly, so each group connects only to its matching regional endpoint. The decision framework in Your Data, Your Rules helps decide which sources belong in Bridge mode at all, and which can sync to Cloud mode through the same release.
helm install bridge-finance getkimo/kimo-bridge -n kimo-bridge-finance \
--create-namespace --version 1.4.2 -f values-finance.yaml --wait
helm install bridge-product getkimo/kimo-bridge -n kimo-bridge-product \
--create-namespace --version 1.4.2 -f values-product.yaml --waitRemember to label each new namespace for the restricted Pod Security profile (Step 1); --create-namespace creates it without labels.
Observability: what to monitor
The bridge exposes Prometheus metrics on port 9090. Four are worth an alert; the rest are for dashboards. You can chart them in Kimo itself or in your existing monitoring stack.
| Metric | Alert when | Why it matters |
|---|---|---|
kimo_bridge_tunnel_connected | Fewer than 2 replicas connected for 5 minutes | Loss of redundancy before loss of service |
kimo_bridge_queries_denied_total | Sudden rate increase | Misconfigured dashboard, or someone probing policy |
kimo_bridge_query_duration_seconds (p95) | Above your statement timeout × 0.8 | Queries about to be killed; source under load |
kimo_bridge_cert_expiry_seconds | Below 2 hours | Renewal failing, usually egress or clock problems |
kimo_bridge_rows_returned_total | Dashboard only | Shows how much data actually leaves, by source |
Ship the audit stream (JSON lines on stdout with audit.stdout: true) to your SIEM alongside the metrics. The security model guide lists every audit field.
Upgrades and rollbacks
helm repo update
helm diff upgrade kimo-bridge getkimo/kimo-bridge -n kimo-bridge \
--version 1.5.0 -f values.yaml # requires the helm-diff plugin
helm upgrade kimo-bridge getkimo/kimo-bridge -n kimo-bridge \
--version 1.5.0 -f values.yaml --wait
helm history kimo-bridge -n kimo-bridge
helm rollback kimo-bridge 3 -n kimo-bridgePolicy-only changes do not need a new image: edit config in values and run helm upgrade. The chart checksums the ConfigMap so pods roll one at a time, and maxUnavailable: 0 in the Deployment rollout strategy keeps the group serving throughout — PodDisruptionBudgets do not limit rolling updates, so the strategy is what matters here.3Source 3 · Kubernetes DocumentationDisruptionskubernetes.io Release notes are on the changelog.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Pods rejected: violates PodSecurity "restricted" | Values override removed a security setting | Restore securityContext defaults; do not set runAsUser: 0 |
tunnel: i/o timeout right after install | Egress policy blocks Kimo or DNS | Check kimoEndpointCIDRs against the Bridge console and the DNS pod labels |
| Everything works, but egress is not restricted | CNI does not enforce NetworkPolicy | Use a plugin that implements NetworkPolicy; the objects alone do nothing |
New pods stay Pending approval in the console | Require approval is on | Approve the members, or disable approval for this group |
enrollment failed: group token revoked | Token rotated in the console | Update the Secret, then kubectl rollout restart deploy/kimo-bridge |
| Drain hangs on the last bridge pod | PDB minAvailable equals replica count | Run at least minAvailable + 1 replicas |
Database too many connections | Replicas × pool size exceeds the role limit | Lower pool.max per source or raise the role's connection limit |
For the reasoning behind this deployment shape — and when to choose Cloud mode instead — see the whitepaper Your Data, Your Rules. Teams that need Kimo itself inside the cluster, with no outbound connection at all, should read the on-premise documentation.
Sources
6 references- Network Policies (opens in a new tab)Kubernetes Documentationkubernetes.io
Egress isolation, additive policies, DNS caveat, ipBlock for cluster-external IPs, no targeting by service name, plugin requirement.
- Secrets (opens in a new tab)Kubernetes Documentationkubernetes.io
Secrets unencrypted in etcd by default; four recommended safety steps.
- Disruptions (opens in a new tab)Kubernetes Documentationkubernetes.io
PodDisruptionBudget limits voluntary disruptions; does not cover involuntary ones or rolling updates.
- Pod Security Standards (opens in a new tab)Kubernetes Documentationkubernetes.io
Restricted profile: non-root, no privilege escalation, drop ALL capabilities, seccomp RuntimeDefault or Localhost.
- Pod Topology Spread Constraints (opens in a new tab)Kubernetes Documentationkubernetes.io
maxSkew, topologyKey and whenUnsatisfiable.
- helm install (opens in a new tab)Helm Documentationhelm.sh
Flags --namespace, --create-namespace, --values, --version and --wait.
External sources were accessed at the time of writing. Kimo product details, customers and figures in examples are illustrative unless a source is cited.
Mark as done
0 of 12 sections done
Frequently asked questions
Does the bridge need a Service or Ingress?
No Ingress, ever. The chart creates a ClusterIP Service only for Prometheus metrics on port 9090; queries arrive over the outbound tunnel the bridge opens itself.
How many replicas should I run?
Two is the minimum for production and three is a comfortable default across three zones. Add replicas for throughput only after checking that your database connection limit covers replicas multiplied by pool size.
Why does NetworkPolicy use IP ranges for Kimo instead of a hostname?
Standard Kubernetes NetworkPolicy selects external destinations by CIDR with ipBlock and cannot target names. Copy the published ranges from the Bridge console, or use your CNI's DNS-aware policy if it has one.
Can I run the bridge in the same namespace as my application?
You can, but a dedicated namespace is safer: anyone who can create pods in a namespace can read its Secrets, and a separate namespace lets you apply the restricted profile and a tight egress policy without affecting the application.
What happens to queries during an upgrade?
Pods roll one at a time. A terminating pod finishes in-flight queries for up to 25 seconds while Kimo routes new queries to other connected members, so dashboards keep loading.
Skip the setup — start from a working version.
Open the app and follow along with the steps in this guide.




