Nextcloud не загружает большие файлы: причины и решение
Мелочь синхронизируется мгновенно, а восьмигигабайтный архив падает: иногда сразу, иногда на 97 %, иногда «загрузился», но в папке его нет. client_max_body_size и upload_max_filesize вы уже подняли до 16G — не помогло. Значит, рвёт не тот лимит: в цепочке есть ещё сборка чанков, дисковый буфер nginx, request_terminate_timeout и, если вы за Cloudflare, потолок в 100 МБ, который вашим конфигом не настраивается.
Содержание
- Сначала выясните, кто именно оборвал загрузку
- Веб грузит чанками, WebDAV — целиком: это две разные поломки
- Cloudflare, Traefik и второй nginx: потолок, которого нет в вашем конфиге
- Место: файл существует в трёх копиях одновременно
- Таймауты: max_execution_time вас не спасёт, request_terminate_timeout убьёт
- Тихие блокировщики: антивирус, квота, блокировки и S3
- Какой сервер под Nextcloud с большими файлами брать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Сначала выясните, кто именно оборвал загрузку
Nextcloud большие файлы теряет по шести разным причинам, и лечатся они в разных местах. Смотрите логи в момент падения:
tail -f /var/log/nginx/error.log /var/log/nginx/access.log /var/log/php8.3-fpm.log
sudo -u www-data tail -f /var/www/nextcloud/data/nextcloud.log | jq -r '[.time,.app,.message]|@tsv'
Дальше — по симптому:
| Что видно | Кто оборвал | Куда идти |
|---|---|---|
413 сразу, а запроса в вашем access.log нет | внешний прокси или CDN | секция про Cloudflare |
client intended to send too large body: 8589934592 bytes | ваш nginx | client_max_body_size |
| Прогресс дошёл до 100 %, потом ошибка | сборка файла (MOVE) | таймауты и место |
expected filesize ... but read ... and wrote ... bytes | тело запроса оборвалось | сеть или убитый воркер |
507 Insufficient Storage | квота или диск | df -h, квота пользователя |
423 Locked при повторе | остатки прошлой попытки | таблица oc_file_locks |
Первая строка полезнее всех: нет записи о запросе в вашем access.log, а браузер получил 413 — файл до сервера не дошёл. Второй тест — залить его по WebDAV мимо веб-интерфейса, паролем приложения (Настройки → Безопасность), иначе двухфакторка не пустит.
fallocate -l 8G /tmp/test-8g.bin
curl -T /tmp/test-8g.bin -u 'ivan:xxxxx-xxxxx-xxxxx-xxxxx-xxxxx' \
-w '\nHTTP %{http_code}, отдано %{size_upload} байт за %{time_total} c\n' \
https://cloud.example.com/remote.php/dav/files/ivan/test-8g.bin
Прошло по WebDAV, но падает в браузере — дело в чанках. Падает везде — виновата цепочка nginx → PHP-FPM → диск.
Веб грузит чанками, WebDAV — целиком: это две разные поломки
Веб-интерфейс и десктоп-клиент не отправляют большой файл одним запросом. С Nextcloud 15 работает chunked upload v2: клиент создаёт временный каталог, льёт туда части и одним MOVE просит сервер склеить их. Руками:
UP=https://cloud.example.com/remote.php/dav/uploads/ivan
AUTH='ivan:xxxxx-xxxxx-xxxxx-xxxxx-xxxxx'
curl -u "$AUTH" -X MKCOL "$UP/1770000000"
curl -u "$AUTH" -T part-00 "$UP/1770000000/00000000"
curl -u "$AUTH" -T part-01 "$UP/1770000000/00000001"
curl -u "$AUTH" -X MOVE -H "OC-Total-Length: 8589934592" \
-H "Destination: https://cloud.example.com/remote.php/dav/files/ivan/big.bin" \
"$UP/1770000000/.file"
Части сортируются лексикографически — нумеруйте с ведущими нулями, иначе 10 встанет между 1 и 2 и файл соберётся вперемешку.
Отсюда три вывода:
upload_max_filesizeпри загрузке из браузера почти не при чём. PHP видит не файл, а один чанк — по умолчанию 10 МБ. Значение2Mвсё сломает, но поднимать его до16Gради веба бессмысленно.- Запрос
MOVEне несёт тела.client_max_body_sizeна него не влияет вообще, аfastcgi_read_timeoutвлияет напрямую: склейка 8 ГБ на NVMe с записью около 900 МБ/с занимает 9–12 секунд, на сетевом хранилище — минуты. Здесь и живёт «дошло до 100 % и упало». - Мелкий чанк на длинном маршруте дорог. 20 ГБ кусками по 10 МБ — 2048 запросов
PUT; при RTT 48 мс одни round-trip'ы добавляют около 100 секунд.
Размер чанка для веба задаёт ключ max_chunk_size в байтах, разумный потолок — 50–100 МБ:
sudo -u www-data php occ config:system:set max_chunk_size --value 52428800
Оговорки честные: чанк обязан пролезать под client_max_body_size и post_max_size, а при обрыве сети заново поедет весь чанк; значение 0 отключает чанкование совсем. Десктоп-клиент подбирает размер сам, целясь в 60 секунд на часть, — переопределяется в ~/.config/Nextcloud/nextcloud.cfg ключами chunkSize, maxChunkSize, targetChunkUploadDuration.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть NextcloudCloudflare, Traefik и второй nginx: потолок, которого нет в вашем конфиге
Конфиг идеален, а 413 приходит всё равно — значит, перед Nextcloud стоит ещё один слой.
Cloudflare в режиме прокси (оранжевое облако) режет тело запроса на 100 МБ для Free и Pro и на 200 МБ для Business. Вашими настройками это не обходится. Диагностика однозначная: curl -sI https://cloud.example.com/ | grep -i cf-ray что-то возвращает, а в вашем access.log неудачного запроса нет.
Вариантов два: увести домен из-под прокси («DNS only», серое облако) либо завести поддомен dav.example.com мимо прокси. Первый проще, но теряется скрытие origin-IP и защита от DDoS; второй сохраняет защиту веб-интерфейса, но раскрывает адрес сервера в DNS. Traefik и внешний nginx режут так же тихо: у первого лимит задаёт middleware buffering с maxRequestBodyBytes, второму нужен свой client_max_body_size.
Отдельная история — куда nginx кладёт тело запроса. По умолчанию он принимает весь запрос на диск и только потом отдаёт PHP-FPM:
[warn] 812#812: *45 a client request body is buffered to a temporary file
/var/lib/nginx/body/0000000003, client: 10.0.0.5, request: "PUT /remote.php/dav/... HTTP/2.0"
Каталог /var/lib/nginx/body живёт на корневом разделе: заливаете 30 ГБ, а под / осталось 12 — загрузка умрёт, и Nextcloud тут ни при чём. Лечится переносом буфера или потоковой передачей:
client_body_temp_path /srv/nginx-body 1 2;
fastcgi_request_buffering off;
Минус второго варианта: без буферизации nginx не может повторить запрос к бэкенду, а медленный клиент занимает воркер PHP-FPM всё время передачи.
Место: файл существует в трёх копиях одновременно
Ошибка планирования — считать, что под файл в 30 ГБ нужно 30 ГБ. В пике на диске лежит до трёх копий: буфер тела в /var/lib/nginx/body, чанки в data/<user>/uploads/<uploadid>/ и собираемый файл в data/<user>/files/.
du -sh /var/www/nextcloud/data/*/uploads/ 2>/dev/null
df -h /var/www/nextcloud/data /var /tmp
df -i /var/www/nextcloud/data
df -i не пропускайте: 20 ГБ кусками по 10 МБ — 2048 inode за загрузку, и на скромном разделе они кончатся раньше байтов. Ошибка невнятная: No space left on device при живых 40 % свободного места. Мусор от оборванных попыток сам не исчезает:
find /var/www/nextcloud/data/*/uploads/ -mindepth 1 -maxdepth 1 -type d -mtime +1 -exec rm -rf {} +
Второе узкое место — временный каталог. Во многих сборках /tmp смонтирован как tmpfs в половину ОЗУ: на сервере с 4 ГБ это 2 ГБ, и запись туда съедает оперативку. Увидели tmpfs в df -h /tmp — уводите временные файлы на диск, и в PHP, и в Nextcloud:
upload_tmp_dir = /var/nc-tmp
sys_temp_dir = /var/nc-tmp
output_buffering = 0
install -d -o www-data -g www-data -m 750 /var/nc-tmp
sudo -u www-data php occ config:system:set tempdirectory --value /var/nc-tmp
output_buffering = 0 не случайно: с буферизацией вывода PHP держит в памяти то, что должен отдавать потоком, и упирается в memory_limit.
Таймауты: max_execution_time вас не спасёт, request_terminate_timeout убьёт
max_execution_time считает время работы скрипта и не учитывает время в системных вызовах — а чтение тела запроса и запись файла это именно они. Поднятие до 3600 на долгую загрузку часто не влияет никак: она под него и не попадала.
Реально обрывает request_terminate_timeout в пуле PHP-FPM: он считает астрономическое время и убивает воркер.
WARNING: [pool nextcloud] child 2841, script '/var/www/nextcloud/remote.php'
(request: "MOVE /remote.php/dav/uploads/ivan/1770000000/.file") execution timed out
(305.418712 sec), terminating
Клиент получит 502 или ту самую строку expected filesize ... but read ... and wrote ... bytes: соединение закрылось раньше, чем приехали все байты. По умолчанию параметр выключен (0), но панели и готовые сборки часто ставят 300 секунд. Правьте /etc/php/8.3/fpm/pool.d/nextcloud.conf:
request_terminate_timeout = 3600
pm = dynamic
pm.max_children = 20
И согласуйте с веб-сервером: fastcgi_read_timeout 3600; в location Nextcloud, иначе оборвёт он. Второй эффект — голодание пула: воркер занят всё время передачи, и при pm.max_children = 5 пять параллельных заливок вешают весь Nextcloud вместе с логином, а в лог падает server reached pm.max_children setting (5), consider raising it. Считайте просто: один процесс PHP-FPM с Nextcloud занимает 90–140 МБ RSS. На 4 ГБ ОЗУ с MariaDB и Redis рядом реалистичный потолок — 12–16 детей. Ставить 50 «с запасом» нельзя: при всплеске сервер уйдёт в OOM, и journalctl -k | grep -i oom покажет убитый php-fpm8.3.
Тихие блокировщики: антивирус, квота, блокировки и S3
Эти причины не дают внятной ошибки — файл просто не появляется.
ClamAV через files_antivirus. Дистрибутивные значения в /etc/clamav/clamd.conf — StreamMaxLength 25M, MaxFileSize 100M, MaxScanSize 100M. Всё, что больше StreamMaxLength, clamd не принимает: Nextcloud получает ошибку сканирования вместо вердикта, и файл либо блокируется, либо помечается непроверенным. Поднимите до StreamMaxLength 2G и сделайте systemctl restart clamav-daemon. Честно: потоковое сканирование забирает память и время, крупные архивы разумнее из проверки исключить.
Квота пользователя. 507 Insufficient Storage часто вызван не диском. На квоту влияют корзина и версии: удалённый вчера архив занимает место, пока лежит в Deleted files.
sudo -u www-data php occ user:info ivan
sudo -u www-data php occ user:setting ivan files quota 500GB
Блокировки. Оборванная загрузка оставляет запись в oc_file_locks, и повтор получает 423 Locked (Sabre\DAV\Exception\Locked). Штатно — подождать, пока протухнет; быстро:
sudo -u www-data php occ maintenance:mode --on
mysql -u nextcloud -p nextcloud -e "DELETE FROM oc_file_locks WHERE 1;"
sudo -u www-data php occ maintenance:mode --off
Если блокировки живут в Redis, достаточно перезапустить redis-server.
S3 как primary storage. Файл сначала целиком собирается локально и лишь потом уходит в бакет частями (uploadPartSize в objectstore.arguments) — локальное место нужно всё равно.
И проверьте разрядность: php -r 'echo PHP_INT_SIZE;' должен вернуть 8. На 32-битной сборке файлы больше 4 ГБ не обрабатываются в принципе.
Какой сервер под Nextcloud с большими файлами брать в MAATRIX
Заливка гигабайтов — задача про диск и канал, а не про процессор: ядра нужны PHP-FPM лишь для параллельных соединений, TLS на потоке в 200 Мбит/с стоит vCPU единицы процентов.
Минимум: 2 vCPU, 4 ГБ RAM, 80 ГБ NVMe. Двое-трое пользователей, файлы до 5–10 ГБ. Ограничения честные: 4 ГБ — это 12–16 процессов PHP-FPM вместе с MariaDB и Redis, то есть 3–4 одновременные заливки, дальше очередь. А 80 ГБ под правило трёх копий дают реальный потолок файла около 25 ГБ, а не 80.
Комфортный вариант: 4 vCPU, 8 ГБ RAM, 200–500 ГБ NVMe. Отдел на 15–30 человек, видео и образы дисков, спокойные pm.max_children = 24, место под /var/nc-tmp и буфер nginx на отдельном разделе. Файлы на сотни гигабайт разумнее держать на отдельном дисковом томе, а не наращивать системный.
Локация — Великобритания (Лондон). Каждый чанк — отдельный round-trip, и на 2000 частей разница в 40 мс превращается в полторы минуты. RTT Москва — Лондон 45–55 мс против 120–140 мс до восточного побережья США, а сотрудникам в ЕС Лондон даёт единицы миллисекунд; плюс европейская юрисдикция и GDPR-соседство. Если аудитория целиком российская и в договоре есть персональные данные — берите RU: 152-ФЗ и минимальный пинг перевесят.
Nextcloud из каталога apps.maatrix.io разворачивается на сервер автоматически при заказе: руками ничего вставлять не нужно, автоустановка работает на Ubuntu и Debian, доступы появляются в личном кабинете в разделе «Доступ». Оговорка честная: базовая установка настроена под типовой сценарий, а лимиты под 30-гигабайтные файлы (request_terminate_timeout, client_body_temp_path, tempdirectory) вы правите под себя. Оплата — картой российского банка, по СБП или криптовалютой.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть NextcloudОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Поднял client_max_body_size и upload_max_filesize до 16G, а из браузера файлы рвутся. Почему?
Веб-интерфейс грузит чанками по 10 МБ, и эти лимиты для него почти не работают. Смотрите на запрос MOVE, который склеивает части: он упирается в fastcgi_read_timeout и request_terminate_timeout, а не в размер тела.
Файл 100 МБ проходит, а 101 МБ — нет, и в логах nginx пусто. Что это?
Почти наверняка Cloudflare в режиме прокси: на Free и Pro тело запроса ограничено 100 МБ. Проверьте cf-ray в ответе и либо уведите домен из-под прокси, либо заведите отдельный поддомен для WebDAV.
Сколько свободного места нужно под файл на 30 ГБ?
Считайте до трёх копий: буфер nginx, каталог uploads с чанками и итоговый файл — около 90 ГБ в пике, если буферизация включена и каталоги на одном разделе.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.