diff --git a/apps/nextcloud/README.md b/apps/nextcloud/README.md new file mode 100644 index 0000000..d6927da --- /dev/null +++ b/apps/nextcloud/README.md @@ -0,0 +1,31 @@ +# Nextcloud + +The `nextcloud` Argo Application adopts the existing Helm release `nextcloud` +in namespace `nextcloud`, using the upstream chart version selected in +`bootstrap/nextcloud.yaml`. Its Bitnami PostgreSQL subchart is already enabled: +the live application uses `nextcloud-postgresql` in this namespace, not the +shared `default/postgresql`. No database copy or restore was needed for this +migration. The same PostgreSQL StatefulSet and PVC remain in use. + +The public values file references existing namespace Secrets for the +administrator, database, Redis, Collabora, metrics, and S3 credentials. The +S3 Secret `nextcloud-objectstore` was created once from the former private +values; none of those values is stored in Git. Recreate these Secrets from the +encrypted administrator backup before restoring this application in a new +cluster. The upstream chart does not render Secrets when these references are +enabled. Retain the old Helm release metadata as historical rollback data. + +The Ceph `nextcloud` object user is adopted unchanged. Its existing bucket ID +is `e326b22c-970e-4ff6-bf65-499f3bab92bf.900401.10`; do not recreate, +rename, or delete the bucket. Before first sync it contained 546,072 objects. +The pre-adoption PostgreSQL dump is outside Git at +`/home/feedc0de/migration-backups/nextcloud/pre-argocd-2026-10-20.dump` on +the administrator host. These local files are not a substitute for off-site +backups. + +Automated pruning is disabled because this release owns data-bearing PVCs and +an object-store user. Nextcloud's local install script must not be run against +an Argo-owned resource. Separate companion Deployments for whiteboard, Talk +HPB, and ClamAV and the notify-push Service/Ingress are being migrated as +their own deployment contracts; do not delete them merely because they are +absent from the upstream Nextcloud chart. diff --git a/apps/nextcloud/resources/ceph-object-user.yaml b/apps/nextcloud/resources/ceph-object-user.yaml new file mode 100644 index 0000000..d6abd2b --- /dev/null +++ b/apps/nextcloud/resources/ceph-object-user.yaml @@ -0,0 +1,7 @@ +apiVersion: ceph.rook.io/v1 +kind: CephObjectStoreUser +metadata: + name: nextcloud + namespace: rook-ceph +spec: + store: my-store diff --git a/apps/nextcloud/resources/middleware.yaml b/apps/nextcloud/resources/middleware.yaml new file mode 100644 index 0000000..db3c37c --- /dev/null +++ b/apps/nextcloud/resources/middleware.yaml @@ -0,0 +1,20 @@ +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: nextcloud-security-headers + namespace: nextcloud +spec: + headers: + stsSeconds: 31536000 + stsIncludeSubdomains: false + stsPreload: false +--- +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: strip-push-prefix + namespace: nextcloud +spec: + stripPrefix: + prefixes: + - /push diff --git a/apps/nextcloud/resources/service-monitor.yaml b/apps/nextcloud/resources/service-monitor.yaml new file mode 100644 index 0000000..25c648c --- /dev/null +++ b/apps/nextcloud/resources/service-monitor.yaml @@ -0,0 +1,22 @@ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: nextcloud + namespace: nextcloud + labels: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud +spec: + namespaceSelector: + matchNames: + - nextcloud + selector: + matchLabels: + app.kubernetes.io/name: nextcloud + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/component: metrics + endpoints: + - port: http + path: /metrics + interval: 60s + scrapeTimeout: 30s diff --git a/apps/nextcloud/values.yaml b/apps/nextcloud/values.yaml new file mode 100644 index 0000000..1c4bab1 --- /dev/null +++ b/apps/nextcloud/values.yaml @@ -0,0 +1,252 @@ +ingress: + enabled: true + className: traefik + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.middlewares: nextcloud-nextcloud-security-headers@kubernetescrd + hosts: + - cloud.brunner.ninja +nextcloud: + host: cloud.brunner.ninja + trustedDomains: + - cloud.brunner.ninja + - nextcloud.nextcloud.svc.cluster.local + mail: + enabled: true + fromAddress: cloud + domain: brunner.ninja + smtp: + host: smtp-relay.gmail.com + secure: '' + port: 587 + authtype: NONE + name: '' + password: '' + objectStore: + s3: + enabled: true + host: rook-ceph-rgw-my-store.rook-ceph.svc.cluster.local + ssl: false + port: 80 + bucket: nextcloud + usePathStyle: true + storageClass: STANDARD_IA + existingSecret: nextcloud-objectstore + secretKeys: + accessKey: access-key + secretKey: secret-key + defaultConfigs: + imaginary.config.php: true + phpConfigs: + zzz-opcache-memory.ini: |- + opcache.memory_consumption=256 + opcache.interned_strings_buffer=32 + configs: + operations.config.php: |- + array ( + '127.0.0.1', + '::1', + '10.0.0.0/24', + '10.0.1.0/24', + '10.0.2.0/24', + '10.0.3.0/24', + '10.0.4.0/24', + '10.0.5.0/24', + '10.0.6.0/24', + ), + 'forwarded_for_headers' => array ( + 'HTTP_X_FORWARDED_FOR', + ), + 'overwrite.cli.url' => 'https://cloud.brunner.ninja', + 'default_phone_region' => 'AT', + // Nextcloud 34 interprets this as the start of a fixed six-hour UTC window. + // 23:00 UTC is 01:00 local time while daylight saving time is active. + 'maintenance_window_start' => 23, + 'loglevel' => 2, + ); + previews.config.php: |- + true, + 'enabledPreviewProviders' => array ( + 'OC\Preview\Movie', + 'OC\Preview\PNG', + 'OC\Preview\JPEG', + 'OC\Preview\GIF', + 'OC\Preview\BMP', + 'OC\Preview\XBitmap', + 'OC\Preview\MP3', + 'OC\Preview\MP4', + 'OC\Preview\TXT', + 'OC\Preview\MarkDown', + 'OC\Preview\PDF' + ), + ); + extraSidecarContainers: + - name: notify-push + image: ghcr.io/nextcloud-releases/aio-notify-push:20260825_084538 + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -ec + args: + - |- + case "$(uname -m)" in + x86_64) notify_arch=x86_64 ;; + aarch64|arm64) notify_arch=aarch64 ;; + *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;; + esac + notify_binary="/var/www/html/custom_apps/notify_push/bin/${notify_arch}/notify_push" + until [ -x "$notify_binary" ]; do sleep 5; done + export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@nextcloud-postgresql:5432/nextcloud" + export REDIS_URL="redis://:${REDIS_PASSWORD}@nextcloud-redis-master:6379" + exec "$notify_binary" --port 7867 + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: nextcloud-db + key: db-username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud-db + key: db-password + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud-redis + key: redis-password + - name: DATABASE_PREFIX + value: oc_ + - name: NEXTCLOUD_URL + value: http://127.0.0.1 + - name: USER_AGENT + value: nextcloud-notify-push-kubernetes + - name: METRICS_PORT + value: '7868' + ports: + - name: notify-push + containerPort: 7867 + - name: push-metrics + containerPort: 7868 + livenessProbe: + tcpSocket: + port: notify-push + initialDelaySeconds: 300 + periodSeconds: 30 + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 250m + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 33 + runAsGroup: 33 + volumeMounts: + - name: nextcloud-main + mountPath: /var/www/html/custom_apps + subPath: custom_apps + readOnly: true + existingSecret: + enabled: true + secretName: nextcloud + tokenKey: nextcloud-token +phpClientHttpsFix: + enabled: true + protocol: https +internalDatabase: + enabled: false +externalDatabase: + enabled: true + type: postgresql + user: nextcloud + database: nextcloud + existingSecret: + enabled: true + secretName: nextcloud-db +mariadb: + enabled: false +postgresql: + enabled: true + global: + postgresql: + auth: + username: nextcloud + database: nextcloud + existingSecret: nextcloud-postgresql + secretKeys: + adminPasswordKey: postgres-password + userPasswordKey: password + primary: + persistence: + enabled: true + storageClass: rook-ceph-block + resources: + requests: + memory: 1Gi + cpu: 250m + limits: + memory: 4Gi + cpu: 1 +redis: + enabled: true + auth: + enabled: true + existingSecret: nextcloud-redis + existingSecretPasswordKey: redis-password + global: + storageClass: rook-ceph-block +startupProbe: + enabled: true +collabora: + enabled: true + image: + tag: 25.04.10.3.1 + collabora: + aliasgroups: + - host: https://cloud.brunner.ninja:443 + extra_params: --o:ssl.enable=false --o:ssl.termination=true + server_name: office.brunner.ninja + username: admin + existingSecret: + enabled: true + secretName: nextcloud-collabora + ingress: + enabled: true + className: traefik + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + hosts: + - host: office.brunner.ninja + paths: + - path: / + pathType: ImplementationSpecific +cronjob: + enabled: true +persistence: + enabled: true + storageClass: rook-ceph-block +imaginary: + enabled: true +metrics: + enabled: true + timeout: 25s + info: + apps: true + update: true +prometheus: + serviceMonitor: + enabled: false + rules: + enabled: true diff --git a/bootstrap/kustomization.yaml b/bootstrap/kustomization.yaml index de92a24..1e427a7 100644 --- a/bootstrap/kustomization.yaml +++ b/bootstrap/kustomization.yaml @@ -70,3 +70,5 @@ resources: - mumble.yaml - quay-project.yaml - quay.yaml +- nextcloud-project.yaml +- nextcloud.yaml diff --git a/bootstrap/nextcloud-project.yaml b/bootstrap/nextcloud-project.yaml new file mode 100644 index 0000000..9fe9f2c --- /dev/null +++ b/bootstrap/nextcloud-project.yaml @@ -0,0 +1,44 @@ +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: nextcloud + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "-1" +spec: + description: Nextcloud and its existing Rook object-store user + sourceRepos: + - https://nextcloud.github.io/helm/ + - https://code.brunner.ninja/feedc0de/homelab-gitops.git + destinations: + - namespace: nextcloud + server: https://kubernetes.default.svc + - namespace: rook-ceph + server: https://kubernetes.default.svc + namespaceResourceWhitelist: + - group: "" + kind: ConfigMap + - group: "" + kind: PersistentVolumeClaim + - group: "" + kind: Service + - group: "" + kind: ServiceAccount + - group: apps + kind: Deployment + - group: apps + kind: StatefulSet + - group: networking.k8s.io + kind: Ingress + - group: networking.k8s.io + kind: NetworkPolicy + - group: policy + kind: PodDisruptionBudget + - group: monitoring.coreos.com + kind: PrometheusRule + - group: monitoring.coreos.com + kind: ServiceMonitor + - group: traefik.io + kind: Middleware + - group: ceph.rook.io + kind: CephObjectStoreUser diff --git a/bootstrap/nextcloud.yaml b/bootstrap/nextcloud.yaml new file mode 100644 index 0000000..c437f36 --- /dev/null +++ b/bootstrap/nextcloud.yaml @@ -0,0 +1,29 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: nextcloud + namespace: argocd +spec: + project: nextcloud + sources: + - repoURL: https://nextcloud.github.io/helm/ + chart: nextcloud + targetRevision: 9.4.1 + helm: + releaseName: nextcloud + valueFiles: + - $values/apps/nextcloud/values.yaml + - repoURL: https://code.brunner.ninja/feedc0de/homelab-gitops.git + targetRevision: main + ref: values + - repoURL: https://code.brunner.ninja/feedc0de/homelab-gitops.git + targetRevision: main + path: apps/nextcloud/resources + destination: + server: https://kubernetes.default.svc + namespace: nextcloud + syncPolicy: + automated: + enabled: true + prune: false + selfHeal: true