# Nextcloud (self-hosted Drive, WebDAV) — KB module 4. # # Second document source for KB pillar #2: arbitrary files + phone/desktop # sync. KB ingest (module 5) SNAPSHOTS files into the KB archive (Nextcloud = # KOPIA in the kb-02 hybrid) — Nextcloud is NOT a source of truth, which # relaxes its backup and availability requirements vs paperless. # # Host decided: PIHA (not SOLARIA) — Oskar uses Nextcloud actively (phone # sync, family), needs always-on availability; SOLARIA's session-only uptime # was not acceptable for this workload. See # docs/kb/modules/DECYZJE-do-podjecia.md #1 and README.md. Compose stays # portable: paths follow the /opt/homelab/data convention, bind IP and proxy # come from .env. # # EXPOSURE: LAN/Tailscale only przez npm@PIHA (cloud.kapala.org), zero public. # npm and nextcloud are both on PIHA — npm reaches it as a local container. services: nextcloud: # Pinned major: 34-apache was current stable as of 2026-07-09 (verified # via endoflife.date/nextcloud — released 2026-06-09). TODO AT DEPLOY: # Nextcloud ships a new major every ~4 months and does NOT support # skipping majors on upgrade — reconfirm the current stable major right # before `docker compose up` and bump the tag if one shipped since. image: nextcloud:34-apache container_name: nextcloud restart: unless-stopped depends_on: db: condition: service_healthy redis: condition: service_healthy # OIDC discovery against Forgejo over the LAN (same lesson as vikunja). extra_hosts: - "forgejo.kapala.org:192.168.31.5" env_file: - .env environment: - POSTGRES_HOST=db - POSTGRES_DB=nextcloud - POSTGRES_USER=nextcloud # Redis: PHP session locking + file locking cache. - REDIS_HOST=redis # Domain confirmed: cloud.kapala.org (mesh-only, *.kapala.org wildcard # cert already covers it). DNS + npm vhost are deploy-time steps, see # README Cutover checklist. - NEXTCLOUD_TRUSTED_DOMAINS=cloud.kapala.org # Behind npm@PIHA (TLS terminated there); without these Nextcloud # generates http:// links and login loops. - OVERWRITEPROTOCOL=https - OVERWRITEHOST=cloud.kapala.org - OVERWRITECLIURL=https://cloud.kapala.org # npm@PIHA as seen by this container: since Nextcloud also runs on # PIHA, npm reaches it as a local container over the docker bridge # (not PIHA's LAN IP) — see TRUSTED_PROXIES in env.example. Kept # configurable via .env in case the host ever changes. - TRUSTED_PROXIES=${TRUSTED_PROXIES} - PHP_MEMORY_LIMIT=512M - PHP_UPLOAD_LIMIT=4G - TZ=Europe/Warsaw volumes: # Whole app dir (code + config + user data in html/data). Runtime path # convention: /opt/homelab/data// on the chosen node's NVMe. - /opt/homelab/data/nextcloud/html:/var/www/html ports: # Bind to the node's LAN IP only, never 0.0.0.0 — npm@PIHA is the sole # entry point (LAN/Tailscale). Port 8220 free on PIHA per the 2026-06-30 # inventory. # TODO AT DEPLOY: reconfirm on the live host: ss -tlnp | grep 8220 - "${LAN_BIND_IP}:8220:80" healthcheck: test: ["CMD", "curl", "-fs", "--max-time", "5", "http://localhost:80/status.php"] interval: 30s timeout: 10s retries: 5 start_period: 120s # Background jobs (file scans, trash/versions cleanup, app jobs) — the # official image's dedicated cron entrypoint on the same code/data volume. cron: image: nextcloud:stable-apache container_name: nextcloud-cron restart: unless-stopped entrypoint: /cron.sh depends_on: db: condition: service_healthy redis: condition: service_healthy env_file: - .env environment: - POSTGRES_HOST=db - POSTGRES_DB=nextcloud - POSTGRES_USER=nextcloud - REDIS_HOST=redis - TZ=Europe/Warsaw volumes: - /opt/homelab/data/nextcloud/html:/var/www/html db: image: postgres:16-alpine container_name: nextcloud-db restart: unless-stopped env_file: - .env environment: - POSTGRES_DB=nextcloud - POSTGRES_USER=nextcloud - TZ=Europe/Warsaw volumes: - /opt/homelab/data/nextcloud/db:/var/lib/postgresql/data # No published port — only this stack talks to it. healthcheck: test: ["CMD-SHELL", "pg_isready -U nextcloud -d nextcloud"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: nextcloud-redis restart: unless-stopped # Pure cache/locking — no persistence needed, no published port. healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5