# services/nextcloud/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: nextcloud namespace: nextcloud labels: app: nextcloud spec: replicas: 1 # These volumes are RWO; release the old pod before replacing it. strategy: type: RollingUpdate rollingUpdate: maxSurge: 0 maxUnavailable: 1 selector: matchLabels: app: nextcloud template: metadata: labels: app: nextcloud annotations: vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/agent-init-first: "true" vault.hashicorp.com/agent-requests-cpu: "25m" vault.hashicorp.com/role: "nextcloud" vault.hashicorp.com/agent-inject-secret-nextcloud-env.sh: "kv/data/atlas/nextcloud/nextcloud-db" vault.hashicorp.com/agent-inject-template-nextcloud-env.sh: | {{ with secret "kv/data/atlas/nextcloud/nextcloud-db" }} export POSTGRES_DB="{{ .Data.data.database }}" export POSTGRES_USER="{{ index .Data.data "db-username" }}" export POSTGRES_PASSWORD="{{ index .Data.data "db-password" }}" {{ end }} {{ with secret "kv/data/atlas/nextcloud/nextcloud-admin" }} export NEXTCLOUD_ADMIN_USER="{{ index .Data.data "admin-user" }}" export NEXTCLOUD_ADMIN_PASSWORD="{{ index .Data.data "admin-password" }}" {{ end }} export ADMIN_USER="${NEXTCLOUD_ADMIN_USER}" export ADMIN_PASS="${NEXTCLOUD_ADMIN_PASSWORD}" {{ with secret "kv/data/atlas/nextcloud/nextcloud-oidc" }} export OIDC_CLIENT_ID="{{ index .Data.data "client-id" }}" export OIDC_CLIENT_SECRET="{{ index .Data.data "client-secret" }}" {{ end }} {{ with secret "kv/data/atlas/shared/postmark-relay" }} export SMTP_NAME="{{ index .Data.data "apikey" }}" export SMTP_PASSWORD="{{ index .Data.data "apikey" }}" {{ end }} {{ with secret "kv/data/atlas/shared/keycloak-admin" }} export KC_ADMIN_USER="{{ .Data.data.username }}" export KC_ADMIN_PASS="{{ .Data.data.password }}" {{ end }} spec: nodeSelector: hardware: rpi5 node-role.kubernetes.io/worker: "true" securityContext: fsGroup: 33 runAsUser: 33 runAsGroup: 33 serviceAccountName: nextcloud-vault initContainers: - name: seed-nextcloud-web image: nextcloud:29-apache@sha256:a7fbfcd4759bdd19b8fb8b1044b47ee3a9471d2e2c8bc68d56a2e671f86cebd2 command: ["/bin/sh", "-c"] args: - | if [ ! -f /var/www/html/version.php ]; then echo "Seeding Nextcloud webroot..." tar -C /usr/src/nextcloud -cf - \ --exclude=./config \ --exclude=./data \ --exclude=./custom_apps \ . | tar -C /var/www/html -xf - chown -R 33:33 /var/www/html || true fi securityContext: runAsUser: 0 runAsGroup: 0 volumeMounts: - name: nextcloud-web mountPath: /var/www/html - name: fix-perms image: alpine:3.20 command: ["/bin/sh", "-c"] args: - | if [ ! -s /var/www/html/config/config.php ]; then rm -f /var/www/html/config/config.php || true fi mkdir -p /var/www/html/config /var/www/html/data /var/www/html/custom_apps || true if [ ! -s /var/www/html/config/config.php ]; then rm -f /var/www/html/data/.ocdata || true fi if [ -s /var/www/html/config/config.php ] && [ ! -f /var/www/html/data/.ocdata ]; then touch /var/www/html/data/.ocdata fi chown -R 33:33 /var/www/html/config /var/www/html/data /var/www/html/custom_apps || true securityContext: runAsUser: 0 runAsGroup: 0 volumeMounts: - name: nextcloud-config-pvc mountPath: /var/www/html/config - name: nextcloud-custom-apps mountPath: /var/www/html/custom_apps - name: nextcloud-user-data mountPath: /var/www/html/data - name: nextcloud-config-extra mountPath: /var/www/html/config/extra.config.php subPath: extra.config.php - name: install-nextcloud image: nextcloud:29-apache@sha256:a7fbfcd4759bdd19b8fb8b1044b47ee3a9471d2e2c8bc68d56a2e671f86cebd2 securityContext: runAsUser: 0 runAsGroup: 0 command: ["/bin/sh", "-c"] args: - | . /vault/secrets/nextcloud-env.sh installed="$(su -s /bin/sh www-data -c "php /var/www/html/occ status" 2>/dev/null | awk '/installed:/{print $3}' || true)" if [ ! -s /var/www/html/config/config.php ]; then su -s /bin/sh www-data -c "php /var/www/html/occ maintenance:install --database pgsql --database-host \"${POSTGRES_HOST}\" --database-name \"${POSTGRES_DB}\" --database-user \"${POSTGRES_USER}\" --database-pass \"${POSTGRES_PASSWORD}\" --admin-user \"${NEXTCLOUD_ADMIN_USER}\" --admin-pass \"${NEXTCLOUD_ADMIN_PASSWORD}\" --data-dir /var/www/html/data" chown 33:33 /var/www/html/config/config.php || true chown -R 33:33 /var/www/html/data || true fi installed="$(su -s /bin/sh www-data -c "php /var/www/html/occ status" 2>/dev/null | awk '/installed:/{print $3}' || true)" if [ "${installed}" = "true" ]; then configure_oidc() { su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_provider_url --value='https://sso.bstein.dev/realms/atlas'" su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_client_id --value='${OIDC_CLIENT_ID}'" >/dev/null su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_client_secret --value='${OIDC_CLIENT_SECRET}'" >/dev/null su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_auto_redirect --type=boolean --value=true" su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_hide_password_form --type=boolean --value=true" su -s /bin/sh www-data -c "php /var/www/html/occ config:system:set oidc_login_disable_registration --type=boolean --value=false" } configure_office() { su -s /bin/sh www-data -c "php /var/www/html/occ config:app:set richdocuments wopi_url --value='https://office.bstein.dev'" su -s /bin/sh www-data -c "php /var/www/html/occ config:app:set richdocuments public_wopi_url --value='https://office.bstein.dev'" } ensure_mime_defaults() { cfg_dir="/var/www/html/resources/config" mkdir -p "${cfg_dir}" if [ ! -s "${cfg_dir}/mimetypemapping.dist.json" ]; then curl --connect-timeout 10 --max-time 120 -fsSL https://raw.githubusercontent.com/nextcloud/server/v29.0.16/resources/config/mimetypemapping.dist.json -o "${cfg_dir}/mimetypemapping.dist.json" || true fi if [ ! -s "${cfg_dir}/mimetypealiases.dist.json" ]; then curl --connect-timeout 10 --max-time 120 -fsSL https://raw.githubusercontent.com/nextcloud/server/v29.0.16/resources/config/mimetypealiases.dist.json -o "${cfg_dir}/mimetypealiases.dist.json" || true fi chown -R 33:33 "${cfg_dir}" || true } install_app() { app="$1" url="$2" target="/var/www/html/custom_apps/${app}" # Installed apps survive pod replacement; upgrades are maintenance. if [ -s "${target}/appinfo/info.xml" ]; then return 0 fi stage="$(mktemp -d /var/www/html/custom_apps/.bootstrap-XXXXXX)" if ! curl --connect-timeout 10 --max-time 180 -fsSL "${url}" -o "${stage}/app.tar.gz" \ || ! tar -xzf "${stage}/app.tar.gz" -C "${stage}" \ || [ ! -s "${stage}/${app}/appinfo/info.xml" ]; then rm -rf "${stage}" return 1 fi # Rename on the same PVC only after extraction is complete. if [ -e "${target}" ]; then mv "${target}" "${stage}/previous" fi mv "${stage}/${app}" "${target}" chown -R 33:33 "${target}" rm -rf "${stage}" su -s /bin/sh www-data -c "php /var/www/html/occ app:enable --force ${app}" } ensure_app() { app="$1" target="/var/www/html/custom_apps/${app}" if [ -s "${target}/appinfo/info.xml" ]; then return 0 fi su -s /bin/sh www-data -c "php /var/www/html/occ app:install ${app}" || return 1 su -s /bin/sh www-data -c "php /var/www/html/occ app:enable ${app}" } ensure_mime_defaults install_app external https://github.com/nextcloud-releases/external/releases/download/v5.4.1/external-v5.4.1.tar.gz || exit 1 install_app mail https://github.com/nextcloud-releases/mail/releases/download/v3.7.24/mail-stable3.7.tar.gz || exit 1 install_app oidc_login https://github.com/pulsejet/nextcloud-oidc-login/releases/download/v3.2.2/oidc_login.tar.gz || exit 1 ensure_app richdocuments || exit 1 configure_office configure_oidc fi env: - name: POSTGRES_HOST value: postgres-service.postgres.svc.cluster.local volumeMounts: - name: nextcloud-web mountPath: /var/www/html - name: nextcloud-config-pvc mountPath: /var/www/html/config - name: nextcloud-custom-apps mountPath: /var/www/html/custom_apps - name: nextcloud-user-data mountPath: /var/www/html/data - name: nextcloud-config-extra mountPath: /var/www/html/config/extra.config.php subPath: extra.config.php containers: - name: nextcloud image: nextcloud:29-apache@sha256:a7fbfcd4759bdd19b8fb8b1044b47ee3a9471d2e2c8bc68d56a2e671f86cebd2 imagePullPolicy: IfNotPresent command: ["/bin/sh", "-c"] args: - >- . /vault/secrets/nextcloud-env.sh && exec /entrypoint.sh apache2-foreground env: # DB (external secret required: nextcloud-db with keys username,password,database) - name: POSTGRES_HOST value: postgres-service.postgres.svc.cluster.local # Admin bootstrap (external secret: nextcloud-admin with keys admin-user, admin-password) - name: NEXTCLOUD_TRUSTED_DOMAINS value: cloud.bstein.dev - name: OVERWRITEHOST value: cloud.bstein.dev - name: OVERWRITEPROTOCOL value: https - name: OVERWRITECLIURL value: https://cloud.bstein.dev # SMTP (external secret: nextcloud-smtp with keys username, password) - name: SMTP_HOST value: mail.bstein.dev - name: SMTP_PORT value: "587" - name: SMTP_SECURE value: tls - name: MAIL_FROM_ADDRESS value: no-reply-nextcloud - name: MAIL_DOMAIN value: bstein.dev # OIDC (external secret: nextcloud-oidc with keys client-id, client-secret) - name: NEXTCLOUD_UPDATE value: "1" - name: APP_INSTALL value: "mail,oidc_login,external" ports: - containerPort: 80 name: http readinessProbe: httpGet: path: /status.php port: http initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 volumeMounts: - name: nextcloud-web mountPath: /var/www/html - name: nextcloud-config-pvc mountPath: /var/www/html/config - name: nextcloud-custom-apps mountPath: /var/www/html/custom_apps - name: nextcloud-user-data mountPath: /var/www/html/data - name: nextcloud-config-extra mountPath: /var/www/html/config/extra.config.php subPath: extra.config.php resources: requests: cpu: 250m # Measured 24-hour peak: 394Mi; burst limit stays at 3Gi. memory: 512Mi limits: cpu: 1 memory: 3Gi volumes: - name: nextcloud-web persistentVolumeClaim: claimName: nextcloud-web-v2 - name: nextcloud-config-pvc persistentVolumeClaim: claimName: nextcloud-config-v2 - name: nextcloud-custom-apps persistentVolumeClaim: claimName: nextcloud-custom-apps-v2 - name: nextcloud-user-data persistentVolumeClaim: claimName: nextcloud-user-data-v2 - name: nextcloud-config-extra configMap: name: nextcloud-config defaultMode: 0444