From d1959dd6cfa5e2cf0f81a65fa905110b59d6aea1 Mon Sep 17 00:00:00 2001 From: Felix Wolf Date: Sat, 4 Apr 2026 18:08:13 +0200 Subject: [PATCH] feat: Adds Nextcloud application deployment Deploys Nextcloud using an FPM-alpine image with a Caddy sidecar for web serving. Integrates with an external CloudNativePG cluster for PostgreSQL and a dedicated Valkey instance for caching. Configures S3-compatible object storage for file data. Includes an initialization Job to create essential admin and Valkey secrets. Sets up Ingress for external access with automated TLS provisioning via cert-manager. Configures local-path persistence for Nextcloud's core data to ensure state is maintained across pod restarts. Centralizes hostname configuration and migrates various Nextcloud settings to environment variables for streamlined management. Adds ArgoCD ignore rules for `batch/Job` resource selectors and template labels, preventing spurious out-of-sync states caused by Kubernetes mutations and improving synchronization stability. --- .../_apps/nextcloud/app-data.ytt.yaml | 5 + .../argocd/ignore-jobs.overlay.ytt.yaml | 16 + envs/production/env-data.ytt.yaml | 1 + prototypes/nextcloud/app-data.ytt.yaml | 5 + prototypes/nextcloud/helm/nextcloud.yaml | 198 ++++++++++ prototypes/nextcloud/vendir/base.ytt.yaml | 16 + .../nextcloud/vendir/vendir-data.ytt.yaml | 8 + .../nextcloud/ytt/admin-secret-job.ytt.yaml | 85 +++++ prototypes/nextcloud/ytt/caddy.ytt.yaml | 61 +++ .../nextcloud/ytt/cnpg-cluster.ytt.yaml | 36 ++ prototypes/nextcloud/ytt/ns.ytt.yaml | 18 + prototypes/nextcloud/ytt/valkey.ytt.yaml | 78 ++++ rendered/argocd/production/app-nextcloud.yaml | 32 ++ .../nextcloud/cluster-nextcloud-cnpg.yaml | 29 ++ .../configmap-nextcloud-caddy-config.yaml | 58 +++ .../configmap-nextcloud-phpconfig.yaml | 26 ++ .../deployment-nextcloud-valkey.yaml | 55 +++ .../nextcloud/deployment-nextcloud.yaml | 358 ++++++++++++++++++ .../nextcloud/ingress-nextcloud.yaml | 32 ++ .../nextcloud/job-nextcloud-secret-init.yaml | 48 +++ .../nextcloud/namespace-nextcloud.yaml | 9 + ...istentvolumeclaim-nextcloud-nextcloud.yaml | 22 ++ .../nextcloud/role-nextcloud-secret-init.yaml | 15 + .../rolebinding-nextcloud-secret-init.yaml | 15 + .../nextcloud/service-nextcloud-valkey.yaml | 20 + .../nextcloud/service-nextcloud.yaml | 26 ++ .../serviceaccount-nextcloud-secret-init.yaml | 7 + 27 files changed, 1279 insertions(+) create mode 100644 envs/production/_apps/nextcloud/app-data.ytt.yaml create mode 100644 envs/production/_apps/nextcloud/argocd/ignore-jobs.overlay.ytt.yaml create mode 100644 prototypes/nextcloud/app-data.ytt.yaml create mode 100644 prototypes/nextcloud/helm/nextcloud.yaml create mode 100644 prototypes/nextcloud/vendir/base.ytt.yaml create mode 100644 prototypes/nextcloud/vendir/vendir-data.ytt.yaml create mode 100644 prototypes/nextcloud/ytt/admin-secret-job.ytt.yaml create mode 100644 prototypes/nextcloud/ytt/caddy.ytt.yaml create mode 100644 prototypes/nextcloud/ytt/cnpg-cluster.ytt.yaml create mode 100644 prototypes/nextcloud/ytt/ns.ytt.yaml create mode 100644 prototypes/nextcloud/ytt/valkey.ytt.yaml create mode 100644 rendered/argocd/production/app-nextcloud.yaml create mode 100644 rendered/envs/production/nextcloud/cluster-nextcloud-cnpg.yaml create mode 100644 rendered/envs/production/nextcloud/configmap-nextcloud-caddy-config.yaml create mode 100644 rendered/envs/production/nextcloud/configmap-nextcloud-phpconfig.yaml create mode 100644 rendered/envs/production/nextcloud/deployment-nextcloud-valkey.yaml create mode 100644 rendered/envs/production/nextcloud/deployment-nextcloud.yaml create mode 100644 rendered/envs/production/nextcloud/ingress-nextcloud.yaml create mode 100644 rendered/envs/production/nextcloud/job-nextcloud-secret-init.yaml create mode 100644 rendered/envs/production/nextcloud/namespace-nextcloud.yaml create mode 100644 rendered/envs/production/nextcloud/persistentvolumeclaim-nextcloud-nextcloud.yaml create mode 100644 rendered/envs/production/nextcloud/role-nextcloud-secret-init.yaml create mode 100644 rendered/envs/production/nextcloud/rolebinding-nextcloud-secret-init.yaml create mode 100644 rendered/envs/production/nextcloud/service-nextcloud-valkey.yaml create mode 100644 rendered/envs/production/nextcloud/service-nextcloud.yaml create mode 100644 rendered/envs/production/nextcloud/serviceaccount-nextcloud-secret-init.yaml diff --git a/envs/production/_apps/nextcloud/app-data.ytt.yaml b/envs/production/_apps/nextcloud/app-data.ytt.yaml new file mode 100644 index 0000000..5a9a3a1 --- /dev/null +++ b/envs/production/_apps/nextcloud/app-data.ytt.yaml @@ -0,0 +1,5 @@ +#@data/values-schema +--- +#@overlay/match-child-defaults missing_ok=True +application: + namespace: nextcloud diff --git a/envs/production/_apps/nextcloud/argocd/ignore-jobs.overlay.ytt.yaml b/envs/production/_apps/nextcloud/argocd/ignore-jobs.overlay.ytt.yaml new file mode 100644 index 0000000..9a505aa --- /dev/null +++ b/envs/production/_apps/nextcloud/argocd/ignore-jobs.overlay.ytt.yaml @@ -0,0 +1,16 @@ +#@ load("@ytt:overlay", "overlay") + +#@overlay/match by=overlay.all, expects="1+" +--- +#@overlay/match-child-defaults missing_ok=True +spec: + ignoreDifferences: + - group: batch + kind: Job + jsonPointers: + - /spec/selector + - /spec/template/metadata/labels + syncPolicy: + syncOptions: + #@overlay/append + - RespectIgnoreDifferences=true diff --git a/envs/production/env-data.ytt.yaml b/envs/production/env-data.ytt.yaml index dbb1451..13e3b49 100644 --- a/envs/production/env-data.ytt.yaml +++ b/envs/production/env-data.ytt.yaml @@ -14,3 +14,4 @@ environment: - proto: hcloud-csi - proto: cloudnative-pg - proto: metrics-server + - proto: nextcloud diff --git a/prototypes/nextcloud/app-data.ytt.yaml b/prototypes/nextcloud/app-data.ytt.yaml new file mode 100644 index 0000000..5a9a3a1 --- /dev/null +++ b/prototypes/nextcloud/app-data.ytt.yaml @@ -0,0 +1,5 @@ +#@data/values-schema +--- +#@overlay/match-child-defaults missing_ok=True +application: + namespace: nextcloud diff --git a/prototypes/nextcloud/helm/nextcloud.yaml b/prototypes/nextcloud/helm/nextcloud.yaml new file mode 100644 index 0000000..541fd75 --- /dev/null +++ b/prototypes/nextcloud/helm/nextcloud.yaml @@ -0,0 +1,198 @@ +--- +_hostname: &hostname nextcloud.tr1ceracop.de + +replicaCount: 1 + +image: + flavor: fpm-alpine + +nginx: + enabled: false + +nextcloud: + host: *hostname + + existingSecret: + enabled: true + secretName: nextcloud-admin-secret + usernameKey: nextcloud-username + passwordKey: nextcloud-password + + objectStore: + s3: + enabled: true + bucket: nextcloud-tr1ceracop + host: nbg1.your-objectstorage.com + port: "443" + ssl: true + region: nbg1 + usePathStyle: true + existingSecret: nextcloud-s3-credentials + secretKeys: + accessKey: ACCESS_KEY_ID + secretKey: SECRET_ACCESS_KEY + + defaultConfigs: + .htaccess: false + apache-pretty-urls.config.php: false + apcu.config.php: false + apps.config.php: false + autoconfig.php: false + redis.config.php: false + reverse-proxy.config.php: false + s3.config.php: false + smtp.config.php: false + swift.config.php: false + upgrade-disable-web.config.php: false + helm-metrics.config.php: false + + extraEnv: + - name: TRUSTED_PROXIES + value: "10.0.0.0/8" + - name: OVERWRITEPROTOCOL + value: "https" + - name: OVERWRITEHOST + value: *hostname + - name: OVERWRITECLIURL + value: "https://nextcloud.tr1ceracop.de" + - name: NC_default_phone_region + value: "DE" + + phpConfigs: + uploadLimit.ini: | + upload_max_filesize = 16G + post_max_size = 16G + max_input_time = 3600 + max_execution_time = 3600 + opcache.ini: | + opcache.enable=1 + opcache.interned_strings_buffer=32 + opcache.max_accelerated_files=10000 + opcache.memory_consumption=256 + opcache.save_comments=1 + opcache.revalidate_freq=60 + + extraSidecarContainers: + - name: caddy + image: caddy:2-alpine + ports: + - name: http + containerPort: 80 + protocol: TCP + volumeMounts: + - name: nextcloud-main + mountPath: /var/www/ + subPath: root + - name: nextcloud-main + mountPath: /var/www/html + subPath: html + - name: nextcloud-main + mountPath: /var/www/html/data + subPath: data + - name: nextcloud-main + mountPath: /var/www/html/config + subPath: config + - name: nextcloud-main + mountPath: /var/www/html/custom_apps + subPath: custom_apps + - name: nextcloud-main + mountPath: /var/www/tmp + subPath: tmp + - name: nextcloud-main + mountPath: /var/www/html/themes + subPath: themes + - name: caddy-config + mountPath: /etc/caddy + resources: + requests: + cpu: 50m + memory: 32Mi + limits: + memory: 64Mi + livenessProbe: + httpGet: + path: /status.php + port: 80 + httpHeaders: + - name: Host + value: *hostname + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /status.php + port: 80 + httpHeaders: + - name: Host + value: *hostname + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 5 + + extraVolumes: + - name: caddy-config + configMap: + name: nextcloud-caddy-config + + strategy: + type: Recreate + +internalDatabase: + enabled: false + +externalDatabase: + enabled: true + type: postgresql + host: nextcloud-cnpg-rw.nextcloud.svc:5432 + database: nextcloud + existingSecret: + enabled: true + secretName: nextcloud-cnpg-app + usernameKey: username + passwordKey: password + +mariadb: + enabled: false + +postgresql: + enabled: false + +redis: + enabled: false + +externalRedis: + enabled: true + host: nextcloud-valkey.nextcloud.svc + port: "6379" + existingSecret: + enabled: true + secretName: nextcloud-valkey-password + passwordKey: password + +cronjob: + enabled: true + +persistence: + enabled: true + size: 2Gi + storageClass: local-path + annotations: + helm.sh/resource-policy: keep + +ingress: + enabled: true + className: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt + tls: + - secretName: nextcloud-tls + hosts: + - *hostname + +resources: + requests: + cpu: 200m + memory: 256Mi + limits: + memory: 512Mi diff --git a/prototypes/nextcloud/vendir/base.ytt.yaml b/prototypes/nextcloud/vendir/base.ytt.yaml new file mode 100644 index 0000000..530cdb0 --- /dev/null +++ b/prototypes/nextcloud/vendir/base.ytt.yaml @@ -0,0 +1,16 @@ +#@ load("@ytt:data", "data") + +#@ app = data.values.application +--- +apiVersion: vendir.k14s.io/v1alpha1 +kind: Config +directories: + - path: #@ "charts/" + app.name + contents: + - path: . + helmChart: + name: #@ app.name + version: #@ app.version + repository: + url: #@ app.url + lazy: true diff --git a/prototypes/nextcloud/vendir/vendir-data.ytt.yaml b/prototypes/nextcloud/vendir/vendir-data.ytt.yaml new file mode 100644 index 0000000..bcee638 --- /dev/null +++ b/prototypes/nextcloud/vendir/vendir-data.ytt.yaml @@ -0,0 +1,8 @@ +#@data/values-schema +--- +#@overlay/match-child-defaults missing_ok=True +application: + #! renovate: datasource=helm + name: nextcloud + url: https://nextcloud.github.io/helm/ + version: 9.0.4 diff --git a/prototypes/nextcloud/ytt/admin-secret-job.ytt.yaml b/prototypes/nextcloud/ytt/admin-secret-job.ytt.yaml new file mode 100644 index 0000000..0c346ed --- /dev/null +++ b/prototypes/nextcloud/ytt/admin-secret-job.ytt.yaml @@ -0,0 +1,85 @@ +#@ load("@ytt:data", "data") + +#@ ns = data.values.application.namespace + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: nextcloud-secret-init + namespace: #@ ns + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: nextcloud-secret-init + namespace: #@ ns +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "create"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: nextcloud-secret-init + namespace: #@ ns +subjects: + - kind: ServiceAccount + name: nextcloud-secret-init + namespace: #@ ns +roleRef: + kind: Role + name: nextcloud-secret-init + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: nextcloud-secret-init + namespace: #@ ns + annotations: + argocd.argoproj.io/sync-options: Replace=true +spec: + ttlSecondsAfterFinished: 300 + template: + spec: + serviceAccountName: nextcloud-secret-init + restartPolicy: OnFailure + containers: + - name: init + image: alpine/k8s:1.32.3 + command: + - sh + - -c + - | + set -e + + if ! kubectl get secret nextcloud-admin-secret -n ${NAMESPACE} >/dev/null 2>&1; then + PASSWORD=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24) + kubectl create secret generic nextcloud-admin-secret \ + -n ${NAMESPACE} \ + --from-literal=nextcloud-username=admin \ + --from-literal=nextcloud-password="${PASSWORD}" + echo "Created nextcloud-admin-secret" + else + echo "nextcloud-admin-secret already exists, skipping" + fi + + if ! kubectl get secret nextcloud-valkey-password -n ${NAMESPACE} >/dev/null 2>&1; then + VALKEY_PASSWORD=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24) + kubectl create secret generic nextcloud-valkey-password \ + -n ${NAMESPACE} \ + --from-literal=password="${VALKEY_PASSWORD}" + echo "Created nextcloud-valkey-password" + else + echo "nextcloud-valkey-password already exists, skipping" + fi + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace diff --git a/prototypes/nextcloud/ytt/caddy.ytt.yaml b/prototypes/nextcloud/ytt/caddy.ytt.yaml new file mode 100644 index 0000000..0d99f1b --- /dev/null +++ b/prototypes/nextcloud/ytt/caddy.ytt.yaml @@ -0,0 +1,61 @@ +#@ load("@ytt:data", "data") + +#@ ns = data.values.application.namespace + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: nextcloud-caddy-config + namespace: #@ ns +data: + Caddyfile: | + :80 { + root * /var/www/html + + header { + Referrer-Policy "no-referrer" + X-Content-Type-Options "nosniff" + X-Frame-Options "SAMEORIGIN" + X-Permitted-Cross-Domain-Policies "none" + X-Robots-Tag "noindex, nofollow" + X-XSS-Protection "1; mode=block" + -X-Powered-By + } + + redir /.well-known/carddav /remote.php/dav/ 301 + redir /.well-known/caldav /remote.php/dav/ 301 + redir /.well-known/* /index.php{uri} 301 + + @blocked path /build/* /tests/* /config/* /lib/* /3rdparty/* /templates/* /data/* + respond @blocked 404 + + @davclnt { + path / + header User-Agent DavClnt* + } + redir @davclnt /remote.php/webdav/ 302 + + redir /remote /remote.php{uri} 301 + + php_fastcgi 127.0.0.1:9000 { + env HTTPS on + env modHeadersAvailable true + env front_controller_active true + resolve_root_symlink + } + + @static path *.css *.js *.mjs *.svg *.gif *.ico *.jpg *.png *.webp *.wasm *.tflite *.map *.ogg *.flac + header @static Cache-Control "max-age=15778463" + + @fonts path *.otf *.woff *.woff2 + header @fonts Cache-Control "max-age=604800" + + encode gzip + + file_server + + request_body { + max_size 16GB + } + } diff --git a/prototypes/nextcloud/ytt/cnpg-cluster.ytt.yaml b/prototypes/nextcloud/ytt/cnpg-cluster.ytt.yaml new file mode 100644 index 0000000..56c925c --- /dev/null +++ b/prototypes/nextcloud/ytt/cnpg-cluster.ytt.yaml @@ -0,0 +1,36 @@ +#@ load("@ytt:data", "data") + +#@ ns = data.values.application.namespace + +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: nextcloud-cnpg + namespace: #@ ns +spec: + instances: 2 + + bootstrap: + initdb: + database: nextcloud + owner: nextcloud + + storage: + size: 5Gi + storageClass: hcloud-volumes + + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + memory: 512Mi + + postgresql: + parameters: + shared_buffers: "64MB" + effective_cache_size: "128MB" + work_mem: "4MB" + maintenance_work_mem: "32MB" + max_connections: "100" diff --git a/prototypes/nextcloud/ytt/ns.ytt.yaml b/prototypes/nextcloud/ytt/ns.ytt.yaml new file mode 100644 index 0000000..f66069b --- /dev/null +++ b/prototypes/nextcloud/ytt/ns.ytt.yaml @@ -0,0 +1,18 @@ +#@ load("@ytt:data", "data") +#@ load("@ytt:overlay", "overlay") + +#@ ns = data.values.application.namespace + +--- +apiVersion: v1 +kind: Namespace +metadata: + name: #@ ns + labels: + pod-security.kubernetes.io/enforce: privileged + +#@overlay/match by=overlay.all, expects="1+" +--- +metadata: + #@overlay/match missing_ok=True + namespace: #@ ns diff --git a/prototypes/nextcloud/ytt/valkey.ytt.yaml b/prototypes/nextcloud/ytt/valkey.ytt.yaml new file mode 100644 index 0000000..ec29459 --- /dev/null +++ b/prototypes/nextcloud/ytt/valkey.ytt.yaml @@ -0,0 +1,78 @@ +#@ load("@ytt:data", "data") + +#@ ns = data.values.application.namespace + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nextcloud-valkey + namespace: #@ ns + labels: + app.kubernetes.io/name: valkey + app.kubernetes.io/instance: nextcloud +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: valkey + app.kubernetes.io/instance: nextcloud + template: + metadata: + labels: + app.kubernetes.io/name: valkey + app.kubernetes.io/instance: nextcloud + spec: + containers: + - name: valkey + image: valkey/valkey:8-alpine + args: + - valkey-server + - --requirepass + - $(VALKEY_PASSWORD) + env: + - name: VALKEY_PASSWORD + valueFrom: + secretKeyRef: + name: nextcloud-valkey-password + key: password + ports: + - name: valkey + containerPort: 6379 + protocol: TCP + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + memory: 128Mi + livenessProbe: + tcpSocket: + port: valkey + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + tcpSocket: + port: valkey + initialDelaySeconds: 5 + periodSeconds: 5 + +--- +apiVersion: v1 +kind: Service +metadata: + name: nextcloud-valkey + namespace: #@ ns + labels: + app.kubernetes.io/name: valkey + app.kubernetes.io/instance: nextcloud +spec: + type: ClusterIP + ports: + - port: 6379 + targetPort: valkey + protocol: TCP + name: valkey + selector: + app.kubernetes.io/name: valkey + app.kubernetes.io/instance: nextcloud diff --git a/rendered/argocd/production/app-nextcloud.yaml b/rendered/argocd/production/app-nextcloud.yaml new file mode 100644 index 0000000..d8ed32b --- /dev/null +++ b/rendered/argocd/production/app-nextcloud.yaml @@ -0,0 +1,32 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + myks.dev/environment: production + finalizers: + - resources-finalizer.argocd.argoproj.io + name: app-production-nextcloud + namespace: argocd +spec: + destination: + namespace: nextcloud + server: https://kubernetes.default.svc + ignoreDifferences: + - group: batch + jsonPointers: + - /spec/selector + - /spec/template/metadata/labels + kind: Job + project: env-production + source: + path: rendered/envs/production/nextcloud + repoURL: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + targetRevision: main + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ServerSideApply=true + - RespectIgnoreDifferences=true diff --git a/rendered/envs/production/nextcloud/cluster-nextcloud-cnpg.yaml b/rendered/envs/production/nextcloud/cluster-nextcloud-cnpg.yaml new file mode 100644 index 0000000..922eca0 --- /dev/null +++ b/rendered/envs/production/nextcloud/cluster-nextcloud-cnpg.yaml @@ -0,0 +1,29 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + name: nextcloud-cnpg + namespace: nextcloud +spec: + bootstrap: + initdb: + database: nextcloud + owner: nextcloud + instances: 2 + postgresql: + parameters: + effective_cache_size: 128MB + maintenance_work_mem: 32MB + max_connections: "100" + shared_buffers: 64MB + work_mem: 4MB + resources: + limits: + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + storage: + size: 5Gi + storageClass: hcloud-volumes diff --git a/rendered/envs/production/nextcloud/configmap-nextcloud-caddy-config.yaml b/rendered/envs/production/nextcloud/configmap-nextcloud-caddy-config.yaml new file mode 100644 index 0000000..2a0f413 --- /dev/null +++ b/rendered/envs/production/nextcloud/configmap-nextcloud-caddy-config.yaml @@ -0,0 +1,58 @@ +apiVersion: v1 +data: + Caddyfile: | + :80 { + root * /var/www/html + + header { + Referrer-Policy "no-referrer" + X-Content-Type-Options "nosniff" + X-Frame-Options "SAMEORIGIN" + X-Permitted-Cross-Domain-Policies "none" + X-Robots-Tag "noindex, nofollow" + X-XSS-Protection "1; mode=block" + -X-Powered-By + } + + redir /.well-known/carddav /remote.php/dav/ 301 + redir /.well-known/caldav /remote.php/dav/ 301 + redir /.well-known/* /index.php{uri} 301 + + @blocked path /build/* /tests/* /config/* /lib/* /3rdparty/* /templates/* /data/* + respond @blocked 404 + + @davclnt { + path / + header User-Agent DavClnt* + } + redir @davclnt /remote.php/webdav/ 302 + + redir /remote /remote.php{uri} 301 + + php_fastcgi 127.0.0.1:9000 { + env HTTPS on + env modHeadersAvailable true + env front_controller_active true + resolve_root_symlink + } + + @static path *.css *.js *.mjs *.svg *.gif *.ico *.jpg *.png *.webp *.wasm *.tflite *.map *.ogg *.flac + header @static Cache-Control "max-age=15778463" + + @fonts path *.otf *.woff *.woff2 + header @fonts Cache-Control "max-age=604800" + + encode gzip + + file_server + + request_body { + max_size 16GB + } + } +kind: ConfigMap +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + name: nextcloud-caddy-config + namespace: nextcloud diff --git a/rendered/envs/production/nextcloud/configmap-nextcloud-phpconfig.yaml b/rendered/envs/production/nextcloud/configmap-nextcloud-phpconfig.yaml new file mode 100644 index 0000000..927eb4d --- /dev/null +++ b/rendered/envs/production/nextcloud/configmap-nextcloud-phpconfig.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +data: + opcache.ini: |- + opcache.enable=1 + opcache.interned_strings_buffer=32 + opcache.max_accelerated_files=10000 + opcache.memory_consumption=256 + opcache.save_comments=1 + opcache.revalidate_freq=60 + uploadLimit.ini: |- + upload_max_filesize = 16G + post_max_size = 16G + max_input_time = 3600 + max_execution_time = 3600 +kind: ConfigMap +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + labels: + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: nextcloud + app.kubernetes.io/version: 33.0.0 + helm.sh/chart: nextcloud-9.0.4 + name: nextcloud-phpconfig + namespace: nextcloud diff --git a/rendered/envs/production/nextcloud/deployment-nextcloud-valkey.yaml b/rendered/envs/production/nextcloud/deployment-nextcloud-valkey.yaml new file mode 100644 index 0000000..c311a8f --- /dev/null +++ b/rendered/envs/production/nextcloud/deployment-nextcloud-valkey.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + labels: + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: valkey + name: nextcloud-valkey + namespace: nextcloud +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: valkey + template: + metadata: + labels: + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: valkey + spec: + containers: + - args: + - valkey-server + - --requirepass + - $(VALKEY_PASSWORD) + env: + - name: VALKEY_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: nextcloud-valkey-password + image: valkey/valkey:8-alpine + livenessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: valkey + name: valkey + ports: + - containerPort: 6379 + name: valkey + protocol: TCP + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 5 + tcpSocket: + port: valkey + resources: + limits: + memory: 128Mi + requests: + cpu: 50m + memory: 64Mi diff --git a/rendered/envs/production/nextcloud/deployment-nextcloud.yaml b/rendered/envs/production/nextcloud/deployment-nextcloud.yaml new file mode 100644 index 0000000..4009e7c --- /dev/null +++ b/rendered/envs/production/nextcloud/deployment-nextcloud.yaml @@ -0,0 +1,358 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + labels: + app.kubernetes.io/component: app + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: nextcloud + app.kubernetes.io/version: 33.0.0 + helm.sh/chart: nextcloud-9.0.4 + name: nextcloud + namespace: nextcloud +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: app + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: nextcloud + strategy: + type: Recreate + template: + metadata: + annotations: + hooks-hash: 9525c2748a6c7cd0e28ec740623d0b3fa5a75c83b51ccfd136bc89c76737b204 + nextcloud-config-hash: 97fd373864ae7c5da0eb066761ee479483364e3957160cacca360fc6a66c03f7 + php-config-hash: b638f66fd8d65de8364dbad6efc59a6524c7b2e2377b5623cf5e921e4d3d2400 + labels: + app.kubernetes.io/component: app + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: nextcloud + spec: + containers: + - env: + - name: POSTGRES_HOST + value: nextcloud-cnpg-rw.nextcloud.svc:5432 + - name: POSTGRES_DB + value: nextcloud + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + key: username + name: nextcloud-cnpg-app + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: nextcloud-cnpg-app + - name: NEXTCLOUD_ADMIN_USER + valueFrom: + secretKeyRef: + key: nextcloud-username + name: nextcloud-admin-secret + - name: NEXTCLOUD_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + key: nextcloud-password + name: nextcloud-admin-secret + - name: NEXTCLOUD_TRUSTED_DOMAINS + value: nextcloud.tr1ceracop.de + - name: OPENMETRICS_ALLOWED_CLIENTS + value: 127.0.0.1,10.42.0.0/16,10.43.0.0/16 + - name: NEXTCLOUD_DATA_DIR + value: /var/www/html/data + - name: REDIS_HOST + value: nextcloud-valkey.nextcloud.svc + - name: REDIS_HOST_PORT + value: "6379" + - name: REDIS_HOST_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: nextcloud-valkey-password + - name: OBJECTSTORE_S3_SSL + value: "true" + - name: OBJECTSTORE_S3_USEPATH_STYLE + value: "true" + - name: OBJECTSTORE_S3_AUTOCREATE + value: "false" + - name: OBJECTSTORE_S3_REGION + value: nbg1 + - name: OBJECTSTORE_S3_PORT + value: "443" + - name: OBJECTSTORE_S3_STORAGE_CLASS + value: STANDARD + - name: OBJECTSTORE_S3_HOST + value: nbg1.your-objectstorage.com + - name: OBJECTSTORE_S3_BUCKET + value: nextcloud-tr1ceracop + - name: OBJECTSTORE_S3_KEY + valueFrom: + secretKeyRef: + key: ACCESS_KEY_ID + name: nextcloud-s3-credentials + - name: OBJECTSTORE_S3_SECRET + valueFrom: + secretKeyRef: + key: SECRET_ACCESS_KEY + name: nextcloud-s3-credentials + - name: OBJECTSTORE_S3_SSE_C_KEY + value: "" + - name: TRUSTED_PROXIES + value: 10.0.0.0/8 + - name: OVERWRITEPROTOCOL + value: https + - name: OVERWRITEHOST + value: nextcloud.tr1ceracop.de + - name: OVERWRITECLIURL + value: https://nextcloud.tr1ceracop.de + - name: NC_default_phone_region + value: DE + image: docker.io/library/nextcloud:33.0.0-fpm-alpine + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + httpHeaders: + - name: Host + value: nextcloud.tr1ceracop.de + path: /status.php + port: 80 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: nextcloud + ports: + - containerPort: 80 + name: http + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + httpHeaders: + - name: Host + value: nextcloud.tr1ceracop.de + path: /status.php + port: 80 + initialDelaySeconds: 10 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + resources: + limits: + memory: 512Mi + requests: + cpu: 200m + memory: 256Mi + volumeMounts: + - mountPath: /var/www/ + name: nextcloud-main + subPath: root + - mountPath: /var/www/html + name: nextcloud-main + subPath: html + - mountPath: /var/www/html/data + name: nextcloud-main + subPath: data + - mountPath: /var/www/html/config + name: nextcloud-main + subPath: config + - mountPath: /var/www/html/custom_apps + name: nextcloud-main + subPath: custom_apps + - mountPath: /var/www/tmp + name: nextcloud-main + subPath: tmp + - mountPath: /var/www/html/themes + name: nextcloud-main + subPath: themes + - mountPath: /usr/local/etc/php/conf.d/opcache.ini + name: nextcloud-phpconfig + subPath: opcache.ini + - mountPath: /usr/local/etc/php/conf.d/uploadLimit.ini + name: nextcloud-phpconfig + subPath: uploadLimit.ini + - command: + - /cron.sh + env: + - name: POSTGRES_HOST + value: nextcloud-cnpg-rw.nextcloud.svc:5432 + - name: POSTGRES_DB + value: nextcloud + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + key: username + name: nextcloud-cnpg-app + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: nextcloud-cnpg-app + - name: NEXTCLOUD_ADMIN_USER + valueFrom: + secretKeyRef: + key: nextcloud-username + name: nextcloud-admin-secret + - name: NEXTCLOUD_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + key: nextcloud-password + name: nextcloud-admin-secret + - name: NEXTCLOUD_TRUSTED_DOMAINS + value: nextcloud.tr1ceracop.de + - name: OPENMETRICS_ALLOWED_CLIENTS + value: 127.0.0.1,10.42.0.0/16,10.43.0.0/16 + - name: NEXTCLOUD_DATA_DIR + value: /var/www/html/data + - name: REDIS_HOST + value: nextcloud-valkey.nextcloud.svc + - name: REDIS_HOST_PORT + value: "6379" + - name: REDIS_HOST_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: nextcloud-valkey-password + - name: OBJECTSTORE_S3_SSL + value: "true" + - name: OBJECTSTORE_S3_USEPATH_STYLE + value: "true" + - name: OBJECTSTORE_S3_AUTOCREATE + value: "false" + - name: OBJECTSTORE_S3_REGION + value: nbg1 + - name: OBJECTSTORE_S3_PORT + value: "443" + - name: OBJECTSTORE_S3_STORAGE_CLASS + value: STANDARD + - name: OBJECTSTORE_S3_HOST + value: nbg1.your-objectstorage.com + - name: OBJECTSTORE_S3_BUCKET + value: nextcloud-tr1ceracop + - name: OBJECTSTORE_S3_KEY + valueFrom: + secretKeyRef: + key: ACCESS_KEY_ID + name: nextcloud-s3-credentials + - name: OBJECTSTORE_S3_SECRET + valueFrom: + secretKeyRef: + key: SECRET_ACCESS_KEY + name: nextcloud-s3-credentials + - name: OBJECTSTORE_S3_SSE_C_KEY + value: "" + - name: TRUSTED_PROXIES + value: 10.0.0.0/8 + - name: OVERWRITEPROTOCOL + value: https + - name: OVERWRITEHOST + value: nextcloud.tr1ceracop.de + - name: OVERWRITECLIURL + value: https://nextcloud.tr1ceracop.de + - name: NC_default_phone_region + value: DE + image: docker.io/library/nextcloud:33.0.0-fpm-alpine + imagePullPolicy: IfNotPresent + name: nextcloud-cron + resources: {} + volumeMounts: + - mountPath: /var/www/ + name: nextcloud-main + subPath: root + - mountPath: /var/www/html + name: nextcloud-main + subPath: html + - mountPath: /var/www/html/data + name: nextcloud-main + subPath: data + - mountPath: /var/www/html/config + name: nextcloud-main + subPath: config + - mountPath: /var/www/html/custom_apps + name: nextcloud-main + subPath: custom_apps + - mountPath: /var/www/tmp + name: nextcloud-main + subPath: tmp + - mountPath: /var/www/html/themes + name: nextcloud-main + subPath: themes + - mountPath: /usr/local/etc/php/conf.d/opcache.ini + name: nextcloud-phpconfig + subPath: opcache.ini + - mountPath: /usr/local/etc/php/conf.d/uploadLimit.ini + name: nextcloud-phpconfig + subPath: uploadLimit.ini + - image: caddy:2-alpine + livenessProbe: + httpGet: + httpHeaders: + - name: Host + value: nextcloud.tr1ceracop.de + path: /status.php + port: 80 + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 5 + name: caddy + ports: + - containerPort: 80 + name: http + protocol: TCP + readinessProbe: + httpGet: + httpHeaders: + - name: Host + value: nextcloud.tr1ceracop.de + path: /status.php + port: 80 + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 5 + resources: + limits: + memory: 64Mi + requests: + cpu: 50m + memory: 32Mi + volumeMounts: + - mountPath: /var/www/ + name: nextcloud-main + subPath: root + - mountPath: /var/www/html + name: nextcloud-main + subPath: html + - mountPath: /var/www/html/data + name: nextcloud-main + subPath: data + - mountPath: /var/www/html/config + name: nextcloud-main + subPath: config + - mountPath: /var/www/html/custom_apps + name: nextcloud-main + subPath: custom_apps + - mountPath: /var/www/tmp + name: nextcloud-main + subPath: tmp + - mountPath: /var/www/html/themes + name: nextcloud-main + subPath: themes + - mountPath: /etc/caddy + name: caddy-config + securityContext: + fsGroup: 33 + volumes: + - name: nextcloud-main + persistentVolumeClaim: + claimName: nextcloud-nextcloud + - configMap: + name: nextcloud-phpconfig + name: nextcloud-phpconfig + - configMap: + name: nextcloud-caddy-config + name: caddy-config diff --git a/rendered/envs/production/nextcloud/ingress-nextcloud.yaml b/rendered/envs/production/nextcloud/ingress-nextcloud.yaml new file mode 100644 index 0000000..39dc9b7 --- /dev/null +++ b/rendered/envs/production/nextcloud/ingress-nextcloud.yaml @@ -0,0 +1,32 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + cert-manager.io/cluster-issuer: letsencrypt + labels: + app.kubernetes.io/component: app + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: nextcloud + app.kubernetes.io/version: 33.0.0 + helm.sh/chart: nextcloud-9.0.4 + name: nextcloud + namespace: nextcloud +spec: + ingressClassName: traefik + rules: + - host: nextcloud.tr1ceracop.de + http: + paths: + - backend: + service: + name: nextcloud + port: + number: 8080 + path: / + pathType: Prefix + tls: + - hosts: + - nextcloud.tr1ceracop.de + secretName: nextcloud-tls diff --git a/rendered/envs/production/nextcloud/job-nextcloud-secret-init.yaml b/rendered/envs/production/nextcloud/job-nextcloud-secret-init.yaml new file mode 100644 index 0000000..f051129 --- /dev/null +++ b/rendered/envs/production/nextcloud/job-nextcloud-secret-init.yaml @@ -0,0 +1,48 @@ +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + argocd.argoproj.io/sync-options: Replace=true + name: nextcloud-secret-init + namespace: nextcloud +spec: + template: + spec: + containers: + - command: + - sh + - -c + - | + set -e + + if ! kubectl get secret nextcloud-admin-secret -n ${NAMESPACE} >/dev/null 2>&1; then + PASSWORD=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24) + kubectl create secret generic nextcloud-admin-secret \ + -n ${NAMESPACE} \ + --from-literal=nextcloud-username=admin \ + --from-literal=nextcloud-password="${PASSWORD}" + echo "Created nextcloud-admin-secret" + else + echo "nextcloud-admin-secret already exists, skipping" + fi + + if ! kubectl get secret nextcloud-valkey-password -n ${NAMESPACE} >/dev/null 2>&1; then + VALKEY_PASSWORD=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24) + kubectl create secret generic nextcloud-valkey-password \ + -n ${NAMESPACE} \ + --from-literal=password="${VALKEY_PASSWORD}" + echo "Created nextcloud-valkey-password" + else + echo "nextcloud-valkey-password already exists, skipping" + fi + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: alpine/k8s:1.32.3 + name: init + restartPolicy: OnFailure + serviceAccountName: nextcloud-secret-init + ttlSecondsAfterFinished: 300 diff --git a/rendered/envs/production/nextcloud/namespace-nextcloud.yaml b/rendered/envs/production/nextcloud/namespace-nextcloud.yaml new file mode 100644 index 0000000..e182df4 --- /dev/null +++ b/rendered/envs/production/nextcloud/namespace-nextcloud.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Namespace +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + labels: + pod-security.kubernetes.io/enforce: privileged + name: nextcloud + namespace: nextcloud diff --git a/rendered/envs/production/nextcloud/persistentvolumeclaim-nextcloud-nextcloud.yaml b/rendered/envs/production/nextcloud/persistentvolumeclaim-nextcloud-nextcloud.yaml new file mode 100644 index 0000000..f8edf43 --- /dev/null +++ b/rendered/envs/production/nextcloud/persistentvolumeclaim-nextcloud-nextcloud.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + helm.sh/resource-policy: keep + labels: + app.kubernetes.io/component: app + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: nextcloud + app.kubernetes.io/version: 33.0.0 + helm.sh/chart: nextcloud-9.0.4 + name: nextcloud-nextcloud + namespace: nextcloud +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi + storageClassName: local-path diff --git a/rendered/envs/production/nextcloud/role-nextcloud-secret-init.yaml b/rendered/envs/production/nextcloud/role-nextcloud-secret-init.yaml new file mode 100644 index 0000000..32421f5 --- /dev/null +++ b/rendered/envs/production/nextcloud/role-nextcloud-secret-init.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + name: nextcloud-secret-init + namespace: nextcloud +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create diff --git a/rendered/envs/production/nextcloud/rolebinding-nextcloud-secret-init.yaml b/rendered/envs/production/nextcloud/rolebinding-nextcloud-secret-init.yaml new file mode 100644 index 0000000..114f318 --- /dev/null +++ b/rendered/envs/production/nextcloud/rolebinding-nextcloud-secret-init.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + name: nextcloud-secret-init + namespace: nextcloud +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: nextcloud-secret-init +subjects: + - kind: ServiceAccount + name: nextcloud-secret-init + namespace: nextcloud diff --git a/rendered/envs/production/nextcloud/service-nextcloud-valkey.yaml b/rendered/envs/production/nextcloud/service-nextcloud-valkey.yaml new file mode 100644 index 0000000..3e0a7d3 --- /dev/null +++ b/rendered/envs/production/nextcloud/service-nextcloud-valkey.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + labels: + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: valkey + name: nextcloud-valkey + namespace: nextcloud +spec: + ports: + - name: valkey + port: 6379 + protocol: TCP + targetPort: valkey + selector: + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: valkey + type: ClusterIP diff --git a/rendered/envs/production/nextcloud/service-nextcloud.yaml b/rendered/envs/production/nextcloud/service-nextcloud.yaml new file mode 100644 index 0000000..9038ce7 --- /dev/null +++ b/rendered/envs/production/nextcloud/service-nextcloud.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + labels: + app.kubernetes.io/component: app + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/monitor: enabled + app.kubernetes.io/name: nextcloud + app.kubernetes.io/version: 33.0.0 + helm.sh/chart: nextcloud-9.0.4 + name: nextcloud + namespace: nextcloud +spec: + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 80 + selector: + app.kubernetes.io/component: app + app.kubernetes.io/instance: nextcloud + app.kubernetes.io/name: nextcloud + type: ClusterIP diff --git a/rendered/envs/production/nextcloud/serviceaccount-nextcloud-secret-init.yaml b/rendered/envs/production/nextcloud/serviceaccount-nextcloud-secret-init.yaml new file mode 100644 index 0000000..f713d4a --- /dev/null +++ b/rendered/envs/production/nextcloud/serviceaccount-nextcloud-secret-init.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + annotations: + a8r.io/repository: ssh://git@git.tr1ceracop.de:222/gitea_admin/k8s-and-chill.git + name: nextcloud-secret-init + namespace: nextcloud