Files
twenty/packages/twenty-docker/helm/twenty
Miguel 921c528cc3 feat(helm): add nodeSelector, tolerations and DNS overrides to server and worker (#22233)
Closes #22250

## What

Adds four standard pod spec fields to both server and worker Deployments
of the Helm chart, exposed under `server.*` and `worker.*` in
`values.yaml`:

- `nodeSelector`
- `tolerations`
- `dnsPolicy`
- `dnsConfig`

## Why

Self-hosters who run Twenty on clusters with dedicated nodes, taints, or
custom DNS requirements currently need to fork the chart or patch
rendered manifests with Kustomize. These are the standard pod spec
fields supported by virtually every other community chart (Bitnami,
prometheus-community, cert-manager, etc.) and are commonly needed in
production setups.

## How

Uses the same `{{- with }}` pattern already present in the chart (e.g.
`extraEnv`, `extraVolumeMounts`), so empty defaults skip rendering
entirely:

```yaml
{{- with .Values.server.nodeSelector }}
nodeSelector:
  {{- toYaml . | nindent 8 }}
{{- end }}
```

## Backward compatibility

Fully backward compatible. Defaults are empty:

```yaml
server:
  nodeSelector: {}
  tolerations: []
  dnsPolicy: ~
  dnsConfig: {}
```

`helm template` output is bit-identical to the previous version for any
existing install.

## Schema note

`dnsPolicy` is typed as `[string, null]` and the enum includes `null` so
the empty default (`dnsPolicy: ~`) passes validation. The `{{- with }}`
guard treats null as falsy and renders nothing.

## Validation

- `helm lint` passes
- `helm template` with default values produces bit-identical output to
before this PR
- `helm template` with values set renders the four fields correctly on
both server and worker
- Live install validated on a multi-node Kubernetes cluster, pinning
Twenty to a dedicated tainted node with custom DNS (`ndots: 1`); server
and worker scheduled and started successfully

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22233?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-06-29 13:00:42 +02:00
..
2026-01-08 12:45:46 +00:00
2026-01-08 12:45:46 +00:00
2026-01-08 12:45:46 +00:00

Twenty Helm Chart

Deploy Twenty CRM on Kubernetes with server, worker, PostgreSQL, and Redis components.

Features

  • Server and worker deployments with full env exposure via values.yaml.
  • Internal PostgreSQL (Spilo) and Redis deployments included.
  • PVC-based persistence using dynamic storage classes (no static PV manifests).
  • Ingress with configurable annotations, hosts, and TLS.
  • Database readiness and migrations handled by server/worker init containers by default. Standard Kubernetes Jobs for DB creation/user and migrations have been removed to simplify installs. Readiness and migrations run in init containers.

Quick Start

See QUICKSTART.md for a simple 2-line install with your domain.

Installing

Prerequisites: Kubernetes 1.21+, Helm 3.8+, default StorageClass

Internal DB + Redis (default):

helm install my-twenty ./packages/twenty-docker/helm/twenty \
  --namespace twentycrm --create-namespace

External DB/Redis:

helm install my-twenty ./packages/twenty-docker/helm/twenty \
  --namespace twentycrm --create-namespace \
  --set db.enabled=false \
  --set db.external.host=db.example.com \
  --set redisInternal.enabled=false

Key Values

See values.yaml for a comprehensive list.

Notes

  • Database URL and Redis URL are composed automatically from chart settings
  • Database twenty and schema core are created automatically by server init container
  • No optional jobs: the chart no longer provides separate Jobs for DB or migrations.
  • Access token auto-generated (32 chars) if not provided; reuses existing secret if present
    • For production, provide a strong secrets.tokens.accessToken value via a secure values file; the auto-generated token is a convenience fallback.
  • TLS enabled by default via cert-manager (acme: true)
  • Requires default StorageClass for PVC provisioning

Testing

helm lint ./packages/twenty-docker/helm/twenty
helm template my-twenty ./packages/twenty-docker/helm/twenty
helm plugin install https://github.com/quintush/helm-unittest
helm unittest ./packages/twenty-docker/helm/twenty

Storage

Local (default): Uses PVCs for persistence

S3: Set storage.type=s3 and provide credentials using a values file. You can either pass credentials directly or reference an existing Kubernetes Secret.

# values-secrets.yaml (do not commit)
# storage:
#   type: s3
#   s3:
#     bucket: my-bucket
#     region: us-east-1
#     # Option A: direct values
#     accessKeyId: AKIA...
#     secretAccessKey: ...
#     # Option B: reference a Secret
#     # secretName: my-s3-creds
#     # accessKeyIdKey: accessKeyId
#     # secretAccessKeyKey: secretAccessKey

helm install my-twenty ./packages/twenty-docker/helm/twenty -f values-secrets.yaml

Production Tips

  • Image versioning: The chart defaults to Chart.yaml's appVersion (currently v1.14.0). Override via image.tag in values to pin a different version or use latest for rolling updates.
  • Keep secrets secure: Avoid --set for sensitive values; use -f values-secrets.yaml or reference existing Kubernetes Secrets via server.extraEnvFrom.
    • S3 credentials can be referenced via storage.s3.secretName + accessKeyIdKey/secretAccessKeyKey to avoid embedding them in pod specs.