CRD Mental Model¶
The operator ships eight CRDs and it's not obvious which to use when. This page is the decision tree.
TL;DR¶
| You want to... | Use this |
|---|---|
| Run one Nextcloud, simple case | NextcloudInstance |
| Onboard many tenants quickly | NextcloudPool + Nextcloud |
| Share config defaults across instances | NextcloudProfile |
| Pin Nextcloud version → chart version mapping | NextcloudVersionMap |
Run occ commands declaratively |
NextcloudCommand |
| Register a Talk HPB backend | SignalingServer |
| Register a Talk recording backend | RecordingServer |
The four core CRDs¶
These interact and form a cascade. The other four (NextcloudVersionMap, NextcloudCommand, SignalingServer, RecordingServer) are independent add-ons.
flowchart LR
Profile[NextcloudProfile<br/>defaults]
Pool[NextcloudPool<br/>template]
NC[Nextcloud<br/>tenant-facing]
NCI[NextcloudInstance<br/>runs Nextcloud]
Profile --> Pool
Profile --> NCI
Pool --> NCI
NC --> NCI
classDef core fill:#6366f1,stroke:#4f46e5,color:#fff
class NCI core
Only NextcloudInstance represents a running Nextcloud. Everything else exists to produce or configure one.
Decision tree¶
flowchart TD
Start([I want to run Nextcloud on Kubernetes]) --> Q1{How many<br/>Nextclouds?}
Q1 -->|One, and I<br/>won't change<br/>profiles| Simple[Create a<br/>NextcloudInstance<br/>directly]
Q1 -->|One or two,<br/>but I want<br/>reusable config| Q2{Is the config<br/>the same<br/>profile everywhere?}
Q1 -->|Many tenants,<br/>on demand| Q3{Do I need<br/>fast onboarding<br/>under 30s?}
Q2 -->|Yes| Profile[Create a NextcloudProfile,<br/>reference it from<br/>NextcloudInstance.spec.profile]
Q2 -->|No, each differs| Simple
Q3 -->|Yes| Pool[Create NextcloudPool +<br/>Nextcloud per tenant]
Q3 -->|Slower is fine| FreshNC[Create Nextcloud per tenant<br/>without poolSelector —<br/>operator creates fresh<br/>NextcloudInstance each time]
style Simple fill:#16a34a,stroke:#15803d,color:#fff
style Profile fill:#16a34a,stroke:#15803d,color:#fff
style Pool fill:#16a34a,stroke:#15803d,color:#fff
style FreshNC fill:#16a34a,stroke:#15803d,color:#fff
"I want one Nextcloud"¶
Use NextcloudInstance directly. It's the simplest path. You write one YAML file, kubectl apply it, and the operator creates the Helm release.
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudInstance
metadata:
name: company
namespace: nextcloud
spec:
profile: production
ingress:
host: cloud.company.com
database:
managed: true
type: postgresql
admin:
username: admin
password: from-secret-manager
"I want reusable config across several Nextclouds"¶
Create a NextcloudProfile (cluster-scoped), then reference it by name from each NextcloudInstance.spec.profile. Profiles are the right answer to "my three test environments share 90% of their config."
Built-in profiles (production, testing, development) already exist — create custom ones only when the built-ins don't fit.
"I want to onboard tenants quickly"¶
Create a NextcloudPool (cluster-scoped) that pre-provisions N unassigned NextcloudInstance resources. Then tenants create a Nextcloud (namespaced, in their own namespace) with a poolSelector. The operator assigns a pool instance in ~30s.
If spec.poolSelector is empty, the operator creates a fresh instance (~2min). You can mix: some tenants pool-backed, others fresh.
"I want to run an occ command"¶
Create a NextcloudCommand targeting an existing instance. The operator runs the commands inside a Nextcloud pod via K8s exec, captures stdout/stderr/exit codes, and writes the results to status. Much better than kubectl exec for auditable, declarative maintenance.
See Running occ Commands.
The 4-layer configuration cascade¶
When the operator builds Helm values for a NextcloudInstance, it applies four layers in order, last wins:
flowchart LR
B[1. Built-in profile<br/>production / testing / development]
C[2. Custom NextcloudProfile CRD<br/>if spec.profile references one]
S[3. Instance spec fields<br/>database, redis, s3, mail, admin, ...]
H[4. spec.helm.values<br/>raw chart overrides]
B --> C --> S --> H
style H fill:#6366f1,stroke:#4f46e5,color:#fff
| Layer | Source | Use when |
|---|---|---|
| 1 | Built-in profile | Always active — gives sensible defaults |
| 2 | NextcloudProfile CRD |
You want org-wide defaults that differ from built-in |
| 3 | NextcloudInstance.spec |
Per-instance config that the operator knows about |
| 4 | spec.helm.values |
Raw Helm values that bypass the operator's schema — escape hatch |
Use layer 4 sparingly. Anything you put there isn't validated by the operator and bypasses its invariants (e.g. secret name conventions).
When pool-based, the effective order becomes:
Built-in profile → NextcloudProfile → NextcloudPool.spec.template → Nextcloud.spec → NextcloudInstance.spec → spec.helm.values
When each CRD is not the right answer¶
| CRD | Don't use it for |
|---|---|
NextcloudInstance |
Multi-tenant onboarding (no indirection, no pre-provisioning) |
Nextcloud |
Running the operator without Flux — the instance it assigns needs Flux to install the chart |
NextcloudPool |
High availability of a single tenant (use spec.replicas on an instance instead) |
NextcloudProfile |
Per-tenant overrides — profiles are shared defaults, not per-instance config |
NextcloudVersionMap |
Overriding a single instance's version — use spec.version or spec.helm.version |
NextcloudCommand |
Scheduled periodic maintenance (use spec.maintenance on the instance) |
SignalingServer / RecordingServer |
Anything other than Talk HPB backend registration |
Scope: cluster vs. namespaced¶
| CRD | Scope | Why |
|---|---|---|
NextcloudInstance |
Namespaced | Each instance runs in one namespace |
Nextcloud |
Namespaced | Lives in the tenant's namespace |
NextcloudPool |
Cluster | Pools span instance namespaces |
NextcloudProfile |
Cluster | Shared defaults across the cluster |
NextcloudVersionMap |
Cluster | One map per cluster; the operator uses the one named default |
NextcloudCommand |
Namespaced | Targets an instance in the same namespace |
SignalingServer |
Cluster | Backends serve all tenants |
RecordingServer |
Cluster | Backends serve all tenants |
See also¶
- Architecture — How the operator translates these CRDs into Kubernetes primitives
- Pool Design — Deep dive on pools and assignment
- Configuration Profiles — Writing and using profiles
- API Reference — Full field reference for every CRD