Skip to main content

Nextcloud

A self-hosted content collaboration platform for file sync, sharing, and productivity.

Nextcloud is the most popular open-source file hosting and collaboration platform. It provides file sync and share, calendar, contacts, document editing (via Collabora Online), and hundreds of apps through its app store. Self-hosting keeps all files and productivity data on your own infrastructure, avoiding per-user cloud storage fees.

Alternatives considered​

Cloud Hosted

ToolOpen SourceFree TierMonthly Cost
Google WorkspaceNoLimitedFrom $7/user
DropboxNoLimitedFrom $9.99/mo
Microsoft 365NoNoFrom $6/user

Self Hosted

ToolOpen SourceFull FeaturesNotes
SeafileYesYesFast file sync, lighter than Nextcloud

Installation​

Architecture​

  • HelmRelease: Nextcloud PHP-FPM application; collabora Helm chart for Collabora Online document editing
  • StatefulSet: Valkey (valkey/valkey, digest-pinned via Renovate) for caching and session management
  • Database: CNPG PostgreSQL cluster with Longhorn-encrypted PVCs
  • Networking: HTTPRoute via public gateway

Security​

  • Valkey: capabilities dropped (Nextcloud container securityContext commented out in chart values)
  • Longhorn PVCs encrypted at rest via SOPS-managed keys

Updates​

Managed by Renovate. Valkey image is digest-pinned.

Data Management​

  • Database: CNPG PostgreSQL cluster (Longhorn-encrypted PVCs)
  • PVCs: Longhorn-encrypted PVCs for Nextcloud data (via HelmRelease)
  • Backups: k8up Schedules to Hetzner and Backblaze via restic, covering the Nextcloud data PVC and the database dump from the backups-cnpg PreBackupPod.

User Management​

OIDC present in SOPS-encrypted secret (Nextcloud Social Login app). Users can authenticate via the cluster's OIDC provider. Internal user management also available via admin panel.

Configuration Management​

  • PHP limits (PHP_MEMORY_LIMIT, PHP_UPLOAD_LIMIT), OVERWRITEPROTOCOL from ConfigMap
  • OIDC and admin credentials from SOPS-encrypted secret
  • Database credentials injected from CNPG-generated secret

Administration​

Manual upgrade​

During upgrades with Helm it may happen that some update procedures get stuck or are not automatically executed. Nextcloud will be put into 'maintenance mode'. Manual intervention is required to finish the upgrade procedure:

After exec into the container the following commands are used

php occ upgrade

After successful execution, maintenance mode can be switched off:

php occ maintenance:mode --off

From outside of the pod the following command can be used

kubectl exec -it NEXTCLOUD_POD -n nextcloud -- php occ maintenance:mode --off

Usage​

Sync files from desktop or mobile clients. Share files and folders with internal users or external links. Use Collabora Online (accessible within Nextcloud) to view and edit office documents in the browser. Calendar and contacts sync with CalDAV/CardDAV clients.

Cluster-specific deviations from the above live in the per-cluster README — see k8s/apps/nextcloud/talos/README.md.

Cluster Deployment​

Nextcloud — Talos cluster

Deviations from defaults​

  • Startup probe with a 30-minute budget. The image upgrades in place during container start: it rsyncs the new code, version.php included, then runs occ upgrade, and only then starts Apache. The chart's liveness probe begins after 10 seconds and killed the container about 40 seconds in, in the middle of the migration. Because version.php was already the new one, the next start found nothing to upgrade and the instance stayed in maintenance mode with needsDbUpgrade, every release. The startup probe holds liveness and readiness back until status.php answers.
  • before-starting hook as the safety net. The entrypoint runs it on every start, after its own version check. It finishes an upgrade that was interrupted anyway (drain, OOM, eviction), leaves maintenance mode, and applies db:add-missing-indices, -columns and -primary-keys. A failing occ upgrade fails the start on purpose, so the reason shows up as a crashloop with a log line.
  • No CPU limit, 1536Mi memory. Throttled to 500m the rsync and migration took long enough to meet the probe, and PHP_MEMORY_LIMIT=512M next to Apache does not fit into 768Mi.
  • Helm never rolls an upgrade back (upgrade.remediation.retries: 0, timeout 35m, above the probe budget). After a failed upgrade the volume holds new code and a half-migrated schema, and the old image refuses to start on data newer than itself; the release stays failed and is retried forward.
Kubernetes Metadata
  • HelmRelease: collabora-online@1.3.6
  • HelmRelease: nextcloud@9.4.0
  • HelmRepo: collabora (oci://ghcr.io/collaboraonline/charts/collabora-online)
  • HelmRepo: nextcloud (oci://ghcr.io/nextcloud/helm/nextcloud)
  • Image: valkey/valkey:9.1.2-alpine@sha256:48332870af354a799964c0012ae1194a0bf2bf894eb508f945810596dc2d8d11
Rendered manifests (kustomize build)
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
name: valkey
namespace: nextcloud
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: valkey
serviceName: valkey
template:
metadata:
labels:
app.kubernetes.io/name: valkey
spec:
containers:
- args:
- valkey-server
image: valkey/valkey:9.1.2-alpine@sha256:48332870af354a799964c0012ae1194a0bf2bf894eb508f945810596dc2d8d11
livenessProbe:
initialDelaySeconds: 10
periodSeconds: 10
tcpSocket:
port: 6379
name: valkey
ports:
- containerPort: 6379
name: client
readinessProbe:
initialDelaySeconds: 3
periodSeconds: 5
tcpSocket:
port: 6379
resources:
limits:
memory: 512Mi
requests:
cpu: 50m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
volumeMounts:
- mountPath: /conf
name: conf
- mountPath: /data
name: data
priorityClassName: tier-1
securityContext:
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 999
seccompProfile:
type: RuntimeDefault
volumes:
- emptyDir: {}
name: conf
- emptyDir: {}
name: data