kimo
GuideAdvanced10 min

Deploy Kimo Bridge on Kubernetes

To run Kimo Bridge in production on Kubernetes, install the getkimo/kimo-bridge Helm chart into a dedicated namespace with at least two replicas, a PodDisruptionBudget, the restricted Pod Security profile, credentials from a properly protected Secret, and a default-deny egress NetworkPolicy that allows only DNS, your database and Kimo's regional endpoint on port 443. This guide walks through each piece, explains why it is there, and ends with verification and troubleshooting.

Arno Visser · Solutions architect
10 min read

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.

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.

ObjectPurposeDefault
DeploymentRuns the bridge pods2 replicas, restricted security context, read-only root filesystem
ServiceAccountPod identityAPI token not mounted; the bridge never talks to the Kubernetes API
ConfigMapkimo-bridge.yaml: sources and policyRendered from config in values
Secret (optional)Group token and database DSNsOff when existingSecret is set
PodDisruptionBudgetKeeps at least one pod during drainsminAvailable: 1
NetworkPolicyDefault-deny egress plus three allow rulesEnabled; you supply CIDRs
ServiceExposes :9090 metrics inside the clusterClusterIP, metrics only
ServiceMonitor (optional)Prometheus Operator scrapingOff
Objects created by the getkimo/kimo-bridge Helm chart.
RequirementDetails
KubernetesA supported release; Linux nodes (amd64 or arm64)
HelmHelm 3.8+ or Helm 4
CNIA network plugin that enforces NetworkPolicy (for example Calico or Cilium)
EgressFrom the bridge namespace to Kimo's regional bridge endpoint on TCP 443, and to your database
DatabaseA read-only role, as in Step 1 of the Docker guide
KimoWorkspace admin, to create a group enrollment token in the Bridge console
Prerequisites for a Kubernetes deployment.

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.4 The chart's defaults satisfy all four.

Namespace with Pod Security admission labels
bash
kubectl create namespace kimo-bridge
kubectl label namespace kimo-bridge \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted

Step 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.2 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.

Create the Secret from files (keeps values out of shell history)
bash
kubectl -n kimo-bridge create secret generic kimo-bridge-credentials \
  --from-file=token=./secrets/group-token \
  --from-file=crm_dsn=./secrets/crm_dsn

Step 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.

values.yaml
yaml
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: false

Resource 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

Add the repository and install
bash
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.6 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.1

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.1 And the whole mechanism depends on your network plugin: creating a NetworkPolicy without a controller that implements it has no effect.1 If your CNI offers DNS-aware egress policies as an extension, you may prefer a hostname rule for bridge.eu.getkimo.com.

What the chart renders (simplified)
yaml
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; maxSkew bounds the imbalance and whenUnsatisfiable decides whether to keep a pod pending or schedule it anyway.5
  • 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.3
  • Drain gracefully. On SIGTERM the bridge stops accepting new queries, finishes in-flight ones for up to 25 seconds, then closes its tunnel. The chart sets terminationGracePeriodSeconds: 30 to match.
Queries served per replica during a rolling node drain
  • Replica A
  • Replica B
  • Replica C
Figure. Illustrative data: simulated three-replica bridge group during a node drain. Load shifts to remaining members within seconds; the group never drops below two connected replicas.

Step 6: Verify the deployment

Check pods, run diagnostics, test policy
bash
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 3s

Verification 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).
  • doctor reports TLS 1.3 with mutual authentication and a read-only database role.
  • The email query is denied, and the denial appears in Activity.
  • kimo-bridge netcheck reports arbitrary internet hosts as blocked.
  • kubectl drain on 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.

Two segments, two releases
bash
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 --wait

Remember 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.

MetricAlert whenWhy it matters
kimo_bridge_tunnel_connectedFewer than 2 replicas connected for 5 minutesLoss of redundancy before loss of service
kimo_bridge_queries_denied_totalSudden rate increaseMisconfigured dashboard, or someone probing policy
kimo_bridge_query_duration_seconds (p95)Above your statement timeout × 0.8Queries about to be killed; source under load
kimo_bridge_cert_expiry_secondsBelow 2 hoursRenewal failing, usually egress or clock problems
kimo_bridge_rows_returned_totalDashboard onlyShows how much data actually leaves, by source
Recommended Kimo Bridge metrics and alerts.

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

Upgrade, inspect, roll back
bash
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-bridge

Policy-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.3 Release notes are on the changelog.

Troubleshooting

SymptomLikely causeFix
Pods rejected: violates PodSecurity "restricted"Values override removed a security settingRestore securityContext defaults; do not set runAsUser: 0
tunnel: i/o timeout right after installEgress policy blocks Kimo or DNSCheck kimoEndpointCIDRs against the Bridge console and the DNS pod labels
Everything works, but egress is not restrictedCNI does not enforce NetworkPolicyUse a plugin that implements NetworkPolicy; the objects alone do nothing
New pods stay Pending approval in the consoleRequire approval is onApprove the members, or disable approval for this group
enrollment failed: group token revokedToken rotated in the consoleUpdate the Secret, then kubectl rollout restart deploy/kimo-bridge
Drain hangs on the last bridge podPDB minAvailable equals replica countRun at least minAvailable + 1 replicas
Database too many connectionsReplicas × pool size exceeds the role limitLower pool.max per source or raise the role's connection limit
Common Kubernetes deployment issues and fixes.

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
  1. 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.

  2. Secrets (opens in a new tab)
    Kubernetes Documentationkubernetes.io

    Secrets unencrypted in etcd by default; four recommended safety steps.

  3. Disruptions (opens in a new tab)
    Kubernetes Documentationkubernetes.io

    PodDisruptionBudget limits voluntary disruptions; does not cover involuntary ones or rolling updates.

  4. 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.

  5. Pod Topology Spread Constraints (opens in a new tab)
    Kubernetes Documentationkubernetes.io

    maxSkew, topologyKey and whenUnsatisfiable.

  6. 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.

0%

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.

Put it to work

Skip the setup — start from a working version.

Open the app and follow along with the steps in this guide.

All resources
GuideBeginner
All

Install Kimo Bridge with Docker

Run the bridge next to your database in minutes: outbound-only, read-only, revocable.

Arno Visser
10 min read
GuideIntermediate
All

The Kimo Bridge security model

What leaves your network, what never does, and how every query is authorized and audited.

Rhea Patel
10 min read
Whitepaper
All

Your Data, Your Rules

The hybrid analytics architecture behind Kimo Bridge: live query pushdown, optional cloud sync, and zero-trust by default.

Arno Visser
22 pages

Your data officer is ready.

Connect a source — or install Kimo Bridge and keep data on your servers — then ask a question and get an answer you can audit.