Deploy to Kubernetes¶
Talmi is a single static binary in a distroless image. This guide runs it in Kubernetes with its config from a ConfigMap, secrets from a Secret, PostgreSQL for persistence, and TLS at the edge.
Kubernetes support is work in progress
There is no official Helm chart or supported manifest set yet. The examples below are a starting point, not a supported deployment. In particular, running more than one replica is not properly supported (see Multiple replicas). Treat this page as guidance for a single-replica setup while proper support is built out.
Before you begin¶
- A Kubernetes cluster and
kubectlaccess. - A container registry reachable from the cluster (the image is published to
ghcr.io/darmiel/talmi). - A PostgreSQL database if you want persistence. See Run with PostgreSQL.
- Your bootstrap config and sourced tree ready, and credentials as secret material.
Run the container¶
The image runs as a non-root user. Use the server run subcommand and point --config at the bootstrap file:
docker run --rm -p 8080:8080 \
-v /etc/talmi:/etc/talmi:ro \
ghcr.io/darmiel/talmi:latest server run --addr :8080 --config /etc/talmi/talmi.yaml
The command is server run
Run Talmi with server run --addr ... --config .... There is no serve command.
Deployment and Service¶
Mount the bootstrap file and the sourced tree from a ConfigMap, and credentials from a Secret, referenced in config with
file:/env:. Probe /healthz for liveness and readiness.
apiVersion: apps/v1
kind: Deployment
metadata:
name: talmi
spec:
replicas: 1
selector: { matchLabels: { app: talmi } }
template:
metadata: { labels: { app: talmi } }
spec:
containers:
- name: talmi
image: ghcr.io/darmiel/talmi:latest
args: ["server", "run", "--addr", ":8080", "--config", "/etc/talmi/talmi.yaml"]
ports: [ { containerPort: 8080 } ]
envFrom:
- secretRef: { name: talmi-secrets } # DSNs, tokens as env: refs
volumeMounts:
- { name: config, mountPath: /etc/talmi, readOnly: true }
- { name: keys, mountPath: /run/secrets, readOnly: true }
livenessProbe: { httpGet: { path: /healthz, port: 8080 } }
readinessProbe: { httpGet: { path: /healthz, port: 8080 } }
volumes:
- { name: config, configMap: { name: talmi-config } }
- { name: keys, secret: { secretName: talmi-keys } }
Put a TLS-terminating ingress or load balancer in front. Talmi speaks plaintext HTTP and expects TLS at the edge.
Behind an ingress¶
The ingress is the TCP peer Talmi sees, so client-IP-based rate limiting must be told which peers to trust. Set
rate_limit.trusted_proxies to your ingress/pod CIDRs so Talmi reads the real client from
X-Forwarded-For. See Configure rate limiting.
Persistence and migrations¶
Use the postgres store and auditor for anything beyond ephemeral runs. Talmi does not migrate the database; run
migrations as a Job before rolling out a version that needs a newer schema:
apiVersion: batch/v1
kind: Job
metadata:
name: talmi-migrate
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: migrate/migrate:latest
args: [ "-path", "/migrations", "-database", "$(TALMI_STORE_DSN)", "up" ]
envFrom: [ { secretRef: { name: talmi-secrets } } ]
volumeMounts: [ { name: migrations, mountPath: /migrations, readOnly: true } ]
volumes:
- { name: migrations, configMap: { name: talmi-migrations } }
See Run with PostgreSQL for the full store and audit setup. With Helm, run this Job as a pre-install /
pre-upgrade hook so the schema is applied before the new pods start.
Multiple replicas¶
Run a single replica for now
More than one replica is not properly supported yet. Scheduled tasks run on every replica (so they
fire N times), and a config webhook only reaches the one pod it hits (the others reload on their
sync.interval). Coordinated multi-node operation is on the roadmap.
Until multi-node lands, prefer a single replica. If you must scale out, at least use the redis
rate-limit backend so all replicas share one view (see Configure rate limiting), and accept the
duplicated background work and webhook lag above.
Verify¶
kubectl rollout status deploy/talmi
kubectl port-forward deploy/talmi 8080:8080 &
curl -sS localhost:8080/healthz
Troubleshooting¶
- CrashLoopBackOff at startup: usually a config error or an unreachable database. Check
kubectl logs; the server fails closed rather than starting misconfigured. - all clients share one rate-limit bucket:
trusted_proxiesis unset, so every request looks like it came from the ingress IP. Set it to the ingress CIDR. - sessions break after a rollout: the signing key is not stable across pods. Mount one
signing.keyfrom a Secret, not the--devephemeral key.
Next steps¶
- Run with PostgreSQL, Configure rate limiting.
- Security overview for the public-exposure checklist.