CRD Overview¶
Custom Resource Definitions¶
The Nextcloud Operator provides the following CRDs:
- Nextcloud (
nc) — Tenant-facing logical resource (namespaced) - NextcloudInstance (
nci) — Physical Nextcloud instance resource (namespaced) - NextcloudPool (
ncp) — Pre-provisioned instance pool management (cluster-scoped) - NextcloudProfile (
ncprofile) — Reusable configuration profiles (cluster-scoped) - NextcloudVersionMap (
ncvm) — Version-to-chart mapping and default images (cluster-scoped) - SignalingServer (
ss) — HPB signaling server backend registration (cluster-scoped) - RecordingServer (
rs) — Talk recording server backend registration (cluster-scoped) - NextcloudCommand (
nccmd) — Ad-hococccommand runner (namespaced). See the Commands guide.
API Version: k8s.bnerd.com/v1alpha1
For the full interactive API specification, see the OpenAPI / Swagger page.
API Resources¶
NextcloudInstance Resource¶
Minimal Example¶
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudInstance
metadata:
name: my-nextcloud
namespace: default
spec:
profile: production
ingress:
host: cloud.example.com
Complete Example¶
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudInstance
metadata:
name: production-nextcloud
namespace: nextcloud
labels:
environment: production
spec:
profile: production
version: "29"
replicas: 3
ingress:
enabled: true
host: cloud.company.com
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
tls:
enabled: true
database:
type: postgresql
managed: true
postgres:
replicas: 3
storage:
size: 50Gi
backup:
enabled: true
s3:
bucket: pg-backups
endpoint: s3.example.com
credentialsSecret: backup-s3-creds
redis:
enabled: true
credentialsSecret: nextcloud-redis-creds
s3:
enabled: true
credentialsSecret: nextcloud-s3-creds
backups:
data:
enabled: true
bucket: nextcloud-backups
endpoint: s3.example.com
region: eu-central-1
credentialsSecret: nextcloud-backup-creds
schedule: "0 3 * * *"
deleteOnCleanup: false
admin:
credentialsSecret: nextcloud-admin-creds
mail:
enabled: true
credentialsSecret: nextcloud-mail-creds
resources:
requests:
cpu: 1000m
memory: 2Gi
limits:
cpu: 4000m
memory: 8Gi
persistence:
enabled: true
size: 100Gi
storageClass: fast-ssd
NextcloudProfile Resource¶
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudProfile
metadata:
name: high-performance
spec:
description: High-performance profile for production workloads
defaults:
version: "stable" # Pins NC version for instances using this profile
replicaCount: 3
resources:
requests:
cpu: 2000m
memory: 4Gi
limits:
cpu: 8000m
memory: 16Gi
persistence:
enabled: true
size: 200Gi
storageClass: fast-ssd
internalDatabase:
enabled: false
helm:
version: "5.0.0" # Helm chart version (escape hatch)
spec.defaults accepts both raw Helm chart values and these instance spec-level fields, which the operator pulls into the instance spec at reconcile time: version, image, database, redis, s3, admin, mail, ingress, oidc, apps, backups, maintenance, persistence, cronjob, livenessProbe, readinessProbe, startupProbe, resources, replicas, php, extraVolumes, extraVolumeMounts, placement. Instance values win on conflict. See the Profiles guide for a worked example.
API Endpoints¶
Nextcloud Endpoints¶
GET /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextclouds
GET /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextclouds/{name}
POST /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextclouds
PUT /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextclouds/{name}
DELETE /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextclouds/{name}
NextcloudInstance Endpoints¶
GET /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudinstances
GET /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudinstances/{name}
POST /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudinstances
PUT /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudinstances/{name}
DELETE /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudinstances/{name}
NextcloudProfile Endpoints (Cluster-Scoped)¶
GET /apis/k8s.bnerd.com/v1alpha1/nextcloudprofiles
GET /apis/k8s.bnerd.com/v1alpha1/nextcloudprofiles/{name}
POST /apis/k8s.bnerd.com/v1alpha1/nextcloudprofiles
PUT /apis/k8s.bnerd.com/v1alpha1/nextcloudprofiles/{name}
DELETE /apis/k8s.bnerd.com/v1alpha1/nextcloudprofiles/{name}
Nextcloud safe-fallback contract: the operator validates every reference on the Nextcloud CR (spec.profile, spec.poolSelector) before doing any work. If a reference can't be honoured (typo'd profile, empty pool), it blocks with an InstanceAssigned=False condition + Warning event and retries every 60 s — never silently spawning a misconfigured instance. See Pool design — Modes, validation, and the safe-fallback contract.
NextcloudPool Endpoints (Cluster-Scoped)¶
GET /apis/k8s.bnerd.com/v1alpha1/nextcloudpools
GET /apis/k8s.bnerd.com/v1alpha1/nextcloudpools/{name}
POST /apis/k8s.bnerd.com/v1alpha1/nextcloudpools
PUT /apis/k8s.bnerd.com/v1alpha1/nextcloudpools/{name}
DELETE /apis/k8s.bnerd.com/v1alpha1/nextcloudpools/{name}
Notable lifecycle/status fields:
| Field | Type | Description |
|---|---|---|
spec.lifecycle.recreateOnProfileChange |
boolean (default false) |
When true, deletes unassigned pool instances when the referenced profile changes — either profileRef.name swap or content edit (detected via SHA256 digest by the reconcile timer). Assigned instances are sticky and never recreated. See Pool design — Profile changes and instance recreation. |
status.observedProfile.{name,digest,observedAt} |
object | Last observed profile snapshot used for content-change detection. Always recorded for observability. |
SignalingServer Endpoints (Cluster-Scoped)¶
GET /apis/k8s.bnerd.com/v1alpha1/signalingservers
GET /apis/k8s.bnerd.com/v1alpha1/signalingservers/{name}
POST /apis/k8s.bnerd.com/v1alpha1/signalingservers
PUT /apis/k8s.bnerd.com/v1alpha1/signalingservers/{name}
DELETE /apis/k8s.bnerd.com/v1alpha1/signalingservers/{name}
RecordingServer Endpoints (Cluster-Scoped)¶
GET /apis/k8s.bnerd.com/v1alpha1/recordingservers
GET /apis/k8s.bnerd.com/v1alpha1/recordingservers/{name}
POST /apis/k8s.bnerd.com/v1alpha1/recordingservers
PUT /apis/k8s.bnerd.com/v1alpha1/recordingservers/{name}
DELETE /apis/k8s.bnerd.com/v1alpha1/recordingservers/{name}
NextcloudCommand Endpoints¶
GET /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudcommands
GET /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudcommands/{name}
POST /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudcommands
DELETE /apis/k8s.bnerd.com/v1alpha1/namespaces/{namespace}/nextcloudcommands/{name}
NextcloudCommand.spec is immutable — PUT/PATCH to spec is rejected by the operator. Re-run by creating a new resource.
spec.lifecycle is OnDemand (default) or OnReady. OnReady waits for the target instance to be Ready (and to have a Running+Ready pod) before running. spec.beforeScript is an optional bash setup script executed via /bin/bash -c before commands; non-zero exit fails the run and skips commands.
Operator-managed lifecycle hooks are declared on NextcloudInstance.spec.hooks (or upstream Nextcloud / Profile / Pool) and materialized as NCCs at three triggers:
onFirstReady— once per instance, on the first-ever Ready.onAssignmentReady— once per assignment event (pool instance bound to a Nextcloud).onEveryReady— every Ready edge (fresh NCC per edge; bounded by NCC TTL).
A hook NCC ending in Failed surfaces as a LifecycleHookFailed=True condition on the NCI; the NCI's own phase stays Ready. See the Commands guide for details.
Using the API¶
With kubectl¶
# Create a Nextcloud instance
kubectl apply -f nextcloudinstance.yaml
# List instances
kubectl get nci -n nextcloud
# Get instance details
kubectl describe nci my-nextcloud -n nextcloud
# Delete instance
kubectl delete nci my-nextcloud -n nextcloud
With Python (Kubernetes Client)¶
from kubernetes import client, config
config.load_kube_config()
api = client.CustomObjectsApi()
# List Nextcloud instances
nextclouds = api.list_namespaced_custom_object(
group="k8s.bnerd.com",
version="v1alpha1",
namespace="default",
plural="nextclouds"
)
# Create instance
body = {
"apiVersion": "k8s.bnerd.com/v1alpha1",
"kind": "Nextcloud",
"metadata": {"name": "my-nextcloud"},
"spec": {
"profile": "production",
"poolSelector": {"matchLabels": {"pool": "default"}}
}
}
api.create_namespaced_custom_object(
group="k8s.bnerd.com",
version="v1alpha1",
namespace="default",
plural="nextclouds",
body=body
)
Field Reference¶
NextcloudInstance Spec Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
profile |
string | - | Profile to use (production, testing, development, or custom) |
version |
string | 29 |
Nextcloud version (Docker tag) |
image |
object | - | Custom container image (overrides version map and profile defaults) |
image.repository |
string | - | Container image repository |
image.tag |
string | - | Container image tag |
image.pullPolicy |
string | - | Pull policy (Always, IfNotPresent, Never) |
image.pullSecrets |
array | - | Image pull secrets for private registries |
replicas |
integer | 1 |
Number of pod replicas |
ingress |
object | - | Ingress configuration |
database |
object | - | Database configuration |
redis |
object | - | Redis configuration |
s3 |
object | - | S3 object storage configuration |
admin |
object | - | Admin credentials |
mail |
object | - | Mail/SMTP configuration |
oidc |
object | - | OIDC/SSO configuration |
resources |
object | - | Resource requests/limits |
persistence |
object | - | Persistent storage configuration |
cronjob |
object | - | Cron job configuration |
livenessProbe |
object | - | Liveness probe overrides |
readinessProbe |
object | - | Readiness probe overrides |
startupProbe |
object | - | Startup probe overrides |
license_key |
string | - | Nextcloud Enterprise license key |
apps |
object | - | Declarative Nextcloud app management |
backups |
object | - | Automated data backup configuration |
php |
object | - | PHP / FPM / Apache MPM / preview tuning. Sane defaults shipped in every built-in profile prevent image-upload from blocking the instance. See the PHP & Preview Tuning guide. |
extraVolumes |
array | - | Extra volumes appended to the Nextcloud pod (chart nextcloud.extraVolumes). |
extraVolumeMounts |
array | - | Extra volume mounts on the Nextcloud container (chart nextcloud.extraVolumeMounts). |
helm |
object | - | Helm chart/values overrides |
placement |
object | - | Pod scheduling constraints for the Nextcloud app and cron pod (nodeSelector, tolerations, affinity, topologySpreadConstraints). See Workload Placement. |
Ingress Configuration¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable ingress. Set to false to disable external access. |
host |
string | - | Hostname for the ingress (e.g., nextcloud.example.com). Required if enabled=true. |
className |
string | nginx |
IngressClass to use (e.g., nginx, traefik) |
annotations |
object | - | Additional annotations for the ingress |
tls.enabled |
boolean | true |
Enable TLS/HTTPS |
tls.secretName |
string | - | Name of TLS secret (defaults to <instance-name>-tls) |
trustedProxies |
array of strings | ["10.0.0.0/8"] |
IP/CIDR ranges of trusted reverse proxies. Auto-sets trusted_proxies in Nextcloud PHP config. |
forwardedForHeaders |
array of strings | ["HTTP_X_FORWARDED_FOR"] |
HTTP headers carrying the real client IP. Auto-sets forwarded_for_headers in Nextcloud PHP config. |
When ingress is enabled, the operator automatically configures overwriteprotocol, overwritehost, trusted_proxies, and forwarded_for_headers in Nextcloud's PHP config. This can be overridden by providing a custom bnerd.config.php via spec.helm.values.nextcloud.configs.
Database Configuration¶
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | postgresql |
Database type (postgresql or mysql) |
managed |
boolean | false |
Create managed PostgreSQL cluster via Percona PG Operator |
deleteOnCleanup |
boolean | false |
Delete the managed PerconaPGCluster when the instance is deleted. Ignored for unmanaged DBs. Note: when the instance owns its namespace, namespace deletion removes the DB regardless — see Deletion & Cleanup |
credentialsSecret |
string | - | Secret name with credentials (overrides inline values) |
host |
string | - | Database host (ignored if credentialsSecret set) |
port |
integer | 5432 |
Database port |
name |
string | nextcloud |
Database name |
user |
string | nextcloud |
Database user |
password |
string | - | Database password |
postgres |
object | - | PostgreSQL cluster config (only when managed: true) |
Data Backup Configuration (spec.backups.data)¶
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable automated S3 data backup |
bucket |
string | - | S3 bucket name for backup storage |
endpoint |
string | - | S3-compatible endpoint URL |
region |
string | - | S3 region |
credentialsSecret |
string | - | Secret with backup credentials (keys: accessKey, secretKey) |
schedule |
string | "0 3 * * *" |
Cron schedule for backup jobs |
deleteOnCleanup |
boolean | false |
Delete backup data when instance is deleted |
Secret References¶
All credential sections support credentialsSecret. See Secret Management for details.
| Section | Secret Keys |
|---|---|
database.credentialsSecret |
host, port, name, user, password |
redis.credentialsSecret |
host, port, password |
s3.credentialsSecret |
bucket, endpoint, region, accessKey, secretKey |
admin.credentialsSecret |
username, password, email |
mail.credentialsSecret |
fromAddress, domain, smtpHost, smtpPort, smtpSecure, smtpAuthType, smtpName, smtpPassword |
App Management (spec.apps)¶
Declarative Nextcloud app management. See below for supported apps.
Well-Known Apps¶
| App key | Configuration | Description |
|---|---|---|
richdocuments |
enabled, wopiUrl, wopiAllowlist |
Nextcloud Office / Collabora |
spreed |
enabled, stunServers, turnServers, signalingServers, recording |
Nextcloud Talk |
calendar |
enabled |
Nextcloud Calendar |
contacts |
enabled |
Nextcloud Contacts |
deck |
enabled |
Nextcloud Deck (Kanban) |
tasks |
enabled |
Nextcloud Tasks |
notes |
enabled |
Nextcloud Notes |
mail |
enabled |
Nextcloud Mail client |
collectives |
enabled |
Nextcloud Collectives (team wikis) |
whiteboard |
enabled |
Nextcloud Whiteboard |
forms |
enabled |
Nextcloud Forms |
admin_audit |
enabled |
Admin Audit Log |
groupfolders |
enabled |
Group Folders |
photos |
enabled |
Nextcloud Photos |
previewgenerator |
enabled |
Preview Generator |
Custom Apps¶
Note
Setting enabled: false disables an app. Removing an app entry entirely does not uninstall it — this prevents accidental data loss.
Status¶
Version Resolution¶
status:
versionResolution:
requestedVersion: "32.0.6"
resolvedVersion: "32.0.6"
chartVersion: "8.9.1"
resolvedBy: builtin
Supported Versions¶
The following Nextcloud versions are shipped in the default NextcloudVersionMap CRD. Use spec.version with any of these values, or set spec.helm.version to override. To add custom versions or override image defaults, edit the NextcloudVersionMap/default resource.
| Nextcloud Version | Helm Chart Version |
|---|---|
| 33.0.3 | 9.0.6 |
| 33.0.2 | 9.0.5 |
| 33.0.0 | 9.0.4 |
| 32.0.9 | 8.9.1 |
| 32.0.8 | 8.9.1 |
| 32.0.6 | 8.9.1 |
| 32.0.5 | 8.9.0 |
| 32.0.3 | 8.8.1 |
| 32.0.2 | 8.6.0 |
| 32.0.1 | 8.5.2 |
| 32.0.0 | 8.5.0 |
| 31.0.9 | 8.0.3 |
| 31.0.8 | 8.0.1 |
| 31.0.7 | 7.0.1 |
| 30.0.10 | 6.6.10 |
| 30.0.6 | 6.6.9 |
| 30.0.5 | 6.6.3 |
| 30.0.4 | 6.6.2 |
| 30.0.3 | 6.3.1 |
| 30.0.2 | 6.3.0 |
| 30.0.1 | 6.2.1 |
| 30.0.0 | 6.1.0 |
| 29.0.6 | 5.5.6 |
| 29.0.5 | 5.5.3 |
| 29.0.4 | 5.5.2 |
| 29.0.3 | 5.2.2 |
| 29.0.2 | 5.0.1 |
| 29.0.1 | 5.0.0 |
| 29.0.0 | 4.6.9 |
| 28.0.4 | 4.6.6 |
| 28.0.3 | 4.6.4 |
| 28.0.2 | 4.6.3 |
| 28.0.1 | 4.5.12 |
| 28.0.0 | 4.5.9 |
Aliases: latest and stable both resolve to 33.0.3.
Prefix resolution: Setting spec.version: "33" resolves to the latest known 33.x.y patch (currently 33.0.3).
Phase Values¶
Pending: kopf has not picked up the CR yetCreating: Operator is provisioning DB / secrets / S3 / HelmRelease, including waiting on managed dependencies (PerconaPGCluster spin-up). Normal database initialization stays in this phase — inspectstatus.databasefor the cause.Deploying: HelmRelease applied; waiting for Flux to roll out the chart and for the workload (Deployment, Endpoints, Ingress) to become readyReady: HelmReleaseReady=Trueand Deployment has all ready replicas and Service has ready endpoints and (if an Ingress exists) the Ingress has a load-balancer addressUpdating: Reserved for future useFailed: An error blocks progress. The operator keeps retrying transient failures (database provisioning timeout, HelmRelease reconcile error) —Failedis informational so monitoring can alert. Configuration errors propagate as kopfPermanentErrorand require manual fix.
phase: Ready is set exclusively by the 30-second status timer after the
layered readiness gate passes. on_create, on_update, and force_reconcile
all leave the instance in Deploying. Inspect status.workload for
per-component check results and status.conditions[type=Ready] for the
machine-readable reason (WaitingForHelmRelease, WaitingForPods,
WaitingForEndpoints, WaitingForIngress, or AllResourcesReady).
status.database is always populated and mirrors the DatabaseReady
condition with drill-down fields (managed, type, clusterName, ready,
reason, message, observedAt).
RBAC¶
To interact with these resources, you need appropriate RBAC permissions:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: nextcloud-manager
rules:
- apiGroups: ["k8s.bnerd.com"]
resources: ["nextclouds", "nextcloudinstances", "nextcloudpools",
"nextcloudprofiles", "signalingservers", "recordingservers"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]