Skip to content

Pool Provisioning

Overview

The NextcloudPool CRD enables fast tenant onboarding by maintaining a pool of pre-provisioned, unassigned NextcloudInstances. When a tenant creates a Nextcloud resource, the operator assigns an existing instance from the pool (~30s) instead of creating one from scratch (~2min).

Architecture

┌────────────────────────────────────────────────────────────┐
│ Cluster-Scoped                                             │
│                                                            │
│  ┌──────────────────────┐  ┌──────────────────────┐       │
│  │ NextcloudPool        │  │ NextcloudPool        │       │
│  │ production           │  │ development          │       │
│  │                      │  │                      │       │
│  │ spec:                │  │ spec:                │       │
│  │   replicas: 5        │  │   replicas: 2        │       │
│  │   profile: prod      │  │   profile: dev       │       │
│  │                      │  │                      │       │
│  │ status:              │  │ status:              │       │
│  │   ready: 5           │  │   ready: 2           │       │
│  │   assigned: 0        │  │   assigned: 0        │       │
│  └──────────────────────┘  └──────────────────────┘       │
└────────────────────────────────────────────────────────────┘
         │                            │
         │ manages                    │ manages
         ▼                            ▼
┌────────────────────────────────────────────────────────────┐
│ Instance Namespaces                                        │
│                                                            │
│  Pool instances (unassigned):                              │
│  ┌──────────────────────┐  ┌──────────────────────┐       │
│  │ NextcloudInstance    │  │ NextcloudInstance    │       │
│  │ nc-brave-lake-g7h8i9 │  │ nc-quiet-hill-j0k1l2│       │
│  │ labels:              │  │ labels:              │       │
│  │   assigned: "false"  │  │   assigned: "false"  │       │
│  └──────────────────────┘  └──────────────────────┘       │
│                                                            │
│  Assigned instances:                                       │
│  ┌──────────────────────┐                                  │
│  │ NextcloudInstance    │                                  │
│  │ nc-happy-sun-a1b2c3  │                                  │
│  │ labels:              │                                  │
│  │   assigned: "true"   │                                  │
│  │   tenant: acme-corp  │                                  │
│  └──────────────────────┘                                  │
└────────────────────────────────────────────────────────────┘

How It Works

Pool-Based Assignment Flow

1. Tenant creates Nextcloud CR
   └─> kubectl apply -f nextcloud.yaml

2. Operator checks poolSelector
   └─> spec.poolSelector.matchLabels: {pool: "production"}

3. Operator finds matching unassigned instance
   └─> Labels: {assigned: "false", pool: "production"}
   └─> Found: nc-brave-lake-g7h8i9

4. Operator assigns instance
   ├─> Updates NextcloudInstance labels (assigned: "true")
   ├─> Copies Nextcloud.spec to NextcloudInstance.spec
   └─> Updates Nextcloud.status.instanceRef

5. NextcloudInstance reconciles with new spec
   └─> Updates HelmRelease, Secrets, etc.

6. Pool operator detects assignment
   └─> Creates new unassigned instance to maintain pool size

Fresh Instance Creation (No Pool)

If no poolSelector is specified or no matching instances are available:

1. Operator creates fresh NextcloudInstance
2. Copies entire spec from Nextcloud
3. NextcloudInstance creates all resources (~2min)

Creating a Pool

apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudPool
metadata:
  name: production
spec:
  replicas: 5
  instanceNamespacePattern: "*"
  template:
    metadata:
      labels:
        pool: production
    spec:
      profile: production
      database:
        managed: true
        type: postgresql
  lifecycle:
    recreateOnProfileChange: true
    maxUnassignedAge: "168h"    # 7 days
    reclaimPolicy: Delete

Using a Pool

Create a Nextcloud resource with a poolSelector:

apiVersion: k8s.bnerd.com/v1alpha1
kind: Nextcloud
metadata:
  name: tenant-cloud
  namespace: tenant-acme
spec:
  poolSelector:
    matchLabels:
      pool: production
  ingress:
    host: cloud.acme-corp.example.com
    tls:
      enabled: true
  # Additional spec fields are applied to the assigned instance

Pool Status

kubectl get nextcloudpools

# Detailed pool status
kubectl describe nextcloudpool production

The pool status shows instance counts:

status:
  phase: Ready
  replicas: 5
  ready: 5
  unassigned: 4
  assigned: 1
  instances:
    - name: nc-brave-lake-g7h8i9
      phase: Ready
      assigned: false
    - name: nc-happy-sun-a1b2c3
      phase: Ready
      assigned: true

Status Phases

NextcloudPool: Pending -> Scaling -> Ready | Failed

Nextcloud (logical): Pending -> Assigning -> Configuring -> Ready | Failed

Instance Naming

Pool instances use a human-readable naming convention:

Format: nc-{adjective}-{noun}-{random}

Examples: nc-happy-sun-a1b2c3, nc-calm-moon-d4e5f6, nc-brave-lake-g7h8i9

This provides memorable names for support conversations while avoiding collisions.

Lifecycle Policies

Policy Description
recreateOnProfileChange Recreate unassigned instances when the pool profile changes
maxUnassignedAge Maximum time an instance can stay unassigned before cleanup (e.g., 168h)
reclaimPolicy What happens to the instance when the Nextcloud is deleted: Delete or Retain

Status Synchronization

The operator keeps the Nextcloud and NextcloudInstance status in sync:

Nextcloud.spec ──────────────► NextcloudInstance.spec
                 (propagation)

Nextcloud.status ◄──────────── NextcloudInstance.status
                    (sync back)

Fields synced back: phase, helmRelease, url, version, conditions

Drift Reconciliation

If a NextcloudInstance is modified directly, the operator detects drift during the 30-second timer reconciliation and overwrites the instance spec with the Nextcloud spec. Manual changes to assigned instances are not preserved.