This document describes how secrets are managed in the Nextcloud platform.
The platform uses a no secrets in Git approach:
- Secrets are never committed to the repository
.envfiles are in.gitignore- Multiple methods are supported:
- Script with .env file (easiest)
- External Secrets Operator (for production)
- Manual kubectl commands
cd nextcloud-platform/scripts
# Copy the template
cp env.example .env
# Edit with your credentials
nano .env
# Create secrets for MariaDB tenant
./create-tenant-secret.sh my-tenant --mariadb
# Or for PostgreSQL tenant (includes Redis)
./create-tenant-secret.sh my-tenant --postgres
# Or auto-generate all passwords
./create-tenant-secret.sh my-tenant --postgres --generate-passwords| Secret Key | Description | Example |
|---|---|---|
nextcloud-username |
Admin username | admin |
nextcloud-password |
Admin password | (generated) |
s3-access-key |
S3/Ceph access key | AKIAIOSFODNN7EXAMPLE |
s3-secret-key |
S3/Ceph secret key | wJalrXUtnFEMI/... |
mariadb-root-password |
MariaDB root password | (generated) |
mariadb-password |
MariaDB user password | (generated) |
nextcloud-secret |
Encryption secret | (generated 64-char) |
| Secret Key | Description | Example |
|---|---|---|
nextcloud-username |
Admin username | admin |
nextcloud-password |
Admin password | (generated) |
s3-access-key |
S3/Ceph access key | AKIAIOSFODNN7EXAMPLE |
s3-secret-key |
S3/Ceph secret key | wJalrXUtnFEMI/... |
postgres-password |
PostgreSQL admin password | (generated) |
db-username |
PostgreSQL user | nextcloud |
db-password |
PostgreSQL user password | (generated) |
redis-password |
Redis password | (generated) |
nextcloud-secret |
Encryption secret | (generated 64-char) |
- Install External Secrets Operator in your cluster
- Configure a ClusterSecretStore (e.g., Vault, AWS Secrets Manager)
- Store secrets in your backend
Store secrets in Vault:
# For each tenant
vault kv put secret/nextcloud/prod/canary \
admin-password="$(openssl rand -base64 24)" \
s3-access-key="AKIAIOSFODNN7EXAMPLE" \
s3-secret-key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
db-password="your-db-password" \
redis-password="" \
nextcloud-secret="$(openssl rand -base64 48)"# Create secret
aws secretsmanager create-secret \
--name nextcloud/prod/canary \
--secret-string '{
"admin-password": "generated-password",
"s3-access-key": "AKIAIOSFODNN7EXAMPLE",
"s3-secret-key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"db-password": "your-db-password",
"redis-password": "",
"nextcloud-secret": "64-char-random-string"
}'# Check if secret is synced
kubectl get externalsecret -n nc-canary nextcloud-secrets
# Check secret content (base64 encoded)
kubectl get secret -n nc-canary nextcloud-secrets -o yamlIf ESO is not available or not ready, a Kubernetes Job generates secrets.
- Job runs as Argo CD PreSync hook
- Checks if secret already exists
- If not, generates secure random values
- Creates Kubernetes Secret
- S3 and DB credentials are placeholders - you MUST update them manually
- Generated admin password is printed in Job logs (save it!)
- Secrets labeled with
nextcloud.platform/generated=true
After Job runs, update the placeholders:
TENANT=canary
# Edit secret
kubectl edit secret nextcloud-secrets -n nc-$TENANT
# Or patch specific values
kubectl patch secret nextcloud-secrets -n nc-$TENANT \
--type='json' \
-p='[
{"op": "replace", "path": "/data/s3-access-key", "value": "'$(echo -n "REAL_KEY" | base64)'"},
{"op": "replace", "path": "/data/s3-secret-key", "value": "'$(echo -n "REAL_SECRET" | base64)'"},
{"op": "replace", "path": "/data/db-password", "value": "'$(echo -n "REAL_PASSWORD" | base64)'"}
]'# From Job logs
kubectl logs -n nc-$TENANT job/nextcloud-secret-generator
# Or from secret
kubectl get secret nextcloud-secrets -n nc-$TENANT \
-o jsonpath='{.data.nextcloud-password}' | base64 -dRegularly backup secrets (not to Git!):
# Export all tenant secrets
for ns in $(kubectl get ns -l app.kubernetes.io/part-of=nextcloud-platform -o name | cut -d/ -f2); do
kubectl get secret -n $ns nextcloud-secrets -o yaml > "backup/secrets-${ns}.yaml"
done
# Encrypt backups
tar czf secrets-backup.tar.gz backup/
gpg --symmetric --cipher-algo AES256 secrets-backup.tar.gz
rm -rf backup/ secrets-backup.tar.gz
# Store encrypted backup securely (NOT in Git!)# Decrypt
gpg --decrypt secrets-backup.tar.gz.gpg > secrets-backup.tar.gz
tar xzf secrets-backup.tar.gz
# Restore
kubectl apply -f backup/secrets-nc-canary.yamlTENANT=canary
NEW_PASSWORD=$(openssl rand -base64 24)
# Update in Vault (if using ESO)
vault kv patch secret/nextcloud/prod/$TENANT admin-password="$NEW_PASSWORD"
# Or update Kubernetes secret directly
kubectl patch secret nextcloud-secrets -n nc-$TENANT \
--type='json' \
-p='[{"op": "replace", "path": "/data/nextcloud-password", "value": "'$(echo -n "$NEW_PASSWORD" | base64)'"}]'
# Restart Nextcloud to pick up new password
kubectl rollout restart deployment -n nc-$TENANT nextcloud- Create new S3 credentials in Ceph RGW
- Update secret in Vault/K8s
- Restart Nextcloud
- Verify S3 access works
- Revoke old credentials
# Update and restart
kubectl patch secret nextcloud-secrets -n nc-$TENANT ...
kubectl rollout restart deployment -n nc-$TENANT nextcloud
# Verify
kubectl exec -it -n nc-$TENANT deploy/nextcloud -- php occ files:scan --dry-run admin-
Never commit secrets to Git
- Use
.gitignoreto exclude secret files - Run
gitleaksin CI
- Use
-
Use short-lived credentials when possible
- Enable automatic rotation in Vault
- Use IAM roles for S3 if on AWS
-
Principle of least privilege
- S3 keys should only access their bucket
- DB users should only access their database
-
Audit secret access
- Enable audit logging in Vault
- Monitor secret access patterns
-
Encrypt backups
- Always encrypt secret backups
- Use strong encryption (AES-256)
- Store encryption keys separately
# Check ESO status
kubectl get externalsecret -n nc-$TENANT nextcloud-secrets -o yaml
# Check ESO logs
kubectl logs -n external-secrets deploy/external-secrets
# Check ClusterSecretStore
kubectl get clustersecretstore -o yaml# Check job status
kubectl get job -n nc-$TENANT nextcloud-secret-generator
# Check logs
kubectl logs -n nc-$TENANT job/nextcloud-secret-generator
# Common issues:
# - ServiceAccount missing permissions
# - Secret already exists with different owner# Check if pods picked up new secret
kubectl rollout status deployment -n nc-$TENANT nextcloud
# Force restart if needed
kubectl rollout restart deployment -n nc-$TENANT nextcloud
# Check environment variables in pod
kubectl exec -it -n nc-$TENANT deploy/nextcloud -- env | grep -i password