Nextcloud
Nextcloud provides self-hosted file synchronization, sharing and collaboration. This chart uses the official Apache image with PostgreSQL, Redis, managed background jobs and coordinated S3 backups.
Requirements
- Kubernetes 1.29 or newer and Helm 3 or newer.
- Persistent storage supporting UID/GID 33 and fsGroup ownership.
- Public DNS and a TLS-enabled ingress or Gateway controller for external access.
- An existing private S3-compatible bucket and credential Secret for backups.
- External Secrets Operator only when that integration is enabled.
The application defaults to nextcloud:35.0.1-apache, with verified amd64 and
arm64 support. The chart is Apache-2.0 licensed; Nextcloud Server is AGPL-3.0.
One replica and Recreate updates provide an explicit single-writer operating
model. Backups and upgrades have scheduled downtime.
Installation
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install nextcloud helmforge/nextcloud \
--namespace nextcloud --create-namespace
Alternatively, use OCI:
helm install nextcloud oci://ghcr.io/helmforgedev/helm/nextcloud \
--namespace nextcloud --create-namespace
Wait for initial installation to complete, then forward the local service:
kubectl -n nextcloud port-forward service/nextcloud 8080:80
Open http://localhost:8080. The default administrator login is admin. Retrieve
the admin-password key from Secret nextcloud-auth using your approved Secret
workflow. Prefer nextcloud.existingSecret for managed production credentials.
The bootstrap password does not reset existing account passwords.
Features
- Official application image and official HelmForge PostgreSQL/Redis dependencies.
- Automated native installation and guarded sequential application upgrades.
- Persistent native configuration, instance identity, custom apps, themes and data.
- Non-root Apache, read-only root filesystem, dropped capabilities and seccomp.
- Co-located cron coordinated with the native initialization lock.
- Existing Secret and External Secrets Operator contracts.
- Explicit trusted hosts/proxies, SMTP, Ingress, Gateway API and dualstack Service.
- Coordinated SQL/files backup, checksummed S3 publication and fresh-storage restore.
- Real HTTP/WebDAV acceptance tests and a full backup/recovery scenario.
Production configuration
Start with examples/production.yaml. Supply the actual public hostname, proxy CIDRs, TLS Secret, storage sizes and credential Secrets. Configure ingress upload limits and timeouts alongside the PHP limits. Verify email delivery before relying on password recovery or sharing notifications.
Helm uninstall deletes the chart-created application PVC. Use
persistence.existingClaim when the volume lifecycle is managed independently,
and retain tested backups before removing a release or namespace.
PostgreSQL and Redis are enabled by default. To use managed private endpoints,
disable the corresponding subchart and provide externalDatabase or
externalRedis settings. These external services must already exist. The chart
supports PostgreSQL and standalone Redis; it does not perform database-engine
conversion or configure Redis Sentinel/Cluster discovery.
Optional integrations
Both integrations are disabled by default. They run alongside Nextcloud in its single application Pod and inherit its non-root security context. No additional public Service, Ingress or HTTPRoute is created. Enabling either adds its resource requests to the Pod; reserve capacity before upgrading.
Imaginary previews
Set imaginary.enabled: true to use the official Nextcloud AIO Imaginary image,
pinned by an immutable multi-platform digest. Imaginary listens on loopback port
9000, receives preview uploads from Nextcloud and has no access to the data PVC.
Remote URL fetching is disabled. Its temporary files use a bounded emptyDir.
The chart manages preview_imaginary_url and enabledPreviewProviders in its
configuration snippet. The default providers preserve text, Markdown,
OpenDocument and Krita previews and offload supported images to Imaginary.
imaginary.previewProviders replaces the whole list; PDF preview is deliberately
opt-in through OC\Preview\ImaginaryPDF. Set imaginary.maxAllowedResolution
(megapixels) and resource limits to suit your workloads. Unsupported formats
remain without previews unless an appropriate provider is configured.
Disabling the integration removes the managed settings on the next rollout. Existing cached previews remain on the Nextcloud volume. Do not maintain competing preview settings in another config snippet.
Client Push
Set notifyPush.enabled: true to start the official nextcloud/notify_push daemon.
The chart generates its configuration in a memory-backed volume from the same
database and Redis Secrets as Nextcloud. Existing database table prefixes are
preserved. Restart the Deployment after credential rotation, as the daemon reads
configuration at startup. The daemon does not mount the application/data PVC.
Apache proxies /push/ and /push/ws to the daemon. Your existing Ingress or
Gateway must route these paths, support WebSocket upgrades and allow long-lived
connections. Configure TLS and nextcloud.overwriteCliUrl with the public URL.
The chart trusts loopback for the daemon’s callback requests; retain the actual
ingress/Gateway addresses in nextcloud.trustedProxies as well.
The Nextcloud Client Push app remains operator-managed. Once Nextcloud is installed, install a compatible app release and run the upstream setup test:
kubectl -n nextcloud exec deployment/nextcloud -c nextcloud -- php occ app:install notify_push
kubectl -n nextcloud exec deployment/nextcloud -c nextcloud -- php occ notify_push:setup https://cloud.example.com/push
For an already installed app use occ app:enable notify_push. The pinned daemon
version is 1.4.1; keep the app and daemon compatible when upgrading. Helm does not
download or update apps automatically. Air-gapped installations must stage the
compatible official app using Nextcloud’s supported app installation workflow.
Run occ notify_push:setup again after changing the public hostname or routing.
Its successful result verifies Redis messages, database mappings, callback
connectivity, trusted proxies and version compatibility. Verify client sync
through your actual external endpoint too.
Daemon readiness checks its listener, not whether the Nextcloud app has been installed or the public route configured. Installation is complete only after the setup test passes. Clients still periodically poll because push delivery is best-effort.
Enable networkPolicy.enabled to prevent other Pods from directly accessing
daemon port 7867. Keep ingress peers allowed for Apache port 8080. App Store
downloads need HTTPS egress; external database and Redis endpoints need their
normal explicit egress rules. Imaginary uses only loopback and needs no egress.
Before disabling Client Push, run occ app:disable notify_push, then disable
notifyPush.enabled. This prevents advertising an unavailable push endpoint.
The daemon stops with the application during coordinated backup and restore;
the app itself is retained in the application PVC and database backup.
See the integration values example, official Client Push instructions, and Nextcloud preview tuning.
Backup and restore
Integrated backups stop the application and cron, capture PostgreSQL and the complete PVC, resume service, and publish checksummed objects with a final completion marker. A failed snapshot retains a volume lock for explicit recovery. Restore requires a completed backup, matching application version, original administrator credentials, fresh PVC and empty database.
See backup and restore before enabling the schedule. Retain external Secrets separately. Practice restoration and verify non-admin login, file contents/identities, shares, settings and new writes.
Examples and operational guides
- Local installation
- Production routing and backups
- Gateway API
- External database and Redis
- Configuration and exposure
- Backup, failure recovery and restore
- Upgrades and migration from another chart
- Architecture and tradeoffs
Nextcloud 35 is a major application upgrade. Release 35.0.1 fixes sharing, ownership transfer, encryption key-cache, sensitive logging, CalDAV and file metadata behavior. Back up the database, data PVC and configuration, confirm app compatibility, and upgrade only one major version at a time.
Non-goals
This chart does not provision office suites, Talk media relays, full-text search, horizontal Nextcloud scaling or primary S3 file storage. Its S3 integration is backup storage. Custom Nextcloud apps are operator-managed and must support the selected upstream release. Helm rollback cannot reverse database migrations.
Parameters
| Parameter | Description | Default |
|---|---|---|
nameOverride |
Override the chart name. | "" |
fullnameOverride |
Override the release resource name. | "" |
commonLabels |
Extra labels on resources. | {} |
image.repository |
Official image repository. | "docker.io/library/nextcloud" |
image.tag |
Pinned application release. | "35.0.1-apache" |
image.pullPolicy |
Image pull policy. | "IfNotPresent" |
imagePullSecrets |
Registry authentication Secret references. | [] |
nextcloud.adminUser |
Initial administrator login; changing this does not rename existing users. | "admin" |
nextcloud.adminPassword |
Initial administrator password; generated and retained when empty. | "" |
nextcloud.existingSecret |
Existing Secret containing the initial administrator password. | "" |
nextcloud.existingSecretPasswordKey |
Password key in the administrator Secret. | "admin-password" |
nextcloud.trustedDomains |
Allowed HTTP Host names, reconciled on upgrades. | ["localhost"] |
nextcloud.trustedProxies |
Explicit reverse proxy IP addresses or CIDRs. | [] |
nextcloud.overwriteCliUrl |
Public URL for links generated by background jobs. | "http://localhost:8080" |
nextcloud.overwriteProtocol |
Proxy protocol override; empty, http or https. | "" |
nextcloud.overwriteHost |
Public host override behind a trusted proxy. | "" |
nextcloud.defaultPhoneRegion |
Default phone region for profile validation. | "US" |
nextcloud.maintenanceWindowStart |
Start hour in UTC for expensive maintenance background tasks. | 1 |
php.memoryLimit |
Maximum memory per PHP request. | "512M" |
php.uploadLimit |
Maximum upload and POST body size. | "512M" |
imaginary.enabled |
Enable private preview offloading. | false |
imaginary.image.repository |
Official Nextcloud Imaginary image. | "ghcr.io/nextcloud-releases/aio-imaginary" |
imaginary.image.digest |
Immutable multi-platform image digest. | "sha256:ea96034f97e9921015001c43947ab88ee5516e7e793c4c753a3618feca9e8548" |
imaginary.image.pullPolicy |
Image pull policy. | "IfNotPresent" |
imaginary.maxAllowedResolution |
Maximum source resolution in megapixels. | 50 |
imaginary.previewProviders |
Complete preview provider list when enabled. | ["OC\\Preview\\TXT","OC\\Preview\\MarkDown","OC\\Preview\\OpenDocument","OC\\Preview\\Krita","OC\\Preview\\Imaginary"] |
imaginary.resources.requests.cpu |
CPU request. | "25m" |
imaginary.resources.requests.memory |
Memory request. | "128Mi" |
imaginary.resources.limits.cpu |
CPU limit. | "1" |
imaginary.resources.limits.memory |
Memory limit. | "512Mi" |
notifyPush.enabled |
Enable the daemon; install and configure the Client Push app separately. | false |
notifyPush.image.repository |
Official Client Push daemon image. | "ghcr.io/nextcloud/notify_push" |
notifyPush.image.tag |
Pinned daemon version. | "v1.4.1" |
notifyPush.image.pullPolicy |
Image pull policy. | "IfNotPresent" |
notifyPush.resources.requests.cpu |
CPU request. | "10m" |
notifyPush.resources.requests.memory |
Memory request. | "32Mi" |
notifyPush.resources.limits.cpu |
CPU limit. | "500m" |
notifyPush.resources.limits.memory |
Memory limit. | "128Mi" |
cron.enabled |
Enable Nextcloud cron processing. | true |
cron.interval |
Seconds between invocations; upstream recommends five minutes. | 300 |
cron.initialDelay |
Delay the first cron invocation after Pod startup, matching upstream timer guidance. | 300 |
cron.resources.requests.cpu |
requests cpu | "25m" |
cron.resources.requests.memory |
requests memory | "128Mi" |
cron.resources.limits.cpu |
limits cpu | "500m" |
cron.resources.limits.memory |
limits memory | "512Mi" |
resources.requests.cpu |
requests cpu | "100m" |
resources.requests.memory |
requests memory | "512Mi" |
resources.limits.cpu |
limits cpu | "2" |
resources.limits.memory |
limits memory | "1536Mi" |
initResources.requests.cpu |
requests cpu | "25m" |
initResources.requests.memory |
requests memory | "64Mi" |
initResources.limits.cpu |
limits cpu | "500m" |
initResources.limits.memory |
limits memory | "128Mi" |
podSecurityContext.runAsNonRoot |
podSecurityContext runAsNonRoot | true |
podSecurityContext.runAsUser |
podSecurityContext runAsUser | 33 |
podSecurityContext.runAsGroup |
podSecurityContext runAsGroup | 33 |
podSecurityContext.fsGroup |
podSecurityContext fsGroup | 33 |
podSecurityContext.fsGroupChangePolicy |
podSecurityContext fsGroupChangePolicy | "OnRootMismatch" |
podSecurityContext.seccompProfile.type |
seccompProfile type | "RuntimeDefault" |
securityContext.allowPrivilegeEscalation |
securityContext allowPrivilegeEscalation | false |
securityContext.readOnlyRootFilesystem |
securityContext readOnlyRootFilesystem | true |
securityContext.capabilities.drop |
capabilities drop | ["ALL"] |
persistence.enabled |
Enable the complete /var/www/html PVC. | true |
persistence.existingClaim |
Reuse a PVC, including a restored instance. | "" |
persistence.storageClass |
Storage class; empty uses the cluster default. | "" |
persistence.accessModes |
Access modes for the single application writer. | ["ReadWriteOnce"] |
persistence.size |
Requested volume size. | "10Gi" |
service.type |
Kubernetes Service type. | "ClusterIP" |
service.port |
Service port mapped to Apache 8080. | 80 |
service.ipFamilyPolicy |
Optional IP family policy. | "" |
service.ipFamilies |
Optional IP family order. | [] |
postgresql.enabled |
Enable the bundled database. | true |
postgresql.auth.database |
auth database | "nextcloud" |
postgresql.auth.username |
auth username | "nextcloud" |
externalDatabase.host |
PostgreSQL DNS name. | "" |
externalDatabase.port |
PostgreSQL port. | 5432 |
externalDatabase.database |
Existing empty database on first installation. | "nextcloud" |
externalDatabase.username |
Database owner login. | "nextcloud" |
externalDatabase.existingSecret |
Secret containing the database password. | "" |
externalDatabase.existingSecretPasswordKey |
Database password key. | "password" |
redis.enabled |
Enable bundled standalone Redis. | true |
redis.architecture |
Supported topology. | "standalone" |
externalRedis.host |
Standalone Redis DNS name. | "" |
externalRedis.port |
Redis port. | 6379 |
externalRedis.existingSecret |
Secret containing the Redis password. | "" |
externalRedis.existingSecretPasswordKey |
Redis password key. | "password" |
podLabels |
Pod labels. | {} |
podAnnotations |
Pod annotations. | {} |
nodeSelector |
Node scheduling constraints. | {} |
tolerations |
Tolerations. | [] |
affinity |
Pod affinity configuration. | {} |
terminationGracePeriodSeconds |
Grace period for draining HTTP requests and active background jobs. | 120 |
ingress.enabled |
Render an Ingress. | false |
ingress.ingressClassName |
Ingress controller class. | "" |
ingress.annotations |
Controller-specific annotations, including request body size limits. | {} |
ingress.hosts |
Host/path rules. | [{"host":"nextcloud.example.com","paths":[{"path":"/","pathType":"Prefix"}]}] |
ingress.tls |
TLS Secret references and hosts. | [] |
gatewayAPI.enabled |
Render HTTPRoute resources. | false |
gatewayAPI.httpRoutes |
Complete route definitions; missing backends target Nextcloud. | [] |
externalSecrets.enabled |
Render ExternalSecret resources. | false |
externalSecrets.refreshInterval |
Default refresh interval. | "1h" |
externalSecrets.items |
Complete ExternalSecret definitions with provider spec. | [] |
smtp.enabled |
Enable SMTP configuration. | false |
smtp.host |
SMTP server hostname. | "" |
smtp.port |
SMTP port. | 587 |
smtp.secure |
Transport security: tls for STARTTLS, ssl for implicit TLS, or empty. | "tls" |
smtp.fromAddress |
Sender local part. | "nextcloud" |
smtp.domain |
Sender domain. | "example.com" |
smtp.username |
Authentication login; empty disables SMTP authentication. | "" |
smtp.existingSecret |
Existing SMTP credential Secret. | "" |
smtp.existingSecretPasswordKey |
SMTP password key. | "password" |
networkPolicy.enabled |
Enable NetworkPolicy. | false |
networkPolicy.extraIngress |
Additional inbound rules; default allows same-namespace clients. | [] |
networkPolicy.extraEgress |
Additional outbound rules for external databases, SMTP and Internet integrations. | [] |
backup.enabled |
Enable scheduled integrated backups. | false |
backup.schedule |
Cron schedule. | "0 2 * * *" |
backup.suspend |
Suspend scheduling, retaining manual job support. | false |
backup.activeDeadlineSeconds |
Maximum total job duration in seconds. | 7200 |
backup.quiesceTimeout |
Maximum time to wait for web and cron termination. | 180 |
backup.successfulJobsHistoryLimit |
Successful job history. | 1 |
backup.failedJobsHistoryLimit |
Failed jobs retained for diagnostics. | 3 |
backup.databaseImage |
Official SQL client; must match the PostgreSQL server major. | "docker.io/library/postgres:18.6-trixie" |
backup.s3Image |
Official AWS CLI for S3-compatible object stores. | "public.ecr.aws/aws-cli/aws-cli:2.36.43" |
backup.workspaceSize |
Maximum ephemeral staging storage; allow space for the complete archive and dump. | "20Gi" |
backup.resources.requests.cpu |
requests cpu | "100m" |
backup.resources.requests.memory |
requests memory | "256Mi" |
backup.resources.limits.cpu |
limits cpu | "2" |
backup.resources.limits.memory |
limits memory | "1Gi" |
backup.s3.endpoint |
Optional endpoint URL for S3-compatible storage. | "" |
backup.s3.allowInsecureHTTP |
Explicitly permit HTTP only on a trusted isolated network; use HTTPS in production. | false |
backup.s3.bucket |
Existing private bucket. | "" |
backup.s3.prefix |
Backup object prefix, without leading or trailing slash. | "nextcloud" |
backup.s3.region |
AWS signing region. | "us-east-1" |
backup.s3.existingSecret |
Secret containing S3 credentials. | "" |
backup.s3.existingSecretAccessKeyKey |
Access key ID key. | "access-key" |
backup.s3.existingSecretSecretKeyKey |
Secret access key key. | "secret-key" |
restore.enabled |
Keep application stopped and create a restore Job. Disable after successful restore. | false |
restore.backupPath |
Exact S3 prefix for a completed backup, including its unique backup ID. | "" |