SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Nextcloud на VPS: Docker, TLS і резервні копії

Запустіть Nextcloud на VPS через Docker Compose: Postgres, Redis і nginx з TLS від Let's Encrypt, а також узгоджені резервні копії для відновлення даних.

Що саме ви створюєте

Цей посібник описує запуск Nextcloud на VPS за допомогою Docker Compose, налаштування TLS від Let's Encrypt перед ним і створення резервної копії, яку можна відновити. Схема складається з чотирьох контейнерів і reverse proxy: офіційний образ nextcloud слухає loopback, Postgres зберігає всі дані про метадані файлів, Redis зберігає блокування файлів, другий контейнер з образом Nextcloud виконує лише цикл cron, а nginx на хості завершує TLS перед усіма цими компонентами. Саме встановлення займає двадцять хвилин і не є найважливішою частиною. Два рішення, прийняті протягом першої години, визначають, чи матимете ви доступ до своїх файлів через рік: використання повноцінної бази даних замість SQLite та резервної копії, яка зберігає каталог даних, базу даних і config.php як один узгоджений набір.

Передбачається, що ви використовуєте Ubuntu 24.04 LTS або Debian 13, Docker Engine із плагіном Compose v2, встановленим із власного репозиторію Docker, а також DNS-запис типу AAAAA, якщо у вас є IPv6), який уже вказує cloud.example.com на VPS. Для цього потрібен сервер, яким ви керуєте. Неможливо виконати TLS termination і дамп бази даних у чужому SaaS.

Sizing: що насправді споживає пам’ять

Використання пам’яті Nextcloud визначають три основні компоненти. Жоден із них не є власне «Nextcloud».

PHP workers. Образ -apache обробляє кожен паралельний запит окремим worker process, який містить PHP interpreter. Кожен worker може використати до PHP_MEMORY_LIMIT, після чого PHP завершить запит. Орієнтовний worst-case обсяг resident memory дорівнює добутку кількості паралельних запитів на ліміт пам’яті. Desktop sync client відкриває кілька паралельних з’єднань для кожного користувача. Верхню межу визначає concurrency, а не кількість користувачів.

Database. Postgres створює окремий backend для кожного з’єднання та зберігає shared buffers у пам’яті. Його working set залежить від кількості файлів, а не від кількості байтів: oc_filecache містить один рядок для кожного файлу кожного користувача. Сто тисяч малих файлів створюють більше навантаження на database, ніж сто великих файлів.

Preview generation. Під час створення thumbnail вихідне зображення декодується в пам’ять у повній роздільній здатності. Для відеопрев’ю використовуються зовнішні виклики до ffmpeg. Запуск occ preview:generate-all повторює цей пік навантаження один за одним і є найпоширенішим способом довести невеликий VPS до спрацювання OOM killer.

Redis порівняно мало споживає пам’яті. Усе, що ви додасте пізніше, зокрема Collabora, full-text search або antivirus scanner, є окремим resident service із власним обсягом пам’яті. Його потрібно врахувати в плані sizing до ввімкнення.

Якщо RAM мало, змініть такі параметри: зменште PHP_MEMORY_LIMIT, обмежте preview_max_x / preview_max_y / preview_max_filesize_image, скоротіть enabledPreviewProviders до форматів, які ви фактично переглядаєте, і налаштуйте trashbin_retention_obligation та versions_retention_obligation, щоб data directory непомітно не зростав у кілька разів порівняно з розміром файлів. Додайте swap file. Swap працює повільно, але завершення процесу через OOM killer під час оновлення має гірші наслідки.

Чому SQLite створює проблеми

Nextcloud підтримує SQLite, і офіційний image без проблем використовуватиме його. Не використовуйте SQLite. SQLite серіалізує операції запису за допомогою блокування всієї бази даних: одночасно може виконуватися лише один запис для всього файлу. Nextcloud постійно виконує записи: блокування файлів, записи про активність, елементи кешу та стан завдань. Один desktop client, який синхронізує дерево каталогів, надсилає багато паралельних запитів. За такого навантаження ви отримуєте SQLSTATE[HY000]: General error: 5 database is locked і HTTP 500, а збій виникає саме тоді, коли instance починає приносити користь.

Пізніше можна виконати конвертацію за допомогою occ db:convert-type, але це тривала міграція «все або нічого» для активного набору даних. Одразу використовуйте Postgres або MariaDB.

Файл Compose

Розмістіть це у /srv/nextcloud/compose.yaml, а секрети — у сусідньому файлі .env з режимом доступу 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:

Зафіксуйте major tag і перевірте актуальний tag у Docker Hub, перш ніж дослівно копіювати 31. latest у майбутньому може перевести вас через межу major-версії під час оновлення до docker compose pull, а Nextcloud цього не підтримує.

Каталог даних навмисно підключено як bind mount, а не як іменований volume: можливість безпосередньо вказати цей шлях для backup tool важливіша за акуратну структуру. Створіть його з UID образу www-data і правами доступу, яких потребує Nextcloud:

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

Зверніть увагу на публікацію порту: 127.0.0.1:8080:80. Docker публікує порти, додаючи правила DNAT, які обробляються до того, як пакет потрапить до ланцюжка INPUT ufw. Простий 8080:80 відкриє незашифрований Nextcloud у публічному інтернеті незалежно від налаштувань ufw. Прив’язка до loopback не дає доступу через публічний інтерфейс. Тоді firewall має дозволити доступ лише проксі. Якщо ви не хочете залишати SSH відкритим для всього інтернету, підключення до VPS через self-hosted WireGuard VPN дає змогу повністю видалити порт 22 із публічних правил:

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

Запустіть стек командою docker compose up -d, а потім моніторте docker compose logs -f app. Під час першого запуску весь каталог застосунку копіюється у volume, після чого запускається інсталятор. Контейнер не відповідає на запити, доки цей процес не завершиться.

TLS і reverse proxy

Встановіть nginx і certbot з репозиторію дистрибутива, створіть звичайний server block для порту 80 із правильним server_name, а потім дозвольте certbot перезаписати його. Механізм перевірки HTTP-01, timer для поновлення та сценарії помилок докладно описано в отриманні сертифікатів Let's Encrypt за допомогою certbot і nginx в Ubuntu 24.04:

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

Certbot додає рядки ssl_certificate і перенаправлення :80:443, а також встановлює systemd timer, який поновлює сертифікат із терміном дії 90 днів. Перевірте його наявність за допомогою systemctl list-timers | grep certbot. Timer для поновлення, який ніколи не було ввімкнено, є 90-денною точкою відмови.

Сам proxy block:

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;
    }
}

У nginx 1.25 і новіших версіях додайте http2 on;. Ubuntu 24.04 постачається зі старішою збіркою, де еквівалентом є listen 443 ssl http2;. Команда nginx -t покаже, який варіант підтримує ваша збірка.

client_max_body_size і великі тайм-аути читання запобігають перериванню великих завантажень на півдорозі. proxy_request_buffering off передає завантаження потоком, а не спочатку зберігає весь файл на диску proxy.

nginx на хості — найпростіший робочий варіант для одного застосунку. Якщо Nextcloud має працювати на VPS разом з іншими контейнерами, запуск Traefik як reverse proxy в Docker Compose для кількох застосунків переносить маршрутизацію та отримання сертифікатів у labels контейнерів. Ті самі питання щодо client_max_body_size і тайм-аутів виникають там знову у вигляді middleware і transport settings.

trusted_proxies і overwriteprotocol

Саме тут найчастіше виникають помилки в self-hosted інсталяціях Nextcloud. Їхні симптоми часто не пов’язані на перший погляд із причиною.

X-Forwarded-Proto: https враховується лише тоді, коли запит надходить з адреси, зазначеної в trusted_proxies. Якщо ця умова не виконується, Nextcloud вважає запит звичайним HTTP і формує URL-адреси http://. Проксі перенаправляє їх на HTTPS, браузер переходить за перенаправленням, а Nextcloud знову формує http://. Так виникає цикл перенаправлень. OVERWRITEPROTOCOL: https примусово задає схему незалежно від цього.

Проблема з TRUSTED_PROXIES полягає в тому, що адреса, яку бачить Nextcloud, — це не 127.0.0.1. nginx працює на хості та підключається до опублікованого порту, тому контейнер бачить шлюз Docker bridge, тобто адресу з 172.x. Визначте фактичну підмережу:

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

Додайте цей CIDR або мережу, що охоплює 172.16.0.0/12, до TRUSTED_PROXIES. Якщо вказати надто широку мережу, будь-який клієнт зможе підробити X-Forwarded-For. Якщо вказати неправильну мережу, усі входи виглядатимуть так, наче вони надходять із адреси шлюзу. Захист від brute-force одночасно заблокує весь інстанс, а в огляді адміністратора з’явиться повідомлення "The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy."

OVERWRITECLIURL потрібен для cron-контейнера, який не отримує вхідного запиту, щоб визначити ім’я хоста. Без цього фонові завдання формують посилання на localhost, а email-сповіщення містять непридатні URL-адреси.

Фонові завдання: cron, а не AJAX

Стандартний засіб запуску завдань Nextcloud — AJAX: завдання виконуються як побічний ефект завантаження сторінки користувачем. О 04:00 ніхто не переглядає сторінки, тому очищення кошика, видалення старих версій, створення попередніх переглядів і повторні спроби федеративних запитів зупиняються. Першою ознакою стає каталог даних, який постійно збільшується. Сервіс cron вище запускає офіційний цикл /cron.sh із використанням тих самих томів. Повідомте Nextcloud, що він має використовувати цей спосіб:

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

Кожна команда occ має таку структуру: docker compose exec -u www-data app php occ <command>. Варто створити для неї alias.

Резервні копії: три компоненти або жодного

Резервна копія лише файлової системи відновлює зламаний інстанс. Каталог даних містить байти; Postgres зберігає файловий кеш, спільні ресурси, користувачів і стан застосунку; config.php містить облікові дані бази даних, ID інстансу та сіль пароля. Якщо відновити файли без бази даних, Nextcloud не зможе їх побачити. Якщо відновити базу даних без config.php, вона не зможе відкрити базу даних. Якщо відновити стару базу даних у новіший каталог даних, ви отримаєте спільні ресурси, що вказують на файли, які вже переміщено.

Створюйте резервні копії всіх трьох компонентів, попередньо перевівши інстанс у стан спокою:

#!/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/

Режим технічного обслуговування забезпечує узгодженість дампу та копії файлів. Якщо пропустити цей крок, зрештою можна отримати базу даних, яка посилається на файл, до якого rsync ще не встиг звернутися. Зверніть увагу: скрипт зберігає дампи бази даних із часовими мітками, але створює лише одне дзеркало каталогу даних, rsync --delete перезаписує його під час кожного запуску, тому з копією файлів узгоджується лише найновіший дамп.

Потім перенесіть резервну копію за межі сервера. Резервна копія, що зберігається на тому самому VPS, що й дані, які вона захищає, є копією, а не резервною копією. restic для object storage або другого хоста є стандартним рішенням, а дедуплікація значно краще обробляє каталог даних, ніж щоденний tarball. Повний опис налаштування, від ініціалізації репозиторію до нічного таймера та перевірки відновлення, наведено в матеріалі резервні копії VPS за межами сервера за допомогою restic.

Відновлення не є простим виконанням цих кроків у зворотному порядку. Щойно запущений стек виконує інсталятор і записує абсолютно новий config.php, новий ID інстансу та сіль пароля. Імпорт дампу поверх цієї нової ідентичності призводить до пошкоджених сеансів і токенів спільних ресурсів. Спочатку поверніть стару ідентичність у такому порядку:

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 узгоджує файловий кеш із фактичним станом диска. Відпрацюйте цю процедуру один раз на резервному VPS, перш ніж вона стане потрібною. Такий самий поділ між байтами на диску та метаданими в Postgres визначає роботу всіх інших застосунків цієї структури. Саме тому резервна копія Immich, що містить бібліотеку, але не базу даних, відновлює порожню часову шкалу.

Оновлення: лише одна major-версія за раз

Nextcloud підтримує оновлення лише на одну major-версію за раз. Перехід з 29 на 31 не завершується коректно: він завершується помилкою Exception: Updates between multiple major versions and downgrades are unsupported. і залишає систему в maintenance mode.

Оновлення Docker виконується так: створіть резервну копію, змініть tag з 31 на 32 в обох сервісах app і cron, потім виконайте docker compose pull && docker compose up -d, а далі docker compose logs -f app. Entrypoint образу виявляє новішу версію коду щодо наявних даних і сам запускає occ upgrade. Не переривайте цей процес. Коли в журналах припиняться нові повідомлення, виконайте docker compose exec -u www-data app php occ status і перевірте versionstring, а також переконайтеся, що застосунки знову увімкнені.

Дотримуйтеся двох правил. Оновіть одну major-версію, перевірте результат, а потім переходьте до наступної. Ніколи не змінюйте tag у сервісі app без відповідної зміни в cron. Дві різні версії Nextcloud, підключені до однієї бази даних, можуть призвести до пошкодження даних.

Фактичні помилки, які ви побачите

"Your data directory is readable by other users. Please change the permissions to 0770." У каталозі, змонтованому через bind mount, встановлено біти читання для групи або інших користувачів. sudo chmod 0770 /srv/nextcloud/data і sudo chown -R 33:33 /srv/nextcloud/data.

"Your data directory is invalid. Ensure there is a file called .ocdata in the root." Bind mount вказує на каталог, який Nextcloud ще не ініціалізував, на шлях із помилкою або на новий порожній каталог, підставлений замість каталогу робочого екземпляра. Перевірте, чи шлях на хості відповідає рядку volume.

"Access through untrusted domain." Ім’я хоста в запиті відсутнє у trusted_domains. NEXTCLOUD_TRUSTED_DOMAINS діє лише під час першого встановлення; після цього змініть значення безпосередньо: occ config:system:set trusted_domains 1 --value=cloud.example.com.

502 Bad Gateway, із connect() failed (111: Connection refused) while connecting to upstream у /var/log/nginx/error.log. nginx не отримав відповіді на 127.0.0.1:8080. Контейнер ще ініціалізується (перевірте docker compose logs app), завершив роботу (docker compose ps), або рядок публікації не відповідає порту proxy_pass. Підтвердьте це за допомогою ss -ltnp | grep 8080.

Цикл перенаправлень або попередження "insecure" в огляді адміністратора. Відсутній OVERWRITEPROTOCOL: https, або TRUSTED_PROXIES не містить підмережу шлюзу Docker. Див. розділ про проксі вище.

LockedException: "files/..." is locked. Якщо встановлено REDIS_HOST, образ налаштовує Redis як бекенд блокувань, тому застарілі блокування виникають рідко. Без цього блокування зберігаються в таблиці бази даних oc_file_locks, а примусове завершення запиту під час запису залишає там рядки. Перш ніж видаляти рядки блокувань вручну, переконайтеся, що Redis справді використовується: occ config:system:get memcache.locking має повернути клас Redis.

"The PHP memory limit is below the recommended value of 512MB." Збільште PHP_MEMORY_LIMIT і створіть контейнер повторно. Пам’ятайте, як це впливає на максимальне споживання пам’яті в найгіршому випадку.

Що ламається під навантаженням

Перша проблема виникає, коли каталог даних стає більшим за том. Збільшення тому на VPS — це зміна його розміру та розширення файлової системи. Виконати це планово значно простіше, ніж після заповнення тому на 100%. Налаштуйте сповіщення про використання диска вже зараз, а не пізніше.

Друга проблема — oc_filecache. Переліки файлів і сканування під час синхронізації сповільнюються зі зростанням кількості записів. Виправлення потребує роботи з базою даних: розмістіть Postgres на швидкому сховищі, виділіть йому достатній обсяг shared memory і видаляйте кошик та старі версії за допомогою параметрів зберігання, а не дозволяйте їм накопичуватися без обмежень.

Третя проблема — конкуренція генерації попередніх переглядів за ресурси з іншими процесами. На невеликому сервері обмежте кількість preview providers і ніколи не запускайте occ preview:generate-all у робочі години. Якщо більшість даних становлять фотографії з камери телефона, генерацію мініатюр краще виконувати на спеціалізованому photo server. У матеріалі Порівняння PhotoPrism та Immich за використанням RAM, телефонними застосунками й командами резервного копіювання описано, скільки ресурсів кожен із них потребує поруч із Nextcloud.

Після цього чесна відповідь така: додатковим компонентам потрібна окрема машина. Collabora і повнотекстовий пошук — це окремі резидентні сервіси з власними профілями використання пам’яті. Якщо розмістити їх на сервері, де також зберігається єдина копія файлів, без жодної користі збільшується домен відмови. Якщо потрібне редагування документів у браузері, саме мінімальні вимоги постачальників до RAM і обмеження кількості підключень, які відрізняють OnlyOffice від Collabora визначають, чи зможе VPS із 2 до 4 GB запустити відповідний сервіс. Переносьте файлове сховище до основного S3-compatible сховища, коли том перестає відповідати вашим потребам. Врахуйте, що це ускладнює резервне копіювання, а не спрощує його: база даних і далі містить метадані, тому її потрібно дампити синхронно з bucket.

Коли instance почне обслуговувати реальних користувачів, встановіть Uptime Kuma перед ним, щоб дізнаватися про простої раніше, ніж про них повідомлять клієнти синхронізації. Приватна cloud-система добре поєднується з власним mail server, а якщо ви не хочете з’єднувати сервіси вручну, у матеріалі Cloudron, CasaOS та Coolify порівнюються платформи, які роблять це за вас. Якщо наступним у списку буде self-hosted search engine, очікуйте проблем іншого класу: помилки 429 у SearXNG виникають або через його власний обмежувач швидкості, або через блокування вашої IP-адреси VPS upstream engines. Визначити причину можна лише за журналом.

FAQ

Чи можна запускати Nextcloud на SQLite замість Postgres?

Можна, і офіційний образ це підтримує, але один клієнт синхронізації на робочому столі, який надсилає паралельні запити, спричинить SQLSTATE[HY000]: General error: 5 database is locked і помилки HTTP 500. SQLite блокує запис на рівні всієї бази даних, а Nextcloud постійно виконує записи: блокування файлів, рядки активності, стан завдань. Почніть із Postgres або MariaDB; occ db:convert-type існує, але це тривала міграція за принципом «усе або нічого» для даних у робочій системі.

Скільки RAM насправді потрібно Nextcloud VPS?

Розраховуйте обсяг за паралельністю, а не за кількістю користувачів. Піковий обсяг резидентної пам’яті приблизно дорівнює кількості паралельних запитів, помноженій на PHP_MEMORY_LIMIT, плюс спільні буфери Postgres і по одному бекенду на кожне підключення, а також пам’ять, яку може тимчасово використати генерація попередніх переглядів. Сервер із 2 GB може працювати для невеликого домашнього екземпляра, якщо обмежити генерацію попередніх переглядів і додати swap; після додавання Collabora або повнотекстового пошуку потрібно окремо врахувати резидентні служби цих компонентів.

Чому великі завантаження не виконуються через reverse proxy nginx?

Зазвичай це пояснюють два параметри проксі: client_max_body_size, для якого стандартне значення становить 1 MB, обрізає запит, а короткі значення proxy_read_timeout / proxy_send_timeout переривають тривалі передавання даних посередині. Задайте для обох параметрів достатньо великі значення, увімкніть proxy_request_buffering off, щоб передавати дані потоком, а не зберігати їх у буфері, і збільште PHP_UPLOAD_LIMIT у контейнері застосунку до такого самого значення.

Чому Nextcloud зациклює перенаправлення або попереджає про reverse proxy?

Контейнер не бачить nginx за адресою 127.0.0.1. Він бачить шлюз Docker bridge, десь у діапазоні 172.x. Якщо цю адресу не додано до TRUSTED_PROXIES, заголовок X-Forwarded-Proto: https ігнорується, Nextcloud формує URL-адреси http://, а проксі перенаправляє їх назад. Задайте TRUSTED_PROXIES для фактичної підмережі bridge і зафіксуйте OVERWRITEPROTOCOL: https.

Чи можна оновити Nextcloud з 29 одразу до 31?

Ні. Nextcloud підтримує оновлення лише на одну основну версію за один крок. Якщо пропустити версію, оновлення зупиниться з повідомленням Updates between multiple major versions and downgrades are unsupported., а екземпляр залишиться в режимі обслуговування. Створіть резервну копію, збільште тег на одну основну версію для обох служб app і cron, docker compose pull && docker compose up -d, перевірте результат за допомогою occ status, а потім повторіть процедуру.