VPS에 Docker로 Nextcloud 설치 및 백업 가이드
Docker Compose를 사용하여 VPS에 Nextcloud를 구축하는 방법을 설명합니다. Postgres와 Redis를 활용한 최적화된 설정부터 Let's Encrypt TLS 적용 및 데이터 복구가 가능한 백업 전략까지 상세히 다룹니다.
What you are actually building
This guide runs Nextcloud on a VPS with Docker Compose, puts Let's Encrypt TLS in front of it, and sets up a backup that actually restores. Four containers and a proxy: the official nextcloud image listening on loopback, Postgres holding every piece of file metadata, Redis holding the file locks, a second copy of the Nextcloud image running nothing but the cron loop, and nginx on the host terminating TLS in front of all of it. The install itself takes twenty minutes, and it is not the part that matters. Two decisions made in the first hour decide whether you still have your files in a year: a real database instead of SQLite, and a backup that captures the data directory, the database and config.php as one consistent set.
This assumes Ubuntu 24.04 LTS or Debian 13, Docker Engine with the Compose v2 plugin installed from Docker's own repository, and a DNS A record (plus AAAA if you have IPv6) already pointing cloud.example.com at the VPS. All of it needs a server you control — there is no way to do TLS termination and a database dump on someone else's SaaS.
Sizing: 실제 메모리 소비 요인
Nextcloud의 메모리 사용량은 세 가지 요소에 의해 결정됩니다. 이 중 "Nextcloud" 자체의 비중은 낮습니다.
PHP workers. -apache 이미지는 PHP 인터프리터를 포함하는 worker process를 통해 각 동시 요청을 처리합니다. 각 worker는 PHP가 요청을 종료하기 전까지 최대 PHP_MEMORY_LIMIT까지 메모리를 사용할 수 있습니다. 최악의 경우 상주 메모리(resident memory)는 대략 동시 요청 수 × 메모리 제한입니다. 데스크톱 동기화 클라이언트는 사용자당 여러 개의 병렬 연결을 생성합니다. 따라서 메모리 상한은 사용자 수가 아닌 동시 요청 수에 의해 결정됩니다.
The database. Postgres는 연결당 backend를 fork하며 shared buffers를 상주 메모리에 유지합니다. 데이터베이스의 working set은 바이트 수가 아닌 파일 수에 따라 증가합니다. oc_filecache은 사용자당 파일 하나당 행(row)을 생성합니다. 10만 개의 작은 파일이 있는 데이터베이스가 100개의 큰 파일이 있는 데이터베이스보다 더 많은 메모리를 사용합니다.
Preview generation. 썸네일을 생성할 때 원본 이미지를 전체 해상도로 메모리에 디코딩합니다. 비디오 미리보기는 ffmpeg를 호출합니다. occ preview:generate-all을 실행하면 이러한 메모리 급증(spike)이 연속적으로 반복되며, 이는 소규모 VPS가 OOM killer에 의해 종료되는 가장 흔한 원인입니다.
Redis는 상대적으로 메모리 소모가 적습니다. 나중에 추가하는 기능들(Collabora, full-text search, antivirus scanner 등)은 각각 별도의 상주 서비스로 동작하며 고유의 메모리 점유율을 가집니다. 따라서 해당 기능을 활성화하기 전에 크기 산정 계획에 포함해야 합니다.
RAM 용량이 부족한 경우 조절 가능한 요소는 다음과 같습니다: PHP_MEMORY_LIMIT를 낮춥니다. preview_max_x / preview_max_y / preview_max_filesize_image의 상한을 설정합니다. enabledPreviewProviders을 실제 사용하는 포맷으로 제한합니다. trashbin_retention_obligation와 versions_retention_obligation를 설정하여 데이터 디렉토리가 파일 크기의 수 배로 조용히 커지는 것을 방지합니다. swap file을 추가하십시오. Swap은 느리지만, 업그레이드 도중 OOM kill이 발생하는 것은 더 심각한 문제입니다.
SQLite가 작동하지 않는 이유
Nextcloud는 SQLite를 지원하며 공식 이미지에서도 이를 기본적으로 사용합니다. 하지만 사용하지 마십시오. SQLite는 데이터베이스 전체에 락(lock)을 걸어 쓰기 작업을 직렬화합니다. 즉, 파일 전체에 대해 한 번에 하나의 쓰기 작업만 가능합니다. Nextcloud는 파일 잠금, activity 행, cache 항목, job 상태 등 지속적으로 쓰기 작업을 수행합니다. 또한 단일 데스크톱 클라이언트가 디렉토리 트리를 동기화할 때 많은 병렬 요청을 보냅니다. 이러한 패턴에서는 SQLSTATE[HY000]: General error: 5 database is locked 및 HTTP 500 오류가 발생하며, 인스턴스가 본격적으로 사용되기 시작할 때 오류가 나타납니다.
나중에 occ db:convert-type을 사용하여 변환할 수 있지만, 라이브 데이터셋에 대해 중단 없이 진행해야 하는 긴 마이그레이션 과정입니다. 처음부터 Postgres 또는 MariaDB로 시작하십시오.
The Compose file
이 내용을 /srv/nextcloud/compose.yaml에 저장하십시오. secrets는 mode 600인 인접 .env 파일에 저장해야 합니다.
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:31을 그대로 복사하기 전에, major tag를 고정하고 Docker Hub에서 현재 버전을 확인하십시오. latest를 사용하면 향후 docker compose pull에서 major boundary를 넘을 수 있으며, Nextcloud는 이를 지원하지 않습니다.
data directory는 의도적으로 named volume이 아닌 bind mount를 사용합니다. 백업 도구가 직접 가리킬 수 있는 경로가 관리의 편의성보다 더 중요하기 때문입니다. 이미지의 www-data UID와 Nextcloud가 요구하는 권한으로 디렉토리를 생성하십시오:
sudo mkdir -p /srv/nextcloud/data
sudo chown -R 33:33 /srv/nextcloud/data
sudo chmod 0770 /srv/nextcloud/dataport publish 설정에 주의하십시오: 127.0.0.1:8080:80. Docker는 DNAT 규칙을 작성하여 포트를 공개합니다. 이 규칙은 ufw의 INPUT chain이 패킷을 확인하기 전에 평가됩니다. 따라서 8080:80만 사용하면 ufw 설정과 관계없이 암호화되지 않은 Nextcloud가 공용 인터넷에 노출됩니다. loopback에 바인딩하면 공용 인터페이스로부터 보호할 수 있습니다. 이 경우 방화벽은 proxy만 허용하면 됩니다. 만약 SSH를 인터넷 전체에 공개하고 싶지 않다면, self-hosted WireGuard VPN을 통해 VPS에 접속하여 공용 규칙에서 port 22를 완전히 제거할 수 있습니다:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enabledocker compose up -d으로 컨테이너를 실행한 후 docker compose logs -f app를 확인하십시오. 첫 실행 시 애플리케이션 트리 전체가 volume으로 복사되고 installer가 실행됩니다. 이 과정이 완료될 때까지 컨테이너는 응답하지 않습니다.
TLS 및 reverse proxy
배포판에서 nginx와 certbot을 설치합니다. 올바른 server_name를 포함한 일반 port-80 server block을 생성한 후, certbot이 이를 재작성하도록 합니다. HTTP-01 challenge의 작동 방식, 갱신 타이머 및 실패 모드는 issuing Let's Encrypt certificates with certbot and nginx on Ubuntu 24.04에서 자세히 다룹니다.
sudo apt install nginx certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.example.comCertbot은 ssl_certificate 라인과 :80 → :443 리다이렉트를 추가하며, 90일 주기의 인증서를 갱신하는 systemd timer를 설치합니다. systemctl list-timers | grep certbot로 해당 타이머의 존재를 확인하십시오. 활성화되지 않은 갱신 타이머은 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과 긴 read timeout 설정은 대용량 업로드가 중간에 중단되는 것을 방지합니다. proxy_request_buffering off는 파일 전체를 proxy의 디스크에 먼저 저장하지 않고 업로드를 스트리밍합니다.
호스트의 nginx를 사용하는 방식은 단일 앱을 운영할 때 가장 간단한 방법입니다. 만약 Nextcloud가 다른 컨테이너들과 VPS를 공유해야 한다면, running Traefik as a Docker Compose reverse proxy for multiple apps를 통해 라우팅과 인증서 발급을 container labels로 관리할 수 있습니다. 이 경우 client_max_body_size 및 timeout 관련 설정은 middleware 및 transport 설정으로 다시 나타납니다.
trusted_proxies and overwriteprotocol
이 설정 오류는 많은 self-hosted Nextcloud 인스턴스에서 발생하며, 증상은 원인과 무관해 보일 수 있습니다.
X-Forwarded-Proto: https은 요청이 trusted_proxies에 나열된 주소에서 올 때만 적용됩니다. 이 설정이 적용되지 않으면 Nextcloud는 요청을 plain HTTP로 인식하여 http:// URL을 생성합니다. 프록시가 이를 HTTPS로 리다이렉트하고 브라우저가 이를 따라가면, Nextcloud는 다시 http://를 생성합니다. 이것이 리다이렉트 루프(redirect loop)의 원인입니다. OVERWRITEPROTOCOL: https를 사용하면 스킴(scheme)을 강제로 고정할 수 있습니다.
TRUSTED_PROXIES의 문제는 Nextcloud가 인식하는 주소가 127.0.0.1이 아니라는 점입니다. nginx는 host에서 실행되며 공개된 port로 연결됩니다. 따라서 컨테이너는 172.x에 해당하는 Docker bridge gateway를 인식합니다. 실제 subnet을 확인하십시오:
docker network inspect nextcloud_default \
-f '{{range .IPAM.Config}}{{.Subnet}}{{end}}'해당 CIDR(또는 이를 포함하는 172.16.0.0/12)을 TRUSTED_PROXIES에 입력하십시오. 범위를 너무 넓게 설정하면 모든 클라이언트가 X-Forwarded-For를 위조할 수 있습니다. 범위를 잘못 설정하면 모든 로그인 요청이 gateway 주소에서 오는 것으로 나타납니다. 이 경우 brute-force protection이 인스턴스 전체를 차단하며, admin overview에 "The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy."라는 메시지가 표시됩니다.
OVERWRITECLIURL는 cron container에 중요합니다. cron container는 hostname을 추론할 수 있는 incoming request가 없습니다. 이 설정이 없으면 background jobs가 localhost 링크를 생성하며, 이메일 알림에 사용할 수 없는 URL이 포함됩니다.
Background jobs: cron, not AJAX
Nextcloud의 기본 작업 실행 방식은 AJAX입니다. AJAX 방식은 사용자가 페이지를 로드할 때 작업이 실행됩니다. 새벽 04:00에는 접속자가 없으므로 휴지통 만료, 버전 정리, 미리보기 생성, federated 재시도 작업이 중단됩니다. 이로 인해 data directory의 용량이 계속해서 증가하는 현상이 발생합니다. 위에서 설명한 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로 등록하여 사용하는 것이 좋습니다.
Backups: three things, or none
A filesystem-only backup restores to a broken instance. The data directory holds the bytes; Postgres holds the file cache, shares, users, and app state; config.php holds the database credentials, the instance ID and the password salt. Restore the files without the database and Nextcloud cannot see them. Restore the database without config.php and it cannot open the database. Restore an old database against a newer data directory and you get shares pointing at files that moved.
Back up all three, from a quiesced instance:
#!/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/Maintenance mode is what makes the dump and the file copy agree with each other. Skip it and you will eventually capture a database that references a file the rsync had not reached yet. Note that the script keeps timestamped database dumps but only one rolling mirror of the data directory — rsync --delete overwrites it each run — so only the newest dump pairs with the file copy.
Then get it off the box. A backup that lives on the same VPS as the thing it backs up is a copy, not a backup. restic against object storage or a second host is the usual answer, and its deduplication handles the data directory far better than a nightly tarball. The full setup, from repository init to the nightly timer and the restore drill, is in off-box VPS backups with restic.
Restore is not simply the reverse. A freshly-started stack runs the installer and writes a brand-new config.php — a new instance ID and password salt — and importing the dump on top of that new identity leaves broken sessions and share tokens. Put the old identity back first, in this order:
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 --allfiles:scan reconciles the file cache with what is actually on disk. Rehearse this once, on a spare VPS, before you need it.
Upgrades: 한 번에 하나의 메이저 버전만
Nextcloud는 한 번에 하나의 메이저 버전만 업그레이드할 수 있습니다. 29에서 31로 바로 건너뛰면 정상적으로 완료되지 않습니다. Exception: Updates between multiple major versions and downgrades are unsupported. 오류가 발생하며 시스템이 maintenance mode로 전환됩니다.
Docker 업그레이드 절차는 다음과 같습니다. 먼저 백업을 수행합니다. app 및 cron 서비스 모두에서 tag를 31에서 32으로 수정합니다. 그 다음 docker compose pull && docker compose up -d를 실행하고 docker compose logs -f app을 수행합니다. image entrypoint가 기존 데이터와 새 코드를 비교하여 occ upgrade을 자동으로 실행합니다. 이 과정을 중단하지 마십시오. 로그 출력이 멈추면 docker compose exec -u www-data app php occ status를 실행하여 versionstring 및 앱의 활성화 여부를 확인하십시오.
문제를 방지하기 위한 두 가지 규칙이 있습니다. 첫째, 메이저 버전을 하나씩 올리고 확인한 뒤 다음 버전을 올리십시오. 둘째, cron를 동일하게 수정하지 않고 app 서비스의 tag만 수정하지 마십시오. 하나의 database에 두 개의 서로 다른 Nextcloud 버전을 연결하면 데이터가 손상될 수 있습니다.
실제로 발생할 수 있는 오류
"Your data directory is readable by other users. Please change the permissions to 0770." Bind mount된 디렉토리에 group 또는 world read 권한이 설정되어 있습니다. 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." 요청에 포함된 hostname이 trusted_domains에 등록되어 있지 않습니다. NEXTCLOUD_TRUSTED_DOMAINS는 최초 설치 시에만 적용됩니다. 설치 이후에는 occ config:system:set trusted_domains 1 --value=cloud.example.com을 사용하여 설정하십시오.
502 Bad Gateway, /var/log/nginx/error.log에서 connect() failed (111: Connection refused) while connecting to upstream 발생. nginx가 127.0.0.1:8080에서 응답을 받지 못했습니다. 컨테이너가 아직 초기화 중이거나(docker compose logs app 확인), 컨테이너가 종료되었거나(docker compose ps), 또는 publish 라인이 proxy_pass 포트와 일치하지 않는 경우입니다. ss -ltnp | grep 8080으로 확인하십시오.
리다이렉트 루프 또는 관리자 개요의 "insecure" 경고. OVERWRITEPROTOCOL: https이 누락되었거나, TRUSTED_PROXIES에 Docker gateway 서브넷이 포함되지 않았습니다. 위의 proxy 섹션을 참조하십시오.
LockedException: "files/..." is locked. REDIS_HOST을 설정하면 이미지가 Redis를 locking backend로 구성하므로 stale lock이 거의 발생하지 않습니다. 설정하지 않으면 lock 정보가 oc_file_locks 데이터베이스 테이블에 저장되며, 쓰기 도중 중단된 요청이 행(row)을 남길 수 있습니다. 수동으로 lock 행을 삭제하기 전에, 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입니다. 행(row) 수가 증가하면 파일 목록 표시와 동기화 스캔 속도가 느려집니다. 해결 방법은 데이터베이스 최적화입니다. Postgres를 빠른 스토리지에 배치하고, 충분한 공유 메모리를 할당하십시오. 또한 보관 설정(retention settings)을 사용하여 불필요한 데이터와 버전이 계속 쌓이지 않도록 정리하십시오.
세 번째는 미리보기 생성 작업이 다른 작업과 자원을 경쟁하는 문제입니다. 소규모 서버에서는 미리보기 제공자(preview providers)를 최소화하고, 업무 시간 중에는 occ preview:generate-all을 실행하지 마십시오.
그 외의 경우, 추가 기능들은 별도의 서버에 설치하는 것이 좋습니다. Collabora와 전체 텍스트 검색(full-text search)은 고유한 메모리 프로필을 가진 별도의 상주 서비스입니다. 파일의 유일한 복사본이 있는 서버에 이 서비스들을 함께 실행하면 이점 없이 장애 범위(failure domain)만 넓어집니다. 볼륨 크기 조절이 적절하지 않다고 판단되면 파일 스토리지를 S3 호환 기본 스토리지로 이동하십시오. 이때 백업이 더 어려워진다는 점에 유의하십시오. 데이터베이스에 여전히 메타데이터가 저장되므로, 버킷과 함께 데이터베이스 덤프를 수행해야 합니다.
인스턴스가 실제 사용자를 서비스하기 시작하면 Uptime Kuma를 앞에 배치하십시오. 그래야 동기화 클라이언트보다 먼저 장애를 인지할 수 있습니다. 프라이빗 클라우드는 자체 메일 서버와 함께 사용하기 좋습니다. 서비스를 수동으로 연결하는 것이 번거롭다면, Cloudron, CasaOS, Coolify를 통해 자동화된 플랫폼을 비교해 보십시오.
FAQ
Postgres 대신 SQLite로 Nextcloud를 실행할 수 있습니까?
가능합니다. 공식 이미지는 이를 지원합니다. 하지만 단일 데스크톱 동기화 클라이언트가 병렬 요청을 보내면 SQLSTATE[HY000]: General error: 5 database is locked 오류와 HTTP 500 오류가 발생합니다. SQLite는 데이터베이스 전체에 쓰기 잠금을 수행합니다. Nextcloud는 파일 잠금, 활동 행, 작업 상태 기록 등을 위해 지속적으로 쓰기를 수행합니다. Postgres 또는 MariaDB로 시작하십시오. occ db:convert-type 기능이 존재하지만, 운영 중인 데이터에 대해 복잡하고 중단 없는 마이그레이션이 필요합니다.
Nextcloud VPS에는 실제로 얼마만큼의 RAM이 필요합니까?
사용자 수가 아닌 동시 접속자 수를 기준으로 크기를 결정하십시오. 최악의 경우 상주 메모리는 동시 요청 수에 PHP_MEMORY_LIMIT를 곱한 값에, Postgres shared buffers, 연결당 백엔드 1개, 그리고 프리뷰 생성 시 발생하는 스파이크 값을 더한 것과 비슷합니다. 2 GB 사양의 서버는 프리뷰를 제한하고 swap을 추가하면 소규모 가정용 인스턴스를 실행할 수 있습니다. Collabora 또는 전체 텍스트 검색을 추가하면 별도의 상주 서비스들을 위한 메모리가 추가로 필요합니다.
nginx 리버스 프록시 환경에서 대용량 업로드가 실패하는 이유는 무엇입니까?
프록시의 두 가지 설정이 원인인 경우가 많습니다. 기본값인 1 MB로 설정된 client_max_body_size은 요청을 잘라버립니다. 또한 짧은 proxy_read_timeout / proxy_send_timeout 값은 긴 전송 과정을 중간에 중단시킵니다. 두 값을 충분히 크게 설정하십시오. proxy_request_buffering off을 spool 대신 stream으로 설정하고, 앱 컨테이너의 PHP_UPLOAD_LIMIT도 이에 맞춰 높이십시오.
Nextcloud에서 리다이렉트 루프가 발생하거나 리버스 프록시 경고가 뜨는 이유는 무엇입니까?
컨테이너는 127.0.0.1에 있는 nginx를 직접 보지 못합니다. 대신 172.x 대역의 Docker bridge gateway를 봅니다. TRUSTED_PROXIES에 해당 주소가 없으면 X-Forwarded-Proto: https 헤더가 무시됩니다. 이로 인해 Nextcloud는 http:// URL을 생성하고, 프록시는 이를 다시 전달하며 루프가 발생합니다. TRUSTED_PROXIES을 실제 bridge 서브넷으로 설정하고 OVERWRITEPROTOCOL: https을 고정하십시오.
Nextcloud를 29 버전에서 31 버전으로 바로 업그레이드할 수 있습니까?
아니요. Nextcloud는 한 번의 업그레이드에 하나의 메이저 버전만 지원합니다. 버전을 건너뛰면 Updates between multiple major versions and downgrades are unsupported. 단계에서 중단되어 인스턴스가 유지 관리 모드(maintenance mode)에 빠집니다. 백업을 수행한 후, app 및 cron 서비스의 태그를 각각 한 단계씩 올리십시오. docker compose pull && docker compose up -d를 수행하고 occ status으로 확인한 뒤, 이 과정을 반복하십시오.