kadet-hub logokadet-hub

Nextcloud Helm chart: Bitnami subcharts and Kapitan

Nextcloud Helm chart and Bitnami: what the 9.4.1 subcharts run today, the chart on your own PostgreSQL and Valkey, and a Kapitan recipe tested on kind.

The Nextcloud Helm chart 9.4.1 packages Bitnami’s PostgreSQL, MariaDB and Redis charts as optional subcharts, all switched off by default. Switched on, they run images from bitnamilegacy, a catalog Bitnami no longer updates. The chart can also use a database and a cache that run outside it, with credentials from existing Secrets. The nextcloud recipe rebuilds the chart for Kapitan and puts a CloudNativePG cluster and Valkey in place of the subcharts. What Bitnami changed is in the Bitnami post, and the replacements for other charts are in Bitnami chart alternatives.

What chart 9.4.1 declares

With Helm 4.3.1:

helm show chart oci://ghcr.io/nextcloud/helm/nextcloud --version 9.4.1

The output lists four dependencies:

dependencies:
- condition: postgresql.enabled
  name: postgresql
  repository: oci://registry-1.docker.io/bitnamicharts
  version: 16.7.4
- condition: mariadb.enabled
  name: mariadb
  repository: oci://registry-1.docker.io/bitnamicharts
  version: 20.5.5
- condition: redis.enabled
  name: redis
  repository: oci://registry-1.docker.io/bitnamicharts
  version: 21.1.3
- alias: collabora
  condition: collabora.enabled
  name: collabora-online
  repository: https://collaboraonline.github.io/online
  version: 1.1.60

Three come from Bitnami’s bitnamicharts repository on Docker Hub. The fourth is the Collabora Online office server from Collabora’s own repository. The chart’s values.yaml sets all four conditions to false. A default install keeps Nextcloud’s data in SQLite (internalDatabase.enabled: true), which the chart’s README does not recommend for production.

The chart archive carries the three Bitnami charts under charts/, so an install fetches nothing from bitnamicharts. Only their images come from Docker Hub.

What the subcharts run today

helm template nextcloud oci://ghcr.io/nextcloud/helm/nextcloud --version 9.4.1 \
  --set internalDatabase.enabled=false \
  --set postgresql.enabled=true --set redis.enabled=true | grep -o 'image: .*' | sort -u
image: docker.io/bitnamilegacy/postgresql:17.5.0-debian-12-r3
image: docker.io/bitnamilegacy/redis:8.0.1-debian-12-r1
image: docker.io/library/nextcloud:35.0.2-apache

The chart’s values replace each subchart’s image repository with bitnamilegacy/<name> and keep the subchart’s tag. Both tags resolve in docker.io/bitnamilegacy. In docker.io/bitnami both returned manifest unknown on 2 November 2026, the registry’s answer for a tag it does not have. The chart’s README still names bitnami/postgresql and bitnami/mariadb as the defaults, but Helm uses what values.yaml says.

The PostgreSQL subchart also comes without storage. The chart’s values set postgresql.primary.persistence.enabled: false, and the rendered StatefulSet mounts the data directory from an emptyDir. The database is gone when the pod is deleted or moves to another node. The values give MariaDB the same default. The two Redis StatefulSets keep their claims.

The chart with a database and cache of your own

The chart reads an external PostgreSQL and an external Redis from existing Secrets. These values use the names that the recipes further down create:

internalDatabase:
  enabled: false
externalDatabase:
  enabled: true
  type: postgresql
  existingSecret:
    enabled: true
    secretName: postgresql-cluster-app
    usernameKey: username
    passwordKey: password
    hostKey: host
    databaseKey: dbname
externalRedis:
  enabled: true
  host: valkey
  existingSecret:
    enabled: true
    secretName: valkey-auth
    passwordKey: default-password

postgresql-cluster-app is the Secret the CloudNativePG operator writes for a Cluster named postgresql-cluster, with a password it generates. valkey is the Service of the Valkey instance, and valkey-auth holds its password. With these values helm template renders a Deployment, a Service and a Secret with the admin account. A server-side dry run of the three on kind with Kubernetes 1.37.0 passed. That shows the API server accepts them, not that Nextcloud installs against the database. The deploy test of the recipe below covers that part.

The nextcloud recipe

The nextcloud recipe in kadet-hub/recipes rebuilds chart 9.4.1 as Kapitan classes for the kubernetes generator 1.9.0. Its version 1.0.1 was published on 27 October 2026. It runs docker.io/library/nextcloud:35.0.2-apache, pinned by digest, as one replica. The chart’s cron sidecar runs the background jobs in the same pod. One claim holds the config, custom apps and themes, the other the data directory, 8Gi each by default.

In place of the subcharts the recipe includes two other recipes:

The recipe keeps the chart’s HTTPRoute. It attaches to a Gateway the cluster already runs, which owns TLS and the listener. Two rules redirect /.well-known/carddav and /.well-known/caldav to /remote.php/dav, and a third sends everything else to the Service on port 8080.

A target sets the host, the Gateway’s name and namespace, and the admin and Valkey passwords as refs. The recipe page has the snippet, the fetch inventory pinned by digest and the commands that verify and compile it.

Apache in the image starts as root and serves as www-data, UID 33. The namespace therefore needs the Pod Security level baseline.

The deploy test on kind

The kadet-hub/recipes CI runs check nextcloud --level deploys from its ci workflow. Run 40481509900 on 27 October 2026 checked commit 21d393a, which the tag nextcloud-v1.0.1 points to.

check first rendered the chart with the recipe’s tests/values.yaml and compared its six objects with the recipe’s: the Deployment, the Service, the Secret, the HTTPRoute and the two claims. They differ in the selector, where the recipe uses name and the chart app.kubernetes.io/name, instance and component. They also differ in defaults the recipe writes out, such as automountServiceAccountToken: true.

Then check deployed the recipe on kind with kindest/node:v1.37.0, after the CloudNativePG operator and Gateway API. The smoke test waited for the Cluster and read status.php through the Gateway until it reported "installed":true. It wrote a file over WebDAV as the admin and read it back, which needs the database, the data claim and file locking. Last, it checked that occ reports Redis as the file locking backend. The job ended with deploys: passed.

The test target runs one PostgreSQL instance instead of three. The two DAV redirects are compared at renders only, because cloud-provider-kind, which serves the Gateway on kind, drops the path of a redirect.

Nextcloud’s admin manual for version 35 says Valkey is “expected to work” as a Redis-compatible backend. Its formal tests run Redis through the redis PHP extension and Valkey through Nextcloud’s own keyvalue cache provider. The Docker image sets up the redis extension, so the recipe runs a pairing the manual expects to work but does not test. The smoke test above is the only test of that pairing in the recipe’s CI.

What the recipe leaves out

The recipe’s README says: “SMTP, notify_push, an office server, imaginary, metrics and more than one replica are not part of this version.” The single replica follows from the chart’s Recreate strategy and its ReadWriteOnce claims.

The recipe renders no backup. Its README lists what a target backs up: the database through the Barman Cloud Plugin, which no recipe installs, and the two claims, with Nextcloud in maintenance mode around both.

Some settings stay with the target. TRUSTED_PROXIES has no default, and without it Nextcloud takes the proxy’s address for the client’s. PHP accepts uploads up to PHP_UPLOAD_LIMIT, 512M by default. Gateway API has no body limit of its own, so the Gateway implementation’s policy has to allow that size. The image writes the host into config.php at install, so a later change of the host also needs occ config:system:set trusted_domains.

Moving a running Helm release and its data to the recipe is not part of the recipe and has not been tested.