SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-22

Cài Nextcloud trên VPS bằng Docker, TLS và backup

Chạy Nextcloud trên VPS với Docker Compose, Postgres, Redis và nginx TLS; hướng dẫn backup, restore và upgrade để data, database, config luôn khôi phục được.

Bạn thực sự đang xây dựng gì

Hướng dẫn này chạy Nextcloud trên VPS bằng Docker Compose, đặt TLS của Let's Encrypt ở phía trước, và thiết lập một bản backup có thể restore thực sự. Có 4 container và một proxy: image nextcloud chính thức lắng nghe trên loopback, Postgres lưu toàn bộ metadata của file, Redis lưu file lock, một bản sao thứ hai của image Nextcloud chỉ chạy vòng lặp cron, và nginx trên host thực hiện TLS termination ở phía trước toàn bộ hệ thống. Việc cài đặt chỉ mất 20 phút và không phải phần quan trọng. Hai quyết định trong giờ đầu tiên sẽ quyết định sau 1 năm bạn còn giữ được các file hay không: dùng database thực thay vì SQLite, và dùng bản backup chứa data directory, database và config.php trong cùng một tập nhất quán.

Hướng dẫn này giả định bạn dùng Ubuntu 24.04 LTS hoặc Debian 13, Docker Engine có cài plugin Compose v2 từ repository chính thức của Docker, và bản ghi DNS A (cộng thêm AAAA nếu bạn dùng IPv6) đã trỏ cloud.example.com đến VPS. Toàn bộ hệ thống cần chạy trên server do bạn kiểm soát. Không có cách nào thực hiện TLS termination và database dump trên SaaS của người khác.

Sizing: những thành phần thực sự tiêu tốn memory

Nextcloud chủ yếu dùng memory cho 3 thành phần, và không thành phần nào trong số đó thực sự là “Nextcloud”.

PHP worker. Image -apache xử lý từng request đồng thời bằng một worker process chứa PHP interpreter. Mỗi worker có thể tăng lên đến PHP_MEMORY_LIMIT trước khi PHP dừng request. Resident memory trong trường hợp xấu nhất xấp xỉ số request đồng thời × memory limit, và desktop sync client mở nhiều kết nối song song cho mỗi user. Chính concurrency, không phải số user, quyết định mức trần.

Database. Postgres tạo một backend cho mỗi connection và giữ shared buffer trong memory. Working set của database tăng theo số file, không phải số byte: oc_filecache lưu một row cho mỗi file của mỗi user. Một trăm nghìn file nhỏ tạo ra database nặng hơn một trăm file lớn.

Tạo preview. Khi tạo thumbnail, hệ thống giải mã ảnh nguồn vào memory ở độ phân giải đầy đủ. Video preview gọi ffmpeg dưới dạng shell command. Chạy occ preview:generate-all sẽ lặp lại việc tăng memory đột biến này liên tục. Đây là cách phổ biến nhất khiến một VPS nhỏ bị OOM killer.

Redis tương đối nhẹ. Mọi thành phần bạn cài thêm sau đó, như Collabora, full-text search hoặc antivirus scanner, đều là một service resident riêng với footprint riêng. Bạn phải đưa chúng vào kế hoạch sizing trước khi enable.

Nếu RAM bị thiếu, hãy giảm PHP_MEMORY_LIMIT, giới hạn preview_max_x / preview_max_y / preview_max_filesize_image, rút gọn enabledPreviewProviders còn những format bạn thực sự duyệt, đồng thời đặt trashbin_retention_obligationversions_retention_obligation để data directory không âm thầm tăng lên gấp nhiều lần kích thước file. Hãy thêm một swap file. Swap chậm, nhưng bị OOM kill giữa lúc upgrade còn tệ hơn.

Vì sao SQLite gây lỗi

Nextcloud có hỗ trợ SQLite và image chính thức sẽ tự động sử dụng nó. Không nên dùng như vậy. SQLite tuần tự hóa các thao tác ghi bằng một lock áp dụng cho toàn bộ database: mỗi lần chỉ có một tiến trình ghi được vào toàn bộ file. Nextcloud liên tục ghi dữ liệu, gồm file lock, các bản ghi activity, cache và trạng thái job. Chỉ một desktop client đồng bộ một cây thư mục cũng có thể tạo ra nhiều request song song. Với mẫu tải này, bạn sẽ gặp SQLSTATE[HY000]: General error: 5 database is locked và lỗi HTTP 500. Lỗi thường xuất hiện đúng lúc instance bắt đầu được sử dụng thường xuyên.

Sau đó vẫn có thể chuyển đổi bằng occ db:convert-type, nhưng đây là một quá trình migration dài, phải thực hiện toàn bộ hoặc không thực hiện gì trên dataset đang hoạt động. Hãy bắt đầu với Postgres hoặc MariaDB.

Tệp Compose

Đặt nội dung này vào /srv/nextcloud/compose.yaml, với các secret trong tệp .env cùng thư mục và đặt mode là 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:

Cố định major tag và kiểm tra tag hiện tại trên Docker Hub trước khi sao chép nguyên văn 31. latest có thể đưa bạn qua một major boundary trong một docker compose pull tương lai, và Nextcloud không hỗ trợ việc đó.

Thư mục dữ liệu là bind mount, không phải named volume, có chủ ý: một path có thể trỏ trực tiếp đến đó từ backup tool có giá trị hơn sự gọn gàng. Tạo thư mục bằng UID www-data của image và đặt permissions theo yêu cầu của Nextcloud:

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

Lưu ý phần publish port: 127.0.0.1:8080:80. Docker publish port bằng cách ghi các rule DNAT được đánh giá trước khi packet đến chain INPUT của ufw, nên một 8080:80 đơn giản sẽ đưa Nextcloud không mã hóa lên public internet bất kể ufw được cấu hình thế nào. Bind vào loopback giúp dịch vụ không xuất hiện trên public interface. Khi đó firewall chỉ cần cho phép proxy; nếu không muốn để SSH mở cho toàn bộ internet, truy cập VPS qua self-hosted WireGuard VPN cho phép bạn xóa port 22 khỏi các rule public hoàn toàn:

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

Khởi động bằng docker compose up -d, sau đó theo dõi docker compose logs -f app. Ở lần boot đầu tiên, container sao chép toàn bộ application tree vào volume và chạy installer; container sẽ không trả lời request nào cho đến khi quá trình này hoàn tất.

TLS và reverse proxy

Cài nginx và certbot từ repository của distro, tạo một server block port 80 không có TLS với server_name phù hợp, rồi để certbot tự chỉnh sửa block đó. Cơ chế HTTP-01 challenge, renewal timer và các trường hợp lỗi được trình bày đầy đủ trong cấp chứng chỉ Let's Encrypt bằng certbot và nginx trên Ubuntu 24.04:

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

Certbot thêm các dòng ssl_certificate và redirect từ :80:443, đồng thời cài một systemd timer để gia hạn chứng chỉ 90 ngày. Xác nhận timer tồn tại bằng systemctl list-timers | grep certbot. Một renewal timer chưa từng được enable là bộ hẹn giờ 90 ngày trước khi chứng chỉ hết hạn.

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

Trên nginx 1.25 trở lên, thêm http2 on;. Ubuntu 24.04 dùng build cũ hơn, trong đó tùy chọn tương đương là listen 443 ssl http2;. nginx -t sẽ cho biết build của bạn chấp nhận tùy chọn nào.

client_max_body_size và các timeout đọc dài giúp upload lớn không bị lỗi giữa chừng. proxy_request_buffering off stream dữ liệu upload trực tiếp thay vì ghi toàn bộ file vào disk của proxy trước.

nginx chạy trên host là lựa chọn đơn giản nhất cho một app. Nếu Nextcloud sẽ dùng chung VPS với các container khác, chạy Traefik làm reverse proxy Docker Compose cho nhiều app sẽ chuyển việc định tuyến và cấp chứng chỉ vào các label của container. Các vấn đề tương tự về client_max_body_size và timeout cũng xuất hiện ở đó dưới dạng middleware và transport settings.

trusted_proxies và overwriteprotocol

Đây là nơi phần lớn instance Nextcloud tự host bị lỗi. Triệu chứng thường không liên quan rõ ràng đến nguyên nhân.

X-Forwarded-Proto: https chỉ được áp dụng khi request đến từ một địa chỉ có trong trusted_proxies. Khi không được áp dụng, Nextcloud cho rằng request là HTTP thuần và tạo URL http://; proxy redirect các URL đó sang HTTPS; browser follow redirect; Nextcloud lại tạo http://. Đó là redirect loop. OVERWRITEPROTOCOL: https luôn cố định scheme, bất kể điều gì xảy ra.

Điểm dễ nhầm trong TRUSTED_PROXIES là địa chỉ Nextcloud nhìn thấy không phải 127.0.0.1. nginx chạy trên host và kết nối đến một published port, nên container nhìn thấy Docker bridge gateway, thuộc một địa chỉ trong 172.x. Tìm subnet thực tế:

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

Đưa CIDR đó, hoặc 172.16.0.0/12 bao phủ CIDR đó, vào TRUSTED_PROXIES. Nếu đặt phạm vi quá rộng, bất kỳ client nào cũng có thể giả mạo X-Forwarded-For. Nếu đặt sai, mọi lần đăng nhập đều có vẻ đến từ gateway address, cơ chế chống brute-force sẽ chặn toàn bộ instance cùng lúc, và phần tổng quan dành cho admin sẽ hiển thị "Cấu hình header của reverse proxy không đúng hoặc bạn đang truy cập Nextcloud từ một trusted proxy."

OVERWRITECLIURL quan trọng đối với cron container vì container này không nhận request đến để suy ra hostname. Nếu thiếu cấu hình này, background job sẽ tạo link đến localhost và email notification sẽ chứa các URL không thể sử dụng.

Tác vụ nền: dùng cron, không dùng AJAX

Trình chạy job mặc định của Nextcloud là AJAX: job chạy như một tác dụng phụ khi có người tải một trang. Không ai duyệt web lúc 04:00, nên việc hết hạn trash, dọn versions, tạo previews và retry liên kết liên kết bị đình trệ. Dấu hiệu đầu tiên là data directory không ngừng tăng kích thước. Service cron ở trên chạy vòng lặp /cron.sh chính thức trên cùng các volume. Cho Nextcloud biết nó sẽ dùng cách này:

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

Mọi lệnh occ đều theo dạng đó: docker compose exec -u www-data app php occ <command>. Nên tạo alias cho lệnh này.

Sao lưu: phải có đủ ba phần

Sao lưu chỉ filesystem sẽ khôi phục thành một instance bị hỏng. Thư mục dữ liệu chứa các byte; Postgres chứa file cache, share, user và trạng thái của app; config.php chứa thông tin xác thực database, instance ID và password salt. Khôi phục file mà không có database thì Nextcloud không thể nhìn thấy chúng. Khôi phục database mà không có config.php thì database không thể mở được. Khôi phục database cũ lên một thư mục dữ liệu mới hơn sẽ tạo ra các share trỏ đến những file đã được di chuyển.

Sao lưu cả ba phần từ một instance đã tạm dừng hoạt động:

#!/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 giúp dump và bản sao file nhất quán với nhau. Bỏ qua bước này, sớm muộn bạn cũng sẽ tạo một database tham chiếu đến một file mà rsync chưa sao chép xong. Lưu ý rằng script giữ các database dump có timestamp nhưng chỉ duy trì một mirror luân phiên của thư mục dữ liệu; rsync --delete ghi đè lên mirror này trong mỗi lần chạy, nên chỉ dump mới nhất khớp với bản sao file.

Sau đó đưa bản sao lưu ra khỏi máy. Backup nằm trên cùng VPS với dữ liệu được sao lưu chỉ là một bản copy, không phải backup. restic đến object storage hoặc host thứ hai là cách thường dùng, và cơ chế deduplication của nó xử lý thư mục dữ liệu tốt hơn nhiều so với tarball chạy mỗi đêm. Toàn bộ thiết lập, từ khởi tạo repository đến nightly timer và quy trình restore, có trong sao lưu VPS ra máy khác bằng restic.

Restore không đơn giản là thực hiện ngược lại. Một stack mới khởi động sẽ chạy installer và ghi một config.php hoàn toàn mới, cùng instance ID và password salt mới. Import dump lên identity mới này sẽ làm hỏng session và share token. Trước tiên hãy khôi phục identity cũ theo thứ tự sau:

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 đối chiếu file cache với những gì thực sự có trên disk. Hãy diễn tập quy trình này một lần trên một VPS dự phòng trước khi bạn cần dùng đến nó. Sự phân tách giữa byte trên disk và metadata trong Postgres cũng chi phối mọi app khác có cấu trúc tương tự. Vì vậy, backup Immich chỉ sao lưu library mà không có database sẽ khôi phục thành timeline trống.

Nâng cấp: mỗi lần chỉ nâng 1 major version

Nextcloud chỉ hỗ trợ nâng cấp đúng 1 major version mỗi lần. Nâng thẳng từ 29 lên 31 không xử lý lỗi an toàn; nó sẽ fail với Exception: Updates between multiple major versions and downgrades are unsupported. và để hệ thống ở maintenance mode.

Quy trình nâng cấp Docker là: tạo backup, sửa tag từ 31 thành 32 trong cả 2 service appcron, sau đó chạy docker compose pull && docker compose up -d rồi docker compose logs -f app. Entrypoint của image phát hiện code mới hơn so với data hiện có và tự chạy occ upgrade. Không được ngắt quá trình này. Khi log không còn hoạt động, chạy docker compose exec -u www-data app php occ status, kiểm tra versionstring và xác nhận các app đã được bật lại.

Có 2 quy tắc giúp tránh sự cố: nâng 1 major, kiểm tra, rồi mới nâng major tiếp theo. Không bao giờ chỉ sửa tag trên service app mà không sửa cron cho khớp. Dùng 2 phiên bản Nextcloud khác nhau với cùng một database có thể làm hỏng dữ liệu.

Các lỗi bạn sẽ thực sự gặp

"Your data directory is readable by other users. Please change the permissions to 0770." Thư mục bind mount có bit quyền đọc cho group hoặc mọi user. sudo chmod 0770 /srv/nextcloud/datasudo chown -R 33:33 /srv/nextcloud/data.

"Your data directory is invalid. Ensure there is a file called .ocdata in the root." Bind mount trỏ đến vị trí mà Nextcloud chưa từng khởi tạo, đường dẫn bị gõ sai hoặc một thư mục rỗng mới đã được gắn thay cho instance đang hoạt động. Kiểm tra đường dẫn trên host có khớp với dòng volume hay không.

"Access through untrusted domain." Hostname trong request không nằm trong trusted_domains. NEXTCLOUD_TRUSTED_DOMAINS chỉ có tác dụng trong lần cài đặt đầu tiên; sau đó hãy cập nhật trực tiếp khi hệ thống đang chạy: occ config:system:set trusted_domains 1 --value=cloud.example.com.

502 Bad Gateway, với connect() failed (111: Connection refused) while connecting to upstream trong /var/log/nginx/error.log. nginx không kết nối được đến tiến trình nào trên 127.0.0.1:8080. Container có thể vẫn đang khởi tạo (kiểm tra docker compose logs app), đã thoát (docker compose ps) hoặc dòng publish không khớp với cổng proxy_pass. Xác nhận bằng ss -ltnp | grep 8080.

Vòng lặp redirect hoặc cảnh báo "insecure" trong phần tổng quan admin. OVERWRITEPROTOCOL: https chưa được cấu hình hoặc TRUSTED_PROXIES không chứa subnet gateway của Docker. Xem lại phần proxy ở trên.

LockedException: "files/..." is locked. Khi đã đặt REDIS_HOST, image sẽ cấu hình Redis làm backend khóa và tình trạng lock cũ sẽ hiếm khi xảy ra. Nếu không đặt, lock được lưu trong bảng database oc_file_locks và một request bị kill giữa lúc ghi sẽ để lại các row ở đó. Hãy xác nhận Redis thực sự đang được sử dụng; occ config:system:get memcache.locking phải trả về class Redis, trước khi bạn xóa các row lock thủ công.

"The PHP memory limit is below the recommended value of 512MB." Tăng PHP_MEMORY_LIMIT rồi tạo lại container. Hãy nhớ giới hạn trần trong trường hợp xấu nhất sẽ thay đổi như thế nào.

Điều gì sẽ gây vấn đề khi mở rộng

Vấn đề đầu tiên là thư mục dữ liệu lớn hơn volume. Mở rộng volume trên VPS gồm resize volume rồi mở rộng filesystem. Thực hiện theo lịch sẽ ít rủi ro hơn nhiều so với chờ đến khi volume đầy 100%. Hãy cảnh báo về mức sử dụng disk ngay bây giờ, không phải sau này.

Vấn đề thứ hai là oc_filecache. Việc liệt kê file và quét để sync sẽ chậm dần khi số dòng tăng. Cách xử lý là tối ưu database: đặt Postgres trên storage nhanh, cấp đủ shared memory cho nó, đồng thời dọn trash và version bằng các thiết lập retention thay vì để chúng tích tụ vô thời hạn.

Vấn đề thứ ba là việc tạo preview tranh chấp tài nguyên với mọi tác vụ khác. Trên máy cấu hình nhỏ, hãy giới hạn các preview provider và không bao giờ chạy occ preview:generate-all trong giờ làm việc. Nếu phần lớn dữ liệu bạn lưu là ảnh từ camera roll của điện thoại, tác vụ tạo thumbnail nên được chuyển sang một photo server chuyên dụng. Bài viết so sánh PhotoPrism và Immich về RAM, ứng dụng điện thoại và lệnh backup trình bày chi phí của từng lựa chọn khi chạy cạnh một Nextcloud box.

Ngoài các vấn đề đó, câu trả lời thực tế là các tính năng bổ sung nên chạy trên máy riêng. Collabora và full-text search là các service resident riêng, mỗi service có mức sử dụng memory khác nhau. Đặt chúng trên cùng box đang lưu bản sao duy nhất của file sẽ làm phạm vi ảnh hưởng khi lỗi lớn hơn mà không đem lại lợi ích. Nếu bạn cần chỉnh sửa tài liệu trong trình duyệt, mức RAM tối thiểu và giới hạn connection của vendor trong bài viết so sánh OnlyOffice với Collabora sẽ quyết định VPS 2 đến 4 GB có chạy được lựa chọn nào hay không. Chuyển file storage sang primary storage tương thích S3 khi volume không còn phù hợp với nhu cầu. Lưu ý rằng cách này khiến backup khó hơn, không phải dễ hơn: database vẫn chứa metadata và phải được dump đồng bộ với bucket.

Khi instance bắt đầu phục vụ user thực, hãy đặt Uptime Kuma ở phía trước để bạn nhận biết downtime trước các sync client. Private cloud kết hợp tốt với mail server riêng của bạn. Nếu không muốn tự kết nối các service bằng tay, Cloudron, CasaOS và Coolify so sánh các platform có thể tự thực hiện việc đó. Nếu self-hosted search engine là mục tiếp theo, hãy chuẩn bị cho một loại vấn đề khác với các vấn đề trên: lỗi 429 của SearXNG có thể xuất phát từ rate limiter của chính nó hoặc do upstream engine chặn IP VPS của bạn. Chỉ log mới cho biết nguyên nhân nào đúng.

FAQ

Tôi có thể chạy Nextcloud trên SQLite thay vì Postgres không?

Có. Official image hỗ trợ cách này, nhưng chỉ một desktop sync client gửi các request song song cũng sẽ gặp SQLSTATE[HY000]: General error: 5 database is locked và lỗi HTTP 500. SQLite khóa ghi trên toàn bộ database, trong khi Nextcloud liên tục ghi dữ liệu như file lock, activity row và trạng thái job. Hãy bắt đầu với Postgres hoặc MariaDB; occ db:convert-type có tồn tại, nhưng đây là quá trình migration dài và phải thực hiện toàn bộ trên dữ liệu đang hoạt động.

VPS chạy Nextcloud thực tế cần bao nhiêu RAM?

Hãy sizing theo mức độ đồng thời, không phải theo số lượng user. Bộ nhớ resident trong trường hợp xấu nhất gần bằng số request đồng thời nhân với PHP_MEMORY_LIMIT, cộng với shared buffer của Postgres, một backend cho mỗi connection và mức tăng đột biến do tạo preview. Máy 2 GB có thể chạy instance nhỏ cho một gia đình nếu giới hạn việc tạo preview và thêm swap. Nếu thêm Collabora hoặc full-text search, bạn phải sizing thêm một nhóm service resident thứ hai.

Tại sao upload lớn bị lỗi phía sau nginx reverse proxy?

Thường có 2 setting trên proxy gây ra lỗi này: client_max_body_size vẫn để mặc định 1 MB sẽ cắt ngắn request, còn giá trị proxy_read_timeout / proxy_send_timeout quá thấp sẽ dừng transfer dài giữa chừng. Hãy đặt cả 2 giá trị đủ lớn, bật proxy_request_buffering off để stream thay vì spool, đồng thời tăng PHP_UPLOAD_LIMIT trên app container cho khớp.

Tại sao Nextcloud redirect lặp hoặc cảnh báo về reverse proxy?

Container không thấy nginx tại 127.0.0.1 mà thấy Docker bridge gateway, nằm ở đâu đó trong 172.x. Khi địa chỉ đó không có trong TRUSTED_PROXIES, header X-Forwarded-Proto: https bị bỏ qua, Nextcloud tạo URL http:// và proxy lại chuyển chúng quay ngược. Hãy đặt TRUSTED_PROXIES theo subnet bridge thực tế và cố định OVERWRITEPROTOCOL: https.

Tôi có thể nâng cấp Nextcloud trực tiếp từ 29 lên 31 không?

Không. Nextcloud chỉ hỗ trợ nâng cấp từng major version một. Nếu bỏ qua version, quá trình sẽ dừng với Updates between multiple major versions and downgrades are unsupported. và instance bị giữ ở maintenance mode. Hãy backup, tăng tag lên major version kế tiếp trên cả 2 service appcron, docker compose pull && docker compose up -d, xác minh bằng occ status, rồi lặp lại quy trình.