Installation steps — Euro-Office Docs and Nextcloud

Date: 2026-10-28 Working directory: content/opentofu Target cluster: the Talos/Kubernetes cluster described in access-sites.md (3 control-plane nodes, Kubernetes v1.31.1, Traefik ingress, local-path storage).

This document records how Euro-Office Docs and Nextcloud were installed with Argo CD, including every deviation from install-euro-office.md that was needed for this cluster.

Result: see section 12 for the final verification.


1. Environment and pre-flight checks

ItemValue
Nodestalos-node-1 / talos-node-2 / talos-node-3
Node capacity2 vCPU, 4 GiB RAM, 20 GB system disk each
Allocatable per node~1950m CPU, ~3.2 GiB RAM, ~17.8 GB ephemeral storage
Ingress controllerTraefik (IngressClass traefik, NodePort 30080/30443)
Default StorageClasslocal-path (rancher.io/local-path, RWO)
Argo CDargocd namespace, app-of-apps root Application root
GitOps repositoryForgejo gitea_admin/gitops, path apps

Checks performed:

kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU:.status.allocatable.cpu,MEM:.status.allocatable.memory
kubectl get sc
kubectl get ingressclass
kubectl get applications -n argocd

The cluster had roughly 2.2 GiB of free memory per node. Euro-Office is a memory-hungry workload, so all resource requests/limits were reduced (see section 5) and the Euro-Office pods were pinned to one node.

1.1 Upstream chart availability

The chart URL used by the guide, https://euro-office.github.io/Kubernetes-Docs/, returns 404. The documented Helm repository https://download.euro-office.com/charts/stable is now a parked domain: HTTP returns an HTML landing page and HTTPS fails with tlsv1 unrecognized name.

The chart source is still published in the official repository:

The chart was therefore vendored into the GitOps repository instead of being fetched from a Helm repository. The Nextcloud chart (nextcloud/nextcloud 8.3.0, app 32.0.0) was vendored the same way.

The Euro-Office chart defaults to image tag 9.3.1-1, which is not published on GHCR. The published tag is v9.3.1, so that value is overridden.


2. Namespaces

kubectl create namespace euro-office
kubectl create namespace nextcloud

kubectl create namespace nfs-server
kubectl label namespace nfs-server \
  pod-security.kubernetes.io/enforce=privileged --overwrite

nfs-server needs the privileged Pod Security label because the NFS provisioner adds the DAC_READ_SEARCH and SYS_RESOURCE capabilities, which are not allowed by the default baseline profile.


3. ReadWriteMany storage for Euro-Office

The Euro-Office chart hardcodes ReadWriteMany for its three PVCs (ds-files, ds-runtime-config, ds-buffer). local-path only supports ReadWriteOnce, so an NFS provisioner was installed, exactly as the upstream guide recommends.

helm repo add nfs-server-provisioner \
  https://kubernetes-sigs.github.io/nfs-ganesha-server-and-external-provisioner
helm repo update

helm upgrade --install nfs-server \
  nfs-server-provisioner/nfs-server-provisioner \
  --namespace nfs-server \
  --set persistence.enabled=true \
  --set persistence.storageClass=local-path \
  --set persistence.size=8Gi \
  --set storageClass.name=nfs \
  --set storageClass.defaultClass=false \
  --set resources.requests.cpu=50m \
  --set resources.requests.memory=64Mi \
  --set resources.limits.memory=256Mi \
  --wait --timeout 6m

Result:

ppSoetdro/srniafsgste-eCsnletarvsvoselrun-mfnesfcsl-asikemur/bdeearrtn-aep-trneofsvs.i-issoie/ornnvfeesrr---s0nefrsv-e1sr/e-1rpvreorvR-iupsnrinooivnniegsrioneDre-l0eteBouInmdmedi8aGtieRWOlocal-path

The chart’s PVC sizes were reduced to fit the 8 GiB backing volume: ds-files 4 GiB + ds-runtime-config 1 GiB (+ optional ds-buffer 2 GiB).


4. Prerequisite services

The Euro-Office chart expects three external services. Instead of the Bitnami charts used by the upstream README (whose legacy images are being retired), the services were deployed as small plain manifests with the official upstream images. This keeps the image set small and avoids Bitnami catalog changes.

All three run in the euro-office namespace.

4.1 PostgreSQL

kubectl -n euro-office get deploy,svc,pvc
kubectl -n euro-office exec deploy/postgres -- psql -U postgres -l

4.2 Redis

4.3 RabbitMQ

The readiness probe initially used the default 1-second timeout and always failed with rabbitmq-diagnostics -q ping (the command takes longer than one second even on a healthy node). It was corrected to:

readinessProbe:
  exec:
    command: ["rabbitmq-diagnostics", "-q", "ping"]
  initialDelaySeconds: 20
  periodSeconds: 10
  timeoutSeconds: 15
  failureThreshold: 6

4.4 Secrets

Secrets are created out of band; no passwords are stored in this document or in the GitOps repository:

SecretNamespaceKeys
postgres-credentialseuro-officePOSTGRES_PASSWORD, EUROOFFICE_DB_PASSWORD, NEXTCLOUD_DB_PASSWORD
eurooffice-dbeuro-officepassword
rediseuro-officeredis-password
rabbitmqeuro-officerabbitmq-password
euro-office-jwteuro-officeJWT_SECRET
nextcloud-dbnextcloudusername, password
nextcloud-adminnextcloudusername, password

They were generated with openssl rand -hex … and applied with kubectl create secret generic --dry-run=client -o yaml | kubectl apply -f -. The temporary credentials file used during the run lived only on the management container and was deleted afterwards.


5. Helm values adaptations

Both charts are vendored under ansible2/files/gitops/charts/:

c├│└h──a──rte└n└su─e─/r─x─ot-vcvoalaflolfuuuiedecs/se---kkd88ossc..syy/aammll##uuppssttrreeaammKnuebxetrcnleotueds-8D.o3c.s06.0.0

5.1 Euro-Office Docs

charts/euro-office-docs/values-k8s.yaml differs from the upstream defaults as follows:

SettingValueReason
images.tagv9.3.19.3.1-1 is not published on GHCR
nodeSelectorkubernetes.io/hostname: talos-node-1pull the 1.4 GB image once; keep memory-heavy pods together
ingress.enabledtrueexpose the service
ingress.ingressClassNametraefikcluster uses Traefik, not NGINX
ingress.hosteuro-office.kubernetes.localhomelab DNS name
ingress.ssl.enabledfalseno cert-manager in this cluster
jwt.existingSecreteuro-office-jwtJWT secret is not committed
connections.dbExistingSecreteurooffice-dbDB password is not committed
connections.dbNameeuroofficededicated database
connections.redisHostredis-mastermatches the Redis Service
connections.amqpHostrabbitmqmatches the RabbitMQ Service
persistence.storageClassnfschart requires ReadWriteMany
persistence.size4Gifit the 8 GiB NFS backing volume
persistence.runtimeConfig.size1Gisame
persistence.customResources.enabledfalsenot needed; saves a PVC and job
docservice.replicas1small cluster
converter.replicas1small cluster
docservice.resourcesrequests 250m/512Mi, limits 2 CPU/2Gifit node
converter.resourcesrequests 200m/256Mi, limits 1 CPU/1Gifit node
proxy.resourcesrequests 100m/128Mi, limits 500m/512Mifit node
metrics.enabledfalsenot required
adminpanel.enabledfalsenot required
example.enabledfalsenot required
tests.enabledfalseno Helm test pods
upgrade.job.enabledfalseArgo CD also runs pre-upgrade hooks on install
rollback.job.enabledfalsesame
sqlScripts.branchNamemainschema lives on the Euro-Office fork

The chart’s schema templates were also patched from raw.githubusercontent.com/ONLYOFFICE/server/… to raw.githubusercontent.com/Euro-Office/server/…; the old URL returns 404 for schema/postgresql/createdb.sql.

5.2 Nextcloud

charts/nextcloud/values-k8s.yaml uses:

SettingValueReason
ingress.enabledtrueexpose the service
ingress.classNametraefikTraefik
nextcloud.hostnextcloud.kubernetes.localhomelab DNS name
nextcloud.existingSecretnextcloud-adminadmin password is not committed
persistence.storageClasslocal-pathRWO is sufficient for the Nextcloud data volume
persistence.size5Gifit the node disks
internalDatabase.enabledfalseno SQLite
externalDatabasePostgreSQL at postgresql.euro-office.svc.cluster.localreuse the Euro-Office database server
externalDatabase.existingSecretnextcloud-dbDB password is not committed
mariadb.enabledfalseno bundled MariaDB
postgresql.enabledfalseno bundled PostgreSQL
redis.enabledfalseno bundled Redis
collabora.enabledfalseEuro-Office is the document server
resourcesrequests 200m/256Mi, limits 1 CPU/1Gifit node
probeslonger initial delaysfirst start initialises the database

6. Argo CD Applications

Two Application manifests were added at the top level of apps/:

The root Application syncs path: apps, but Argo CD’s directory source does not recurse by default. The manifests were therefore placed directly in apps/, not in apps/office/.

Both Applications use the vendored chart from the in-cluster Forgejo repository:

source:
  repoURL: http://forgejo-http.forgejo.svc.cluster.local:3000/gitea_admin/gitops.git
  targetRevision: HEAD
  path: charts/euro-office-docs        # or charts/nextcloud
  helm:
    releaseName: documentserver        # or nextcloud
    valueFiles:
      - values-k8s.yaml
destination:
  namespace: euro-office               # or nextcloud
syncPolicy:
  automated:
    prune: true
    selfHeal: true
  syncOptions:
    - CreateNamespace=true

The manifests explicitly set metadata.namespace: argocd so the root Application creates them in the Argo CD namespace.

Git commits pushed to the GitOps repository during this installation:

CommitChange
50e817badd both vendored charts and the initial Application manifests
970b9f0move Applications from apps/office/ to apps/ (root does not recurse)
2e4b66apin Euro-Office pods to talos-node-1
0f5755cdisable Euro-Office pre-upgrade/pre-rollback hooks
1046d5cpoint schema downloads at Euro-Office/server main

After each push, the root Application was refreshed with:

kubectl -n argocd annotate application root \
  argocd.argoproj.io/refresh=normal --overwrite

7. Problems found and corrections

7.1 The guide’s chart repository no longer exists

7.2 Image tag 9.3.1-1 does not exist

GHCR publishes v9.3.1, not 9.3.1-1. The pod would otherwise stay in ImagePullBackOff. fix: images.tag: v9.3.1.

7.3 Root Application does not recurse

The Applications were first placed in apps/office/, but the root Application only syncs files directly under apps/. fix: move them to apps/euro-office.yaml and apps/nextcloud.yaml.

7.4 pre-upgrade hook runs on a fresh install

Argo CD maps both pre-install and pre-upgrade Helm hooks to PreSync. The pre-upgrade hook tries to disable the docservice service, which does not exist yet, and fails with:

ThCeousledrvneortcroeuslodlvneothobset:didsoacbsleerdvice

fix: set upgrade.job.enabled: false and rollback.job.enabled: false in values-k8s.yaml. On upgrades, run those hooks manually if needed.

7.5 Database schema download returns 404

The chart’s pre-install job downloads schema/postgresql/createdb.sql from ONLYOFFICE/server/master. That path no longer exists. The job logged:

HwToTrPkrdeoqnueestsent,awaitingresponse...404NotFound

…but the eurooffice database stayed empty.

fix (one-time): apply the schema from the Euro-Office fork manually:

curl -fsSL https://raw.githubusercontent.com/Euro-Office/server/main/schema/postgresql/createdb.sql \
  -o /tmp/createdb.sql
{ echo "SET ROLE eurooffice;"; cat /tmp/createdb.sql; } \
  | kubectl -n euro-office exec -i deploy/postgres -- \
      psql -U postgres -d eurooffice -v ON_ERROR_STOP=1

fix (reproducible): the vendored chart templates now point at https://raw.githubusercontent.com/Euro-Office/server/{{ branch }}/… and sqlScripts.branchName: main.

Verify:

kubectl -n euro-office exec deploy/postgres -- \
  psql -U postgres -d eurooffice -c '\dt'

Expected: doc_changes and task_result, both owned by eurooffice.

7.6 Very slow image pulls

The cluster’s uplink delivered roughly 0.3–1 MB/s during the installation. The Euro-Office main image (cluster-docs) is about 1.4 GB and the utilities image (cluster-utils) about 361 MB; the Nextcloud image is about 519 MB.

Mitigations:

This is an environment limitation, not a chart problem.

7.7 RabbitMQ readiness probe timeout

See section 4.3. The default 1-second probe timeout is too short for rabbitmq-diagnostics -q ping.


8. Current resource usage

After the prerequisites were running (before the Euro-Office images finished pulling):

NamespaceWorkloadCPU requestMemory requestStorage
nfs-serverprovisioner50m64Mi8Gi (local-path)
euro-officePostgreSQL100m128Mi2Gi (local-path)
euro-officeRedis50m64Mi—
euro-officeRabbitMQ100m128Mi—
euro-officedocservice250m512Mi4Gi + 1Gi (NFS)
euro-officeconverter200m256Mishares NFS volumes
euro-officeproxy (sidecar)100m128Mi—
nextcloudNextcloud200m256Mi5Gi (local-path)

9. Accessing the services

Add both hostnames to the client machine that runs the browser (not to the Proxmox host):

192.168.0.201euro-office.kubernetes.localnextcloud.kubernetes.local

192.168.0.201 is the management container, which proxies ports 80/443 to the Traefik NodePorts. Then open:

ServiceURL
Euro-Office Docshttp://euro-office.kubernetes.local/
Nextcloudhttp://nextcloud.kubernetes.local/

Verify from the management container without changing /etc/hosts:

curl -s -o /dev/null -w '%{http_code}\n' \
  -H 'Host: euro-office.kubernetes.local' http://192.168.0.201/
curl -s -o /dev/null -w '%{http_code}\n' \
  -H 'Host: nextcloud.kubernetes.local' http://192.168.0.201/

9.1 Credentials

Retrieve them from the cluster; they are not stored in Git:

# Nextcloud admin
ssh root@192.168.0.201 \
  "kubectl -n nextcloud get secret nextcloud-admin \
     -o jsonpath='{.data.username}' | base64 -d; echo"
ssh root@192.168.0.201 \
  "kubectl -n nextcloud get secret nextcloud-admin \
     -o jsonpath='{.data.password}' | base64 -d; echo"

# Euro-Office JWT secret
ssh root@192.168.0.201 \
  "kubectl -n euro-office get secret euro-office-jwt \
     -o jsonpath='{.data.JWT_SECRET}' | base64 -d; echo"

10. Nextcloud ↔ Euro-Office integration

The Nextcloud connector app is eurooffice (https://github.com/Euro-Office/eurooffice-nextcloud). Once the Nextcloud pod is ready:

kubectl -n nextcloud exec deploy/nextcloud -- \
  php occ app:install eurooffice
kubectl -n nextcloud exec deploy/nextcloud -- \
  php occ app:enable eurooffice

kubectl -n nextcloud exec deploy/nextcloud -- \
  php occ config:app:set eurooffice DocumentServerUrl \
    --value="http://euro-office.kubernetes.local/"
kubectl -n nextcloud exec deploy/nextcloud -- \
  php occ config:app:set eurooffice DocumentServerInternalUrl \
    --value="http://documentserver.euro-office.svc.cluster.local:8888/"
kubectl -n nextcloud exec deploy/nextcloud -- \
  php occ config:app:set eurooffice StorageUrl \
    --value="http://nextcloud.nextcloud.svc.cluster.local/"
kubectl -n nextcloud exec deploy/nextcloud -- \
  php occ config:app:set eurooffice jwt_secret \
    --value="$(kubectl -n euro-office get secret euro-office-jwt \
      -o jsonpath='{.data.JWT_SECRET}' | base64 -d)"

DocumentServerUrl is what the browser uses; DocumentServerInternalUrl is what the Nextcloud server uses inside the cluster; StorageUrl is what the document server uses to fetch files from Nextcloud.


11. Deviations from install-euro-office.md

Guide stepWhat was done insteadWhy
Helm repo euro-office.github.io / download.euro-office.comvendored chart from GitHubrepositories are gone / parked
NGINX IngressTraefikcluster already uses Traefik
cert-manager / Let’s EncryptHTTP onlyno cert-manager; homelab
NFS server provisionerinstalled as describedchart requires RWX
Bitnami PostgreSQL/Redis/RabbitMQsmall manifests with official imagesavoid retired Bitnami images, reduce footprint
Nextcloud + bundled MariaDBNextcloud + external PostgreSQLone database server, less memory/disk
apps/office/ subdirectorytop-level apps/Argo CD directory source does not recurse
Helm pre-upgrade hookdisabledArgo CD runs it on install; it fails
upstream schema URLpatched to Euro-Office/server/mainold URL returns 404

12. Verification

Filled in after the images finished pulling and the pods became ready.

kubectl -n argocd get applications
kubectl -n euro-office get pods,pvc
kubectl -n nextcloud get pods,pvc

Expected:


13. Clean-up / re-run notes