Kubernetes
The Helm chart runs the API, the web app and a worker for background jobs. You bring PostgreSQL, the object store, the SMTP server and the sign-in provider, and route traffic with the chart's Ingress or with your own.
Before you start
- Helm 3.8 or later, since the chart is published as an OCI artifact.
- 2 hostnames, one for the web app and one for the API, such as
eventail.example.comandapi.eventail.example.com. Both apps serve from/, so they cannot share a hostname. - A PostgreSQL database, managed or run in the cluster, for example with CloudNativePG. The chart does not bundle one.
- A sign-in provider set up as described in Sign-in provider.
- A bucket set up as described in Object storage.
- An SMTP server that Eventail can send mail through.
Create the secrets
The chart reads secrets only from Kubernetes Secrets you create, never from the values, so no secret ends up in the release history.
Create the Secrets in the namespace you install into:
kubectl create namespace eventail
kubectl create secret generic eventail-postgres --namespace eventail \
--from-literal=password=...
kubectl create secret generic eventail-s3 --namespace eventail \
--from-literal=accessKeyId=... --from-literal=secretAccessKey=...
kubectl create secret generic eventail-smtp --namespace eventail \
--from-literal=user=... --from-literal=pass=...| Values key | Secret keys | If unset |
|---|---|---|
postgres.existingSecret | password, or the key named in postgres.passwordKey | Required. |
s3.existingSecret | accessKeyId, secretAccessKey | The AWS default credential chain applies, for example AWS_* variables set through api.extraEnv or the node's instance role. |
email.smtp.existingSecret | user, pass | No authentication is sent. |
If CloudNativePG runs the database in the namespace you install into, skip eventail-postgres. For a cluster named eventail-db, CloudNativePG creates a Secret eventail-db-app with the password under password, and serves the database at eventail-db-rw. This holds as long as the cluster keeps CloudNativePG's default database and owner, both app.
Write the values
A minimal values.yaml:
webUrl: https://eventail.example.com
apiUrl: https://api.eventail.example.com
web:
oidc:
authority: https://id.example.com/realms/eventail
clientId: eventail-web
postgres:
hostname: postgres.example.com # eventail-db-rw with CloudNativePG
database: eventail # app with CloudNativePG
username: eventail # app with CloudNativePG
existingSecret: eventail-postgres # eventail-db-app with CloudNativePG
jwt:
issuer: https://id.example.com/realms/eventail
audience: eventail
superAdminPredicate: "sub == 'replace-with-your-subject'"
integrationPredicate: "`false`"
userInfo:
emailAddressPath: email
displayNamePath: name
s3:
bucketName: eventail
publicBaseUrl: https://s3.example.com/eventail
endpoint: https://s3.example.com
forcePathStyle: true
existingSecret: eventail-s3
email:
sender: eventail@example.com
smtp:
host: smtp.example.com
existingSecret: eventail-smtp
ingress:
enabled: true
className: nginx
tls:
- secretName: eventail-tls
hosts:
- eventail.example.com
- api.eventail.example.comwebUrl and apiUrl are the only place the public addresses go. The Ingress hosts, the origin the API accepts requests from and the links in emails are all derived from them, so both must be origins exactly as a browser shows them: lowercase scheme and host, a port only when it is not the default, no path and no trailing slash.
The Ingress serves TLS from the eventail-tls Secret, which must already exist. Create it from your certificate with kubectl create secret tls, or have cert-manager issue it by adding an annotation under ingress.annotations, for example cert-manager.io/cluster-issuer: letsencrypt.
A cluster that routes with Gateway API sets httpRoute instead, naming the Gateway to attach to:
httpRoute:
enabled: true
parentRefs:
- name: public
namespace: gatewaysThe chart creates one HTTPRoute per hostname and nothing else. The Gateway holds the listeners and their certificates, so TLS is configured there rather than in these values, and the chart does not create the Gateway: it is usually shared and outlives any one release. Both routers may be enabled at once, which is how you move from one to the other without a gap.
Both predicates are required. Leave integrationPredicate at `false`, which matches no token, until you build an integration. Sign-in provider explains both.
The chart validates the values against its schema, so a missing required value fails at helm install, before any pod starts. helm show values lists every value with its default:
helm show values oci://ghcr.io/eventail-scheduling/charts/eventailAny API setting the values do not cover goes into api.extraEnv, as a list of Kubernetes EnvVar entries. The worker receives these too, and settings for the worker alone go into worker.extraEnv. The chart refuses a variable in either list that it already sets itself. Configuration lists every setting with its variable name.
api:
extraEnv:
- name: LOG_LEVEL
value: warn
worker:
extraEnv:
- name: WORKER_RUNTIME_CONCURRENCY
value: "4"
resources:
limits: { memory: 1Gi }Set resources explains the worker's memory limit.
Install
helm install eventail oci://ghcr.io/eventail-scheduling/charts/eventail \
--namespace eventail --values values.yamlThe API migrates the database as it starts. Its pods report ready only once the migration has run and the API has read the sign-in provider's discovery document. If they stay unready, the API's log names the setting at fault:
kubectl logs --namespace eventail deployment/eventail-apiSign in
Open webUrl and sign in with the account that jwt.superAdminPredicate matches. As a super admin, you create the first team and the first edition. If signing in fails, Sign-in provider lists what the API checks.
Without the chart's routing
Leave both ingress.enabled and httpRoute.enabled at their default, false, and route each hostname to its Service on port 80. For a release named eventail those are eventail-web and eventail-api; kubectl get services shows the names for any other release name. The chart still derives everything else from webUrl and apiUrl.
Security defaults
Every pod runs as a non-root user with a read-only root filesystem, no privilege escalation, all capabilities dropped and the RuntimeDefault seccomp profile. Each gets an emptyDir at /tmp, the only place the containers write to. No pod mounts a service account token.
Workers
Background jobs run in a Deployment of their own by default, so image processing does not compete with requests for the API's CPU and memory. Workers and scaling explains the trade-offs and the settings.
Set resources
The chart sets no resource requests or limits. api.resources, worker.resources and web.resources each take a Kubernetes resources block. Measured with the 0.1.0 images and the default standalone worker:
| Pod | Idle (MiB) | Peak (MiB) | Load |
|---|---|---|---|
| API | 150 | 440 | 50 clients reading at once, about 500 requests a second |
| Worker | 120 | 265 | Processing uploaded images one at a time, photos up to 24 megapixels |
| Web | 30 | 30 | Static files only |
A starting point with headroom over those peaks:
api:
resources:
requests: { cpu: 100m, memory: 256Mi }
limits: { memory: 512Mi }
worker:
resources:
requests: { cpu: 100m, memory: 192Mi }
limits: { memory: 512Mi }
web:
resources:
requests: { cpu: 10m, memory: 32Mi }
limits: { memory: 64Mi }Leave CPU unlimited so image processing can use spare cores. Under the load above, the API and the worker both use more than 1 core at times.
The worker's memory grows with WORKER_RUNTIME_CONCURRENCY: at 4 it peaks at about 610 MiB. Raise its limit to 1Gi before you raise the concurrency to 4, as the extraEnv example above does. With worker.enabled: false the API runs the jobs itself and needs the worker's headroom on top of its own, about 1Gi in total.
Upgrade
Each chart release pins the Eventail version it was tested with. The eventail-deploy releases page carries both charts, and this one's are tagged eventail-v*. Read those and Eventail's before upgrading: a release that adds a required setting says so. Then run:
helm upgrade eventail oci://ghcr.io/eventail-scheduling/charts/eventail \
--namespace eventail --values values.yaml