SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-22

Installer Nextcloud sur un VPS avec Docker et TLS

Déployez Nextcloud sur votre VPS avec Docker Compose, Postgres, Redis et nginx, puis sécurisez vos sauvegardes et mises à niveau pour restaurer vos données.

Ce que vous allez réellement mettre en place

Ce guide exécute Nextcloud sur un VPS avec Docker Compose, place TLS Let’s Encrypt devant l’instance et configure une sauvegarde qui peut réellement être restaurée. Quatre conteneurs et un proxy : l’image officielle nextcloud à l’écoute sur loopback, Postgres qui contient toutes les métadonnées des fichiers, Redis qui gère les verrous de fichiers, une seconde instance de l’image Nextcloud qui n’exécute que la boucle cron, et nginx sur l’hôte qui assure la terminaison TLS devant l’ensemble. L’installation prend vingt minutes, mais ce n’est pas la partie importante. Deux décisions prises pendant la première heure déterminent si vous aurez encore vos fichiers dans un an : utiliser une vraie base de données plutôt que SQLite, et créer une sauvegarde qui capture le répertoire de données, la base de données et config.php comme un ensemble cohérent.

Ces instructions partent du principe que vous utilisez Ubuntu 24.04 LTS ou Debian 13, Docker Engine avec le plugin Compose v2 installé depuis le dépôt officiel de Docker, ainsi qu’un enregistrement DNS A (et AAAA si vous avez IPv6) pointant déjà cloud.example.com vers le VPS. Vous devez contrôler le serveur. Il est impossible d’assurer la terminaison TLS et d’effectuer un dump de base de données sur le SaaS d’un tiers.

Dimensionnement : ce qui consomme réellement la mémoire

La consommation mémoire de Nextcloud dépend principalement de trois éléments, et aucun ne correspond réellement à « Nextcloud ».

Les workers PHP. L’image -apache traite chaque requête simultanée avec un processus worker qui contient un interpréteur PHP. Chaque worker peut atteindre PHP_MEMORY_LIMIT avant que PHP ne tue la requête. Dans le pire des cas, la mémoire résidente est approximativement égale au nombre de requêtes simultanées multiplié par la limite mémoire. Un client de synchronisation desktop ouvre plusieurs connexions parallèles par utilisateur. C’est donc la simultanéité, et non le nombre d’utilisateurs, qui définit la limite.

La base de données. Postgres crée un backend par connexion et conserve ses shared buffers en mémoire. Son working set dépend du nombre de fichiers, et non du nombre d’octets : oc_filecache contient une ligne par fichier et par utilisateur. Cent mille petits fichiers sollicitent davantage la base de données que cent gros fichiers.

La génération des aperçus. La génération d’une miniature décode l’image source en mémoire dans sa résolution complète. La génération des aperçus vidéo lance ffmpeg. Exécuter occ preview:generate-all répète ce pic de consommation, sans interruption, et constitue la cause la plus fréquente d’un déclenchement de l’OOM killer sur un petit VPS.

Redis est relativement peu coûteux. Tout service ajouté ultérieurement, comme Collabora, la recherche full-text ou un antivirus, est un service résident distinct avec sa propre empreinte mémoire. Il faut l’inclure dans le dimensionnement avant de l’activer.

Si la RAM est limitée, vous pouvez réduire PHP_MEMORY_LIMIT, plafonner preview_max_x / preview_max_y / preview_max_filesize_image, limiter enabledPreviewProviders aux formats que vous consultez réellement, puis définir trashbin_retention_obligation et versions_retention_obligation afin que le répertoire de données n’atteigne pas silencieusement plusieurs fois la taille de vos fichiers. Ajoutez un fichier swap. Le swap est lent, mais un OOM kill au milieu d’une mise à niveau est pire.

Pourquoi SQLite ne convient pas

Nextcloud prend en charge SQLite et l’image officielle l’utilisera sans problème. Ne l’utilisez pas. SQLite sérialise les écritures avec un verrou portant sur toute la base de données : un seul processus peut écrire à la fois dans l’ensemble du fichier. Nextcloud écrit en permanence : verrous de fichiers, lignes d’activité, entrées de cache et état des tâches. Un seul client de bureau qui synchronise une arborescence de répertoires génère de nombreuses requêtes parallèles. Dans ce contexte, vous obtenez SQLSTATE[HY000]: General error: 5 database is locked et des erreurs HTTP 500. La panne survient précisément lorsque l’instance commence à devenir utile.

Une conversion ultérieure est possible avec occ db:convert-type, mais il s’agit d’une migration longue et indivisible sur un jeu de données actif. Commencez avec Postgres ou MariaDB.

Le fichier Compose

Placez ceci dans /srv/nextcloud/compose.yaml, avec les secrets dans un fichier .env situé dans le même répertoire et avec le mode 600.

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    volumes:
      - db:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${DB_PASSWORD}

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}

  app:
    image: nextcloud:31-apache
    restart: unless-stopped
    depends_on: [db, redis]
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - html:/var/www/html
      - /srv/nextcloud/data:/var/www/html/data
    environment:
      POSTGRES_HOST: db
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      REDIS_HOST: redis
      REDIS_HOST_PASSWORD: ${REDIS_PASSWORD}
      NEXTCLOUD_ADMIN_USER: admin
      NEXTCLOUD_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.com
      TRUSTED_PROXIES: 172.16.0.0/12
      OVERWRITEPROTOCOL: https
      OVERWRITECLIURL: https://cloud.example.com
      APACHE_DISABLE_REWRITE_IP: "1"
      PHP_MEMORY_LIMIT: 512M
      PHP_UPLOAD_LIMIT: 10G

  cron:
    image: nextcloud:31-apache
    restart: unless-stopped
    entrypoint: /cron.sh
    depends_on: [db, redis]
    volumes:
      - html:/var/www/html
      - /srv/nextcloud/data:/var/www/html/data

volumes:
  db:
  html:

Figez le tag majeur et vérifiez celui qui est actuellement disponible sur Docker Hub avant de copier 31 tel quel. latest vous fera passer une limite de version majeure lors d’un prochain docker compose pull, ce que Nextcloud ne prend pas en charge.

Le répertoire de données est volontairement un bind mount, et non un named volume : un chemin que vous pouvez indiquer directement à un outil de sauvegarde est plus utile qu’une organisation parfaite. Créez-le avec l’UID www-data de l’image et les permissions exigées par Nextcloud :

sudo mkdir -p /srv/nextcloud/data
sudo chown -R 33:33 /srv/nextcloud/data
sudo chmod 0770 /srv/nextcloud/data

Notez la publication du port : 127.0.0.1:8080:80. Docker publie les ports en écrivant des règles DNAT évaluées avant que le paquet n’atteigne la chaîne INPUT de ufw. Un simple 8080:80 expose donc un Nextcloud non chiffré sur Internet, quelles que soient les règles de ufw. La liaison à loopback le maintient hors de l’interface publique. Le pare-feu doit alors uniquement autoriser le proxy. Si vous préférez ne pas laisser SSH ouvert à tout Internet, accéder au VPS via un VPN WireGuard auto-hébergé vous permet de supprimer complètement le port 22 des règles publiques :

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Démarrez-le avec docker compose up -d, puis surveillez docker compose logs -f app. Lors du premier démarrage, l’ensemble de l’arborescence de l’application est copié dans le volume et l’installeur est exécuté. Le conteneur ne répond à aucune requête avant la fin de cette opération.

TLS et le reverse proxy

Installez nginx et certbot depuis les dépôts de la distribution, créez un bloc server simple sur le port 80 avec le bon server_name, puis laissez certbot le réécrire. Le fonctionnement du challenge HTTP-01, du timer de renouvellement et des cas d’échec est décrit en détail dans émettre des certificats Let's Encrypt avec certbot et nginx sur Ubuntu 24.04 :

sudo apt install nginx certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.example.com

Certbot ajoute les lignes ssl_certificate ainsi que la redirection :80:443. Il installe également un timer systemd qui renouvelle le certificat de 90 jours. Vérifiez sa présence avec systemctl list-timers | grep certbot. Un timer de renouvellement qui n’a jamais été activé est une échéance de 90 jours.

Le bloc proxy lui-même :

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name cloud.example.com;

    # certbot manages ssl_certificate / ssl_certificate_key here

    add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;

    client_max_body_size 10G;
    client_body_timeout 300s;

    location = /.well-known/carddav { return 301 /remote.php/dav; }
    location = /.well-known/caldav  { return 301 /remote.php/dav; }

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host  $host;
        proxy_request_buffering off;
        proxy_buffering off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Avec nginx 1.25 et les versions ultérieures, ajoutez http2 on;. Ubuntu 24.04 fournit une version plus ancienne, dans laquelle l’équivalent est listen 443 ssl http2;. nginx -t vous indiquera laquelle de ces directives votre build accepte.

client_max_body_size et les délais d’attente de lecture longs empêchent les gros uploads d’échouer en cours de transfert. proxy_request_buffering off transmet l’upload en flux au lieu de stocker d’abord tout le fichier sur le disque du proxy.

nginx sur l’hôte est la solution la plus simple pour une seule application. Si Nextcloud doit partager le VPS avec d’autres conteneurs, exécuter Traefik comme reverse proxy Docker Compose pour plusieurs applications déplace le routage et l’émission des certificats dans les labels des conteneurs. Les mêmes problèmes liés à client_max_body_size et aux délais d’attente se retrouvent alors dans les paramètres de middleware et de transport.

trusted_proxies et overwriteprotocol

C’est ici que la plupart des instances Nextcloud auto-hébergées rencontrent des problèmes, avec des symptômes qui semblent sans rapport avec la cause.

X-Forwarded-Proto: https n’est pris en compte que si la requête arrive depuis une adresse répertoriée dans trusted_proxies. Dans le cas contraire, Nextcloud considère que la requête utilise HTTP sans chiffrement et génère des URL en http:// ; le proxy les redirige vers HTTPS ; le navigateur suit la redirection ; Nextcloud génère de nouveau http://. C’est la boucle de redirection. OVERWRITEPROTOCOL: https impose le schéma dans tous les cas.

Le piège avec TRUSTED_PROXIES est que l’adresse vue par Nextcloud n’est pas 127.0.0.1. nginx s’exécute sur l’hôte et se connecte à un port publié. Le conteneur voit donc la passerelle du bridge Docker, qui se trouve dans quelque chose comme 172.x. Trouvez le sous-réseau réel :

docker network inspect nextcloud_default \
  -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}'

Ajoutez ce CIDR, ou le 172.16.0.0/12 qui le couvre, dans TRUSTED_PROXIES. Si la plage est trop large, n’importe quel client peut usurper X-Forwarded-For. Si elle est incorrecte, toutes les connexions semblent provenir de l’adresse de la passerelle, la protection contre les attaques par force brute bloque toute votre instance d’un coup, et la vue d’ensemble de l’administration affiche « La configuration des en-têtes du reverse proxy est incorrecte ou vous accédez à Nextcloud depuis un proxy de confiance. »

OVERWRITECLIURL est nécessaire pour le conteneur cron, qui ne reçoit aucune requête entrante permettant de déduire un nom d’hôte. Sans ce paramètre, les tâches en arrière-plan génèrent des liens vers localhost et les notifications par e-mail contiennent des URL inutilisables.

Tâches en arrière-plan : cron, pas AJAX

Le planificateur de tâches par défaut de Nextcloud utilise AJAX : les tâches s’exécutent lorsqu’une personne charge une page. Personne ne navigue à 04:00. L’expiration de la corbeille, le nettoyage des versions, la génération des aperçus et les nouvelles tentatives fédérées restent donc en attente. Le premier symptôme est un répertoire de données qui ne cesse de grossir. Le service cron ci-dessus exécute la boucle officielle /cron.sh sur les mêmes volumes. Indiquez à Nextcloud de l’utiliser :

docker compose exec -u www-data app php occ background:cron

Chaque commande occ suit cette structure : docker compose exec -u www-data app php occ <command>. Il est utile de lui créer un alias.

Sauvegardes : trois éléments, ou rien

Une sauvegarde du seul système de fichiers restaure une instance inutilisable. Le répertoire de données contient les octets ; Postgres contient le cache des fichiers, les partages, les utilisateurs et l’état de l’application ; config.php contient les identifiants de la base de données, l’ID de l’instance et le sel du mot de passe. Restaurez les fichiers sans la base de données et Nextcloud ne peut pas les voir. Restaurez la base de données sans config.php et elle ne peut pas ouvrir la base de données. Restaurez une ancienne base de données avec un répertoire de données plus récent et vous obtenez des partages qui pointent vers des fichiers déplacés.

Sauvegardez les trois éléments sur une instance mise en maintenance :

#!/usr/bin/env bash
set -euo pipefail
cd /srv/nextcloud
DEST="/var/backups/nextcloud/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$DEST"

occ() { docker compose exec -T -u www-data app php occ "$@"; }

occ maintenance:mode --on
trap 'occ maintenance:mode --off' EXIT

docker compose exec -T db \
  pg_dump -U nextcloud --clean --if-exists nextcloud | gzip > "$DEST/db.sql.gz"

docker compose exec -T app \
  tar -C /var/www/html -cf - config custom_apps themes > "$DEST/app.tar"

rsync -a --delete /srv/nextcloud/data/ /var/backups/nextcloud/data/

Le mode maintenance garantit que l’export de la base de données et la copie des fichiers correspondent. Si vous l’ignorez, vous finirez par capturer une base de données qui référence un fichier que rsync n’avait pas encore copié. Notez que le script conserve des exports de base de données horodatés, mais une seule copie miroir tournante du répertoire de données : rsync --delete l’écrase à chaque exécution. Seul l’export le plus récent correspond donc à la copie des fichiers.

Transférez ensuite la sauvegarde hors du serveur. Une sauvegarde stockée sur le même VPS que les données qu’elle protège est une copie, pas une sauvegarde. restic vers un stockage objet ou un second hôte est la solution habituelle. Sa déduplication traite bien mieux le répertoire de données qu’une archive tar créée chaque nuit. La configuration complète, de l’initialisation du dépôt au minuteur nocturne et au test de restauration, est décrite dans sauvegardes de VPS hors serveur avec restic.

La restauration ne consiste pas simplement à effectuer les étapes en sens inverse. Une stack démarrée à neuf exécute l’installateur et écrit un nouveau config.php, un nouvel ID d’instance et un nouveau sel de mot de passe. L’import de l’export par-dessus cette nouvelle identité laisse des sessions et des jetons de partage inutilisables. Restaurez d’abord l’ancienne identité, dans cet ordre :

docker compose up -d && docker compose stop app cron    # create the volumes, then halt the app
sudo rsync -a --delete /var/backups/nextcloud/data/ /srv/nextcloud/data/
docker compose run --rm -T --entrypoint "" app \
  tar -C /var/www/html -xf - < app.tar                  # the original config.php returns
gunzip -c db.sql.gz | docker compose exec -T db psql -U nextcloud -d nextcloud
docker compose start app cron
docker compose exec -T -u www-data app php occ maintenance:mode --off
docker compose exec -T -u www-data app php occ files:scan --all

files:scan rapproche le cache des fichiers de leur présence réelle sur le disque. Répétez cette procédure une fois sur un VPS de secours avant d’en avoir besoin. La même séparation entre les octets sur le disque et les métadonnées dans Postgres concerne toutes les autres applications de ce type. C’est pourquoi une sauvegarde Immich qui capture la bibliothèque mais pas la base de données restaure une timeline vide.

Mises à niveau : une version majeure à la fois

Nextcloud prend en charge la mise à niveau d’une seule version majeure à la fois. Passer directement de 29 à 31 échoue sans traitement propre, avec Exception: Updates between multiple major versions and downgrades are unsupported., et vous laisse en mode maintenance.

Pour mettre à niveau le déploiement Docker : effectuez une sauvegarde, remplacez le tag de 31 par 32 dans les services app et cron, puis exécutez docker compose pull && docker compose up -d, suivi de docker compose logs -f app. L’entrypoint de l’image détecte que le code est plus récent que les données existantes et exécute lui-même occ upgrade. Ne l’interrompez pas. Lorsque les journaux cessent d’afficher des messages, exécutez docker compose exec -u www-data app php occ status, puis vérifiez versionstring et que les applications sont de nouveau activées.

Deux règles vous éviteront des problèmes : augmentez d’une version majeure, vérifiez, puis augmentez la suivante. Ne modifiez jamais le tag du service app sans modifier également cron pour qu’ils correspondent. Deux versions différentes de Nextcloud utilisant une seule base de données peuvent entraîner une corruption.

Les erreurs que vous rencontrerez réellement

« Your data directory is readable by other users. Please change the permissions to 0770. » Le répertoire monté par bind possède les bits de lecture pour le groupe ou pour les autres utilisateurs. sudo chmod 0770 /srv/nextcloud/data et sudo chown -R 33:33 /srv/nextcloud/data.

« Your data directory is invalid. Ensure there is a file called .ocdata in the root. » Le bind mount pointe vers un emplacement que Nextcloud n’a jamais initialisé, vers un chemin erroné ou vers un répertoire vide nouvellement associé à une instance fonctionnelle. Vérifiez que le chemin de l’hôte correspond à la ligne du volume.

« Access through untrusted domain. » Le nom d’hôte de la requête ne figure pas dans trusted_domains. NEXTCLOUD_TRUSTED_DOMAINS ne s’applique qu’à la première installation ; définissez ensuite la valeur à chaud avec : occ config:system:set trusted_domains 1 --value=cloud.example.com.

502 Bad Gateway, avec connect() failed (111: Connection refused) while connecting to upstream dans /var/log/nginx/error.log. nginx n’a rien trouvé sur 127.0.0.1:8080. Le conteneur est peut-être encore en cours d’initialisation (vérifiez docker compose logs app), il s’est peut-être arrêté (docker compose ps), ou la ligne de publication ne correspond pas au port proxy_pass. Confirmez avec ss -ltnp | grep 8080.

Une boucle de redirection ou des avertissements « insecure » dans la vue d’ensemble de l’administration. OVERWRITEPROTOCOL: https est absent ou TRUSTED_PROXIES ne contient pas le sous-réseau de la passerelle Docker. Consultez la section précédente consacrée au proxy.

LockedException: "files/..." is locked. Avec REDIS_HOST défini, l’image configure Redis comme backend de verrouillage et les verrous obsolètes sont rares. Sans ce paramètre, les verrous sont stockés dans la table de base de données oc_file_locks et une requête interrompue pendant une écriture laisse des lignes résiduelles. Vérifiez que Redis est réellement utilisé : occ config:system:get memcache.locking doit retourner la classe Redis, avant de supprimer manuellement des lignes de verrouillage.

« The PHP memory limit is below the recommended value of 512MB. » Augmentez PHP_MEMORY_LIMIT, puis recréez le conteneur. Tenez compte de l’effet de cette modification sur votre plafond maximal en cas de forte charge.

Ce qui casse à grande échelle

Le premier obstacle apparaît lorsque le répertoire de données dépasse la taille du volume. Agrandir un volume sur un VPS consiste à redimensionner le volume, puis à agrandir le système de fichiers. Cette opération est bien moins pénible lorsqu’elle est planifiée que lorsque le volume est plein à 100 %. Configurez dès maintenant une alerte sur l’utilisation du disque.

Le deuxième obstacle est oc_filecache. Les listes de fichiers et les analyses de synchronisation ralentissent à mesure que le nombre de lignes augmente. La solution relève de la base de données : placez Postgres sur un stockage rapide, laissez-le utiliser suffisamment de mémoire partagée et purgez la corbeille et les versions avec les paramètres de rétention au lieu de les laisser s’accumuler indéfiniment.

Le troisième obstacle est la génération des aperçus, qui entre en concurrence avec les autres tâches. Sur une petite machine, limitez les fournisseurs d’aperçus et n’exécutez jamais occ preview:generate-all pendant les heures de travail. Si l’essentiel de ce que vous stockez provient de la pellicule photo d’un téléphone, cette génération de miniatures doit plutôt être confiée à un serveur photo dédié. L’article Comparaison de PhotoPrism et Immich sur la RAM, les applications mobiles et les commandes de sauvegarde détaille le coût de chacune de ces solutions à côté d’un serveur Nextcloud.

Au-delà, la réponse honnête est que les services supplémentaires veulent leur propre machine. Collabora et la recherche plein texte sont des services résidents distincts, avec leurs propres besoins en mémoire. Les installer sur la machine qui contient également votre seule copie des fichiers élargit le domaine de panne sans apporter de bénéfice. Si l’édition de documents dans le navigateur est le service supplémentaire recherché, les besoins minimaux en RAM et les limites de connexions du fournisseur qui distinguent OnlyOffice de Collabora déterminent si un VPS de 2 à 4 GB peut réellement l’exécuter. Transférez le stockage des fichiers vers un stockage primaire compatible S3 lorsque le volume n’a plus la taille adaptée. Notez que cela rend les sauvegardes plus difficiles, et non plus simples : la base de données contient toujours les métadonnées et doit être sauvegardée en même temps que le bucket.

Une fois que l’instance sert de vrais utilisateurs, placez Uptime Kuma devant elle afin d’être informé d’une interruption avant les clients de synchronisation. Un cloud privé s’associe bien à votre propre serveur de messagerie. Si vous préférez ne pas relier les services manuellement, Cloudron, CasaOS et Coolify comparent les plateformes qui s’en chargent pour vous. Si un moteur de recherche auto-hébergé figure ensuite sur votre liste, attendez-vous à un problème d’une autre nature que ceux décrits ci-dessus : les erreurs 429 de SearXNG proviennent soit de son propre limiteur de débit, soit du blocage de l’adresse IP de votre VPS par les moteurs en amont. Seuls les journaux permettent de déterminer lequel.

FAQ

Puis-je exécuter Nextcloud avec SQLite au lieu de Postgres ?

Oui. L’image officielle l’autorise. Cependant, un seul client de synchronisation de bureau qui envoie des requêtes en parallèle provoquera SQLSTATE[HY000]: General error: 5 database is locked et des erreurs HTTP 500. SQLite verrouille les écritures à l’échelle de la base de données. Or Nextcloud écrit en permanence : verrous de fichiers, lignes d’activité et état des tâches. Commencez avec Postgres ou MariaDB. occ db:convert-type existe, mais il s’agit d’une migration longue et tout ou rien sur des données en production.

De combien de RAM un VPS Nextcloud a-t-il réellement besoin ?

Dimensionnez la RAM selon la concurrence, et non selon le nombre d’utilisateurs. La mémoire résidente maximale correspond approximativement au nombre de requêtes simultanées multiplié par PHP_MEMORY_LIMIT, auquel il faut ajouter les buffers partagés de Postgres, un backend par connexion et les pointes de consommation liées à la génération des aperçus. Une machine de 2 GB peut héberger une petite instance familiale si vous limitez les aperçus et ajoutez du swap. Avec Collabora ou la recherche plein texte, prévoyez un second ensemble de services résidents.

Pourquoi les téléversements volumineux échouent-ils derrière le reverse proxy nginx ?

Deux paramètres du proxy l’expliquent généralement : client_max_body_size, qui conserve sa valeur par défaut de 1 MB, tronque la requête, et des valeurs trop faibles pour proxy_read_timeout / proxy_send_timeout interrompent les transferts longs en cours de route. Définissez ces deux paramètres avec des valeurs suffisamment élevées, activez proxy_request_buffering off pour diffuser les données au lieu de les mettre en spool, puis augmentez PHP_UPLOAD_LIMIT sur le conteneur de l’application pour l’aligner.

Pourquoi Nextcloud redirige-t-il en boucle ou signale-t-il un problème avec le reverse proxy ?

Le conteneur ne voit pas nginx à 127.0.0.1. Il voit la gateway du bridge Docker, quelque part dans 172.x. Lorsque cette adresse ne figure pas dans TRUSTED_PROXIES, l’en-tête X-Forwarded-Proto: https est ignoré. Nextcloud génère alors des URL http://, que le proxy redirige à nouveau. Définissez TRUSTED_PROXIES avec le vrai subnet du bridge et fixez OVERWRITEPROTOCOL: https.

Puis-je mettre à niveau Nextcloud directement de 29 à 31 ?

Non. Nextcloud prend en charge une seule version majeure par mise à niveau. Un saut de version s’arrête avec Updates between multiple major versions and downgrades are unsupported. et laisse l’instance en mode maintenance. Effectuez une sauvegarde, augmentez d’une version majeure le tag sur les services app et cron, docker compose pull && docker compose up -d, vérifiez avec occ status, puis recommencez.