Skip to main content

The chart in charts/rolter deploys the data-plane gateway and the control plane (which also serves the dashboard) as two Deployments. PostgreSQL, Redis and ClickHouse are deliberately not part of it: use managed services and your normal backup policy, and point the chart at them.


Install#

Keep the database URL, which carries a password, in a Kubernetes Secret and hand it to both workloads through secretEnv (see Secrets):

env.clickhouseUrl turns on request logging and the dashboard’s analytics. Without Redis each control-plane replica keeps its own sign-in throttle counters, so a multi-replica install wants it set. The control plane refuses to start without an admin token when it listens on 0.0.0.0, which it does in a pod, so supply ROLTER_ADMIN_TOKEN as described below.


Secrets#

Provider credentials and tokens belong in Kubernetes Secrets, passed through secretEnv, never in config.file or --set values (which land in release history):

secretEnv entries reach both workloads. gateway.extraEnv and control.extraEnv add variables to one of them. Other settings, such as mail for invitations (ROLTER_SMTP_URL), go the same way.


Your own rolter.toml#

config.file is the bootstrap file the chart writes into a ConfigMap. To manage the file outside the release, create the ConfigMap yourself and name it:

Once a database is configured, providers, routes and the rest are managed from the dashboard and propagate to the gateways without a restart; see Hot reload.


Preflight#

By default (preflight.enabled) each pod runs rolter check --strict in an init container, against the same environment block the workload will get, so a misconfigured release fails the rollout instead of starting degraded and the pod never becomes ready. preflight.connect also probes that the datastores accept a TCP connection; it is off because a DNS blip during a rollout would then block a release that is fine. See Production hardening.


Probes#

The control plane wires a startup, readiness and liveness probe; the gateway wires readiness and liveness.

ProbeEndpointMeans
liveness/healthzThe process is up and not wedged. It checks no dependency, so a database outage can never restart the fleet.
readiness/readyzThe instance may take traffic. On the control plane: the Postgres pool hands out a connection, every migration is applied, and ROLTER_KEK parses when set. Redis and ClickHouse only degrade it.

A gateway answers /readyz with 503 in three cases, each with its own body: draining (the control plane marked the node as draining, so it leaves the Service while in-flight requests finish), awaiting_snapshot (it has not yet applied a first configuration snapshot) and snapshot_format_unsupported (the control plane it polls cannot serve a snapshot format the gateway can read, as when a new gateway starts before its control plane). In all three the pod stays out of the Service and nothing is killed. Alert on /readyz staying down and on /healthz flapping. The control plane’s readiness and startup windows are control.probes.


Image tags#

Both workloads run image.tag, which defaults to the chart’s appVersion. control.image.tag and gateway.image.tag move one component alone, so the control plane can be upgraded first and the gateways after it, and the gateways can be held on, or rolled back to, an older release. A gateway may lag behind its control plane but never lead it. The procedure, and when it matters, is in Two steps with the Helm chart.


Scaling, disruption and exposure#

  • Autoscaling. gateway.autoscaling.enabled adds a HorizontalPodAutoscaler on CPU (targetCPUUtilizationPercentage) between minReplicas and maxReplicas. It needs metrics-server.
  • Disruption. gateway.podDisruptionBudget keeps at most maxUnavailable gateways down during a drain.
  • Ingress. ingress.enabled publishes the Services through your controller. Each entry in ingress.hosts[].paths[] names the service (gateway or control) it routes to, and ingress.tls carries the certificates.
  • Hardening. The pods run as non-root with a read-only root filesystem, no Linux capabilities and no service-account token.

Control plane limits#

ValuesVariableDefaultMeaning
control.pool.maxConnectionsROLTER_DB_MAX_CONNECTIONS10Postgres pool per pod; keep it times replicaCount under the database’s own limit
control.requestTimeoutSecondsROLTER_REQUEST_TIMEOUT_SECS60Outer bound on one API request
control.login.*ROLTER_LOGIN_*Failed sign-in throttling
control.export.maxRowsROLTER_EXPORT_MAX_ROWS1000000Most rows one request log CSV export carries
control.export.maxSecondsROLTER_EXPORT_MAX_SECS600Longest one export runs; also frees the place of a client that stopped reading
control.export.maxConcurrentROLTER_EXPORT_MAX_CONCURRENT4Exports running at once in one pod
control.export.maxConcurrentPerCallerROLTER_EXPORT_MAX_CONCURRENT_PER_CALLER1Exports running at once for one caller in one pod

0 turns an export bound off. The concurrency counts are per control-plane pod. See Dashboard analytics API for what an export does past a bound.

control.updateCheck: false stops the control plane, and the preflight init container, from asking GitHub for the latest release, for a cluster that must make no outbound call.