Secret Management¶
Overview¶
The Nextcloud operator supports referencing existing Kubernetes secrets for sensitive credentials instead of embedding them in the Nextcloud CR. This follows security best practices and integrates seamlessly with secret management tools like:
- External Secrets Operator (ESO)
- Sealed Secrets
- HashiCorp Vault
- AWS Secrets Manager / Azure Key Vault / Google Secret Manager
- Manual secret creation
How It Works¶
Priority Order¶
For each credential type, the operator follows this priority:
credentialsSecret— Reference to existing Kubernetes secret (highest priority)- Inline values — Credentials defined directly in the CR
- Defaults — Built-in defaults (admin credentials only)
Benefits¶
- Security: Credentials never stored in CRDs or Git
- Separation of Concerns: Dev team manages apps, ops team manages secrets
- GitOps Friendly: Nextcloud CRs can be in Git without exposing secrets
- Integration: Works with any secret management tool
- Flexibility: Mix and match — use secrets for some, inline for others
Supported Credential Types¶
1. Database Credentials¶
Required secret keys: host, name, user, password
Optional secret keys: port (default: 5432), type (default: postgresql)
2. Redis Credentials¶
Required secret keys: host
Optional secret keys: port (default: 6379), password
3. S3 Credentials¶
Required secret keys: bucket, accessKey, secretKey
Optional secret keys: endpoint (default: s3.amazonaws.com), region (default: us-east-1)
4. Admin Credentials¶
Required secret keys: username, password
Optional secret keys: email
5. Mail/SMTP Credentials¶
Secret keys: fromAddress, domain, smtpHost, smtpPort, smtpSecure, smtpAuthType, smtpName, smtpPassword
6. OIDC Credentials¶
OIDC uses a different pattern — clientSecretRef references a single key in a secret:
spec:
oidc:
enabled: true
providerName: keycloak
clientId: nextcloud
clientSecretRef:
name: oidc-secret
key: client-secret
discoveryUri: https://iam.example.com/realms/office/.well-known/openid-configuration
Examples¶
All Credentials from Secrets¶
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudInstance
metadata:
name: secure-nextcloud
spec:
profile: production
database:
type: postgresql
credentialsSecret: nextcloud-db-creds
redis:
enabled: true
credentialsSecret: nextcloud-redis-creds
s3:
enabled: true
credentialsSecret: nextcloud-s3-creds
admin:
credentialsSecret: nextcloud-admin-creds
Mixed Approach¶
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudInstance
metadata:
name: mixed-nextcloud
spec:
# Database from secret (production, managed by ops)
database:
type: postgresql
credentialsSecret: prod-db-credentials
# Redis inline (development, not sensitive)
redis:
enabled: true
host: redis.default.svc
port: 6379
# Admin from secret
admin:
credentialsSecret: admin-credentials
With External Secrets Operator¶
# External Secret that syncs from Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: nextcloud-db-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: nextcloud-db-credentials
creationPolicy: Owner
dataFrom:
- extract:
key: secret/data/nextcloud/database
---
# Nextcloud references the synced secret
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudInstance
metadata:
name: my-nextcloud
spec:
database:
type: postgresql
credentialsSecret: nextcloud-db-credentials
With Sealed Secrets¶
# Sealed Secret (safe to commit to Git)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: nextcloud-admin-credentials
spec:
encryptedData:
username: AgBvN3RI...
password: AgCd8hP2...
template:
metadata:
name: nextcloud-admin-credentials
---
# Nextcloud references the sealed secret
apiVersion: k8s.bnerd.com/v1alpha1
kind: NextcloudInstance
metadata:
name: my-nextcloud
spec:
admin:
credentialsSecret: nextcloud-admin-credentials
Creating Secrets¶
With kubectl¶
# Database
kubectl create secret generic nextcloud-db-creds \
--from-literal=host=postgres.db.svc \
--from-literal=port=5432 \
--from-literal=name=nextcloud \
--from-literal=user=nextcloud \
--from-literal=password='db-password'
# Redis
kubectl create secret generic nextcloud-redis-creds \
--from-literal=host=redis.cache.svc \
--from-literal=port=6379 \
--from-literal=password='redis-password'
# S3
kubectl create secret generic nextcloud-s3-creds \
--from-literal=bucket=my-bucket \
--from-literal=endpoint=s3.amazonaws.com \
--from-literal=region=us-east-1 \
--from-literal=accessKey='AKIA...' \
--from-literal=secretKey='secret...'
# Admin
kubectl create secret generic nextcloud-admin-creds \
--from-literal=username=admin \
--from-literal=password='admin-password' \
--from-literal=email=admin@example.com
With YAML Manifest¶
apiVersion: v1
kind: Secret
metadata:
name: nextcloud-db-credentials
type: Opaque
stringData:
host: postgres.database.svc.cluster.local
port: "5432"
name: nextcloud
user: nextcloud
password: "super-secret-password"
Warning
Do not commit unencrypted Secret manifests to Git. Use Sealed Secrets, External Secrets Operator, or SOPS instead.
Error Handling¶
Secret Not Found¶
If you specify a credentialsSecret that doesn't exist, the instance will fail:
status:
phase: Failed
conditions:
- type: SecretsReady
status: "False"
reason: "Failed"
message: "Database credentialsSecret 'nonexistent-secret' specified but secret not found"
Missing Required Keys¶
If a secret exists but is missing required keys:
Security Best Practices¶
- Always use
credentialsSecretfor production — never commit inline passwords - Integrate with secret management tools — External Secrets Operator + Vault/AWS/Azure
- Restrict RBAC for secrets — limit who can read secrets
- Rotate credentials regularly — update secrets and trigger reconciliation
- Use separate secrets per environment — production and staging should use different credentials
Troubleshooting¶
# Check secret exists
kubectl get secret nextcloud-db-credentials
# List keys in secret
kubectl get secret nextcloud-db-credentials -o json | jq -r '.data | keys | .[]'
# Check operator logs for credential messages
kubectl logs -n nextcloud-operator-system -l app.kubernetes.io/name=nextcloud-operator | grep -i credential