Tutorials

Nextcloud 30: migratie van Nextcloud AIO

Waarom AIO stilletjes faalt bij grotere instances, en hoe je netjes naar een handmatige stack overstapt.

Nextcloud All-in-One (AIO) is slim bedacht: één mastercontainer orchestreert alles, updates verlopen via een webinterface en je hoeft in principe geen enkel configuratiebestand aan te raken. Voor wie snel een werkende Nextcloud-instantie wil, is het de kortste weg. Maar zodra je instantie groeit — meer gebruikers, grotere bestanden, Redis-tuning, Collabora naast je bestaande reverse proxy — stuit je op grenzen die AIO bewust niet transparant maakt. Nextcloud 30, uitgebracht op 11 september 2024, is een goed moment voor die overstap: de occ-tooling is stabieler dan ooit, en de handmatige docker-compose-stack is goed gedocumenteerd.

Waarom AIO schaalt tot een bepaalde grens

Nextcloud AIO pakt het probleem van complexiteit aan door alles weg te abstraheren. Dat werkt goed zolang je binnen de aannames van de ontwikkelaars blijft. Die aannames zijn ruim, maar niet onbeperkt.

Het eerste pijnpunt is de uploadlimiet. AIO gebruikt een standaard PHP-limiet van 10 GB per bestand. Dat is ruim voor foto's en documenten, maar te krap voor VM-snapshots, grote videoprojecten of offsite back-ups. In theorie pas je dit aan via omgevingsvariabelen als NEXTCLOUD_MAX_UPLOAD_LIMIT, maar meerdere gebruikers rapporteren dat die variabelen niet consequent doorwerken in alle containers die AIO beheert. Het resultaat: een instelling die je in de AIO-interface aanpast, maar die niet terug te vinden is in de actieve PHP-configuratie.

Het tweede knelpunt is performantie. Gebruikers met gigabit-verbindingen en snel lokaal opslag melden transfersnelheden die niet verder komen dan 15 MB/s. AIO voegt een extra bufferlaag toe via de mastercontainer, wat bij kleinere bestanden acceptabel is maar bij bulkoverdrachten merkbaar pijn doet. In een handmatige stack communiceren nginx, php-fpm en MariaDB rechtstreeks met elkaar, zonder tussenpersoon.

Het derde probleem is specifiek voor VPS-omgevingen: AIO vereist bepaalde kernelfuncties en namespaces die op gedeelde VPS-aanbieders soms niet beschikbaar zijn. Op systemen met een /proc/user_beancounters-bestand (OpenVZ-gebaseerde VPS) breekt AIO snel door de numproc-limiet omdat de mastercontainer zelf al meerdere subprocessen forkt voor beheer.

Tot slot: als je al een eigen nginx of Caddy reverse proxy draait, wil je niet dat AIO zijn eigen proxylaag bovenop plaatst. AIO heeft hier een "Reverse Proxy"-modus voor, maar die vergt specifieke aanpassingen die slecht gedocumenteerd zijn en met elke update opnieuw kunnen verschuiven.

AIO is niet kapot — het is een bewuste afruil. Je ruilt flexibiliteit in voor gemak. Die afruil loont totdat de instantie groter wordt dan de aannames van het ontwerp.

Voorbereiding: back-up en export

Zet voor alles maintenance-mode aan. Dat vergrendelt actieve sessies en voorkomt dat bestanden wijzigen terwijl je aan het migreren bent.

docker exec --user www-data nextcloud-aio-nextcloud \
  php occ maintenance:mode --on

Maak vervolgens een volledige back-up via de AIO-interface (Backup & Restore) of rechtstreeks via het bestandssysteem. De data ligt standaard in een Docker-volume; achterhaal het pad met:

docker volume inspect nextcloud_aio_nextcloud_data

Kopieer het volledige datavolume naar een tijdelijke locatie. Doe hetzelfde voor het databasevolume:

docker volume inspect nextcloud_aio_database

Exporteer de database als aanvullende zekerheid:

docker exec nextcloud-aio-database \
  mysqldump --single-transaction -u nextcloud -p'JOUW_DB_WACHTWOORD' nextcloud \
  > /backup/nextcloud-$(date +%Y%m%d).sql

Noteer ook de huidige versie van Nextcloud zodat je weet met welke versie je de nieuwe stack start:

docker exec --user www-data nextcloud-aio-nextcloud \
  php occ status

Stop daarna alle AIO-containers, maar verwijder de volumes nog niet:

docker stop nextcloud-aio-nextcloud nextcloud-aio-database \
  nextcloud-aio-redis nextcloud-aio-caddy

De handmatige docker-compose-stack

Een handmatige stack bestaat uit vier kernservices: MariaDB, Redis, de Nextcloud-applicatiecontainer (fpm-variant) en nginx als webserver. Collabora of OnlyOffice voeg je daarna als vijfde service toe.

Stack-overzicht

ServiceImageRol
dbmariadb:11Relationele database
redisredis:7-alpineMemcache + file-locking
appnextcloud:30-fpm-alpinePHP-FPM applicatieserver
webnginx:alpineWebserver / static assets
collaboracollabora/code:latestOnline document-editor
cronnextcloud:30-fpm-alpineAchtergrondtaken (5 min)

Hieronder de kern van het docker-compose.yml-bestand. Vervang JOUW_*-waarden door je eigen credentials:

version: '3.9'

services:
  db:
    image: mariadb:11
    restart: always
    command: >
      --transaction-isolation=READ-COMMITTED
      --binlog-format=ROW
      --innodb-buffer-pool-size=512M
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: JOUW_ROOT_WACHTWOORD
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: JOUW_DB_WACHTWOORD

  redis:
    image: redis:7-alpine
    restart: always
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru

  app:
    image: nextcloud:30-fpm-alpine
    restart: always
    depends_on:
      - db
      - redis
    volumes:
      - nc_data:/var/www/html/data
      - nc_app:/var/www/html
    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: JOUW_DB_WACHTWOORD
      REDIS_HOST: redis
      NEXTCLOUD_TRUSTED_DOMAINS: "cloud.jouwdomein.be"
      PHP_MEMORY_LIMIT: 1G
      PHP_UPLOAD_LIMIT: 50G

  web:
    image: nginx:alpine
    restart: always
    depends_on:
      - app
    ports:
      - "8080:80"
    volumes:
      - nc_app:/var/www/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

  cron:
    image: nextcloud:30-fpm-alpine
    restart: always
    depends_on:
      - db
      - redis
    volumes:
      - nc_data:/var/www/html/data
      - nc_app:/var/www/html
    entrypoint: /cron.sh

  collabora:
    image: collabora/code:latest
    restart: always
    environment:
      aliasgroup1: "https://cloud.jouwdomein.be:443"
      DONT_GEN_SSL_CERT: "YES"
      extra_params: "--o:ssl.enable=false --o:ssl.termination=true"
    expose:
      - "9980"

volumes:
  db_data:
  nc_data:
  nc_app:

De MariaDB-vlag --transaction-isolation=READ-COMMITTED is niet optioneel: zonder deze instelling krijg je bij gelijktijdige uploads database-deadlocks. De --innodb-buffer-pool-size stel je in op circa 50-70% van het beschikbare RAM op de databasehost. Bij 2 GB RAM is 512 MB een conservatieve maar veilige keuze; bij 8 GB mag dit naar 4 GB.

Redis configureeer je met een maxmemory-policy om te voorkomen dat het geheugengebruik onbeperkt groeit. allkeys-lru is de aanbevolen waarde voor Nextcloud: minst recent gebruikte cachevermeldingen worden als eerste verwijderd zodra de limiet bereikt is.

De cron-service is een aparte container die het /cron.sh-script van de officiële image elke vijf minuten uitvoert. Dit vervangt de crontab die AIO intern beheerde.

Data migreren zonder dataverlies

Breng de volumes van AIO over naar de nieuwe stack. Dat doe je het eenvoudigst door de data rechtstreeks te kopiëren vanuit de Docker-volumemappen naar de nieuwe volumes. Start daarvoor de nieuwe stack éénmalig zonder de app-service om de volumes aan te maken:

docker compose up -d db redis
docker compose up --no-start app

Kopieer vervolgens de bestandsdata vanuit het AIO-volume naar het nieuwe volume. Achterhaal de padnaam van het nieuwe volume:

docker volume inspect nextcloud_nc_data
# Kijk naar "Mountpoint"

Kopieer de inhoud:

rsync -av --progress \
  /var/lib/docker/volumes/nextcloud_aio_nextcloud_data/_data/ \
  /var/lib/docker/volumes/nextcloud_nc_data/_data/

Importeer daarna de eerder gemaakte SQL-dump in de nieuwe MariaDB-container:

docker compose exec -T db mysql \
  -u nextcloud -p'JOUW_DB_WACHTWOORD' nextcloud \
  < /backup/nextcloud-20240924.sql

Pas de config.php aan. De nieuwe stack heeft andere hostnamen dan AIO (AIO gebruikte interne namen als nextcloud-aio-database; jouw compose gebruikt db). Open de configuratie:

docker compose exec app \
  vi /var/www/html/config/config.php

Controleer en pas aan: dbhost moet db zijn, redis host redis, en trusted_domains moet je eigen domein bevatten. Zet ook de memcache-instellingen als die nog niet aanwezig zijn:

'memcache.local' => '\OC\Memcache\APCu',
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => [
    'host' => 'redis',
    'port' => 6379,
],

Start de volledige stack en voer de database-upgrade uit:

docker compose up -d

docker compose exec --user www-data app \
  php occ upgrade

Schakel maintenance-mode uit en herscan de bestanden zodat de Nextcloud-index klopt met de gekopieerde data:

docker compose exec --user www-data app \
  php occ maintenance:mode --off

docker compose exec --user www-data app \
  php occ files:scan --all

De files:scan-stap kan bij grote instanties lang duren — reken op enkele minuten per 10.000 bestanden. Je kunt de scan per gebruiker uitvoeren als je de downtime wilt spreiden:

docker compose exec --user www-data app \
  php occ files:scan gebruikersnaam

Controleer tot slot de systeemstatus op waarschuwingen:

docker compose exec --user www-data app \
  php occ status

docker compose exec --user www-data app \
  php occ check

Veelgestelde vragen

Kan ik later nog terug naar AIO?
Technisch wel, maar het is bewerkelijk. AIO verwacht zijn eigen volumestructuur en intern naamgevingschema. Een terugmigratie verloopt via dezelfde stappen maar in omgekeerde richting, en vereist opnieuw een volledige back-up als startpunt. In de praktijk gaat niemand terug.
Moet ik Collabora of OnlyOffice kiezen?
Collabora (CODE) is de open-source variant van LibreOffice Online en standaard geïntegreerd in Nextcloud Hub. OnlyOffice biedt een interface die dichter bij Microsoft 365 aanleunt en heeft een gratis community-editie zonder gebruikerslimiet in de Docker-versie (wel een 20-verbindingen-limiet per instantie). Beide zijn via de Nextcloud-app-store te configureren; je wijst de app naar het interne Docker-hostname (collabora of onlyoffice).
Wat met updates? AIO deed dat automatisch.
In een handmatige stack update je door het image-label in docker-compose.yml aan te passen (nextcloud:30-fpm-alpinenextcloud:31-fpm-alpine) en vervolgens docker compose pull && docker compose up -d uit te voeren, gevolgd door occ upgrade. Dat kost vijf minuten en geeft je volledige controle over timing.
Hoe monitoor ik de stack?
De eenvoudigste aanpak is docker compose logs -f app voor real-time logging. Voor een structurelere oplossing koppel je Nextcloud aan een Prometheus-exporter via de Nextcloud Exporter-app en draai je Grafana in een aparte compose-stack. AIO had ook monitoring, maar die was niet exporteerbaar naar externe systemen.
Nextcloud 31 is al uit — kan ik direct naar 31 migreren?
Ja, maar doe het stap voor stap: migreer eerst naar 30 (de versie waarvoor je back-up is gemaakt), laat occ upgrade slagen, en upgrade dan via hetzelfde process naar 31. Nextcloud ondersteunt geen slagen-overstijgende upgrades vanuit één stap.