Skip to main content

Kubernetes (Helm)

A single container is one thing to run; keeping several replicas of it healthy, routed to, and rolling through updates without downtime is a different problem, and that's what a real Kubernetes deployment buys you over just running the Docker image by hand. rapidrest generate k8s packages a project as a Helm chart to run it that way:

rapidrest generate k8s

This adds a Helm chart under helm/. Like generate docker, it's idempotent: regenerate it with --force any time your project's datastores change (see CLI → Add-ons).

Architecture

The chart deploys your server as a stateless Kubernetes Deployment, not a StatefulSet. Every replica is identical and sits behind a single Service, which is in turn exposed outside the cluster through a Gateway API Gateway/HTTPRoute rather than an Ingress. Each pod waits, via an init container, for its datastores to accept connections before starting.

------------ --------------- -----------
| Gateway | ---> | HTTPRoute | ---> | Service |
------------ --------------- -----------
|
-----------------------------------------------------------
| | |
------------ ------------ ------------
| Pod | | Pod | ... | Pod |
| (replica) | | (replica) | | (replica) |
------------ ------------ ------------
| | |
-----------------------------------------------------------
|
-----------------------------------
| |
------------ ------------
| MongoDB | | Redis |
| or | | (optional, |
| PostgreSQL | | standalone)|
| (optional, | ------------
| standalone)|
------------

What's in the chart

MongoDB, PostgreSQL, and Redis are optional dependencies, pulled in as Bitnami subcharts only if your project actually uses them, each gated behind a condition in Chart.yaml (mongodb.create / postgresql.create / redis.create) so they can be swapped out for externally-managed databases in values.yaml without touching the chart itself.

The templates are organized by the order Kubernetes resources naturally depend on each other:

helm/templates/
├── 0_config/ # ConfigMap + Secrets: becomes your app's env vars
├── 1_deployments/ # the server Deployment
├── 2_services/ # the Service exposing it inside the cluster
├── 3_gateways/ # Gateway + HTTPRoute exposing it outside the cluster
└── NOTES.txt # printed after `helm install` (see below)

Configuring values.yaml

host and environment drive most of the chart's other defaults: host sets the auth.audience/auth.issuer claims, the Gateway/HTTPRoute hostname, and the TLS certificate's domain, while environment sets NODE_ENV on the deployment (and, outside production, enables --inspect on the container for remote debugging). auth.secret, cookies.secret, and sessions.secret default to Helm's randAlphaNum 32, a fresh random value generated at install time and preserved across upgrades rather than a value you set. That's deliberate (see Save the install output below).

mongodb.create, postgresql.create, and redis.create only appear in a freshly generated project's values.yaml for the datastores that project actually uses, matching whichever of MongoDB/PostgreSQL/Redis you configured when the project was scaffolded. Set the corresponding create to false and provide a connection URL (mongodb.url/postgresql.url/redis.url) to point at an instance you already run instead of the bundled Bitnami subchart.

Just like Docker Compose, every datastore ends up as a datastores__<name>__<key> environment variable on the pod, sourced from a generated ConfigMap/Secret pair rather than typed in by hand. These are the same nconf keys from Configuration, just populated by Kubernetes instead of Compose.

Parameters

NameDescriptionValue
hostPublic hostname the Gateway, HTTPRoute, and TLS certificate are issued forlocalhost
environmentSets NODE_ENV for the deploymentdev
auth.audienceJWT audience claim'{{ .Values.host }}'
auth.issuerJWT issuer claim'api.{{ .Values.host }}'
auth.expiresInAccess token lifetime1 hour
auth.secretJWT signing secret. Randomly generated at install time and preserved across upgrades'{{ randAlphaNum 32 }}'
cookies.secretCookie signing secret. Randomly generated at install time and preserved across upgrades'{{ randAlphaNum 32 }}'
sessions.secretSession secret. Randomly generated at install time and preserved across upgrades'{{ randAlphaNum 32 }}'
gateway.nameName of the Gateway the HTTPRoute attaches to. The chart only creates this Gateway itself when the name and namespace are both left at their defaults; point it at an existing Gateway to share one across releasesapi-gateway
gateway.namespaceNamespace of that Gateway'{{ .Release.Namespace }}'
gateway.tlsAdd an HTTPS listener and request a cert-manager certificate for host. Skipped automatically when host is localhost or ends in .localtrue
gateway.hstsKeep the Strict-Transport-Security response header. Set to false to strip it, for local or non-TLS testingtrue
redis.createDeploy a Redis instance via the Bitnami subchart. Present only if your project uses a redis datastoretrue
redis.urlRedis connection URL. Override when redis.create is false to point at an external Redisredis://db-redis-master
redis.architectureRedis architecture, passed through to the Bitnami subchartstandalone
redis.auth.enabledRequire a password on the bundled Redistrue
redis.fullnameOverrideResource name prefix for the bundled Redis subchartdb-redis
redis.master.persistence.enabledEnable persistence on the Redis masterfalse
redis.replica.persistence.enabledEnable persistence on Redis replicasfalse
redis.networkPolicy.enabledCreate a NetworkPolicy restricting access to Redistrue
redis.networkPolicy.allowExternalAllow traffic from outside the cluster when networkPolicy.enabled is truefalse
mongodb.createDeploy a MongoDB instance via the Bitnami subchart. Present only if your project uses a mongodb datastoretrue
mongodb.urlMongoDB connection URL, used only when mongodb.create is false""
mongodb.architectureMongoDB architecture, passed through to the Bitnami subchartstandalone
mongodb.auth.enabledRequire credentials on the bundled MongoDBtrue
mongodb.fullnameOverrideResource name prefix for the bundled MongoDB subchartmongodb
postgresql.createDeploy a PostgreSQL instance via the Bitnami subchart. Present only if your project uses a postgres datastoretrue
postgresql.urlPostgreSQL connection URL, used only when postgresql.create is false""
postgresql.architecturePostgreSQL architecture, passed through to the Bitnami subchartstandalone
postgresql.fullnameOverrideResource name prefix for the bundled PostgreSQL subchartpostgresql
postgresql.auth.enablePostgresUserRequire credentials on the bundled PostgreSQLtrue
postgresql.auth.databaseName of the one database the bundled subchart auto-provisions on first startyour project's name
service.image.registryContainer image registryghcr.io
service.image.repositoryContainer image repositoryyour project's name
service.image.tagContainer image tag. Defaults to the chart's appVersion when empty""
service.imagePullSecretName of an existing image pull secret""
service.replicasNumber of replicas1
service.resources.requests.cpuCPU request100m
service.resources.requests.memoryMemory request256Mi
service.resources.limits.memoryMemory limit512Mi
service.mongodb.acl.nameDatabase name for the ACL datastoreaccess_control_lists
service.mongodb.acl.hostHost override for the ACL datastore""
service.mongodb.acl.urlFull connection URL override for the ACL datastore""
service.mongodb.mongo.nameDatabase name for the application datastoreyour project's name
service.mongodb.mongo.hostHost override for the application datastore""
service.mongodb.mongo.urlFull connection URL override for the application datastore""
service.postgresql.acl.nameDatabase name for the ACL datastorepostgres
service.postgresql.acl.hostHost override for the ACL datastore""
service.postgresql.acl.urlFull connection URL override for the ACL datastore""
service.postgresql.sql.nameDatabase name for the application datastoreyour project's name
service.postgresql.sql.hostHost override for the application datastore""
service.postgresql.sql.urlFull connection URL override for the application datastore""
service.configArbitrary key/value pairs merged into the service's environment configuration{logs: true, metrics: true, releaseNotes: true}
service.specArbitrary key/value pairs merged into the pod spec, for settings such as nodeSelector or tolerations{}

Installing

helm install my-api ./helm

Before your service starts serving traffic, init containers block startup until Redis and whichever of MongoDB/PostgreSQL is bundled respond to a basic connectivity probe. The pod won't report ready before its datastores are actually reachable.

Save the install output

Because auth.secret, the bundled datastore's admin password, and other credentials are randomly generated at install time rather than configured up front, helm install prints them to the terminal exactly once, via NOTES.txt. There's no way to recover them from the cluster afterward except by reading the underlying Secret resources directly. Write them down (or better, pull them into your own secret manager) before you close that terminal.

Routing

The chart uses the Kubernetes Gateway API (a Gateway + HTTPRoute), not a classic Ingress. Your cluster needs a Gateway API-compatible controller installed (the chart assumes an NGINX Gateway Fabric-style gatewayClassName: nginx). TLS termination and HSTS are both configurable via gateway.tls/gateway.hsts in values.yaml.

Trying it locally

If you don't already have a cluster to deploy to, helm/scripts/k3s_install.sh sets up a single-node k3s cluster with the prerequisites this chart expects (a Gateway API controller, the Bitnami Helm repo, and so on), a reasonable way to try a full Helm deployment on a single machine before deploying to a real cluster.