SSD Nodes Learn
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-07-24

نصب Nextcloud روی VPS با Docker

آموزش نصب Nextcloud با Docker Compose، استفاده از Postgres و Redis و راه‌اندازی TLS. یادگیری روش صحیح Backup برای جلوگیری از از دست رفتن داده‌ها در VPS.

آنچه در واقع در حال ساخت آن هستید

این راهنما Nextcloud را روی یک VPS با استفاده از Docker Compose اجرا می‌کند، پروتکل TLS از Let's Encrypt را در مقابل آن قرار می‌دهد و سیستمی برای پشتیبان‌گیری (Backup) که قابلیت بازیابی (Restore) واقعی داشته باشد، راه‌اندازی می‌کند. چهار کانتینر و یک پروکسی: ایمیج رسمی nextcloud که روی loopback گوش می‌دهد، Postgres که تمام متادیتای فایل‌ها را نگه می‌دارد، Redis که قفل‌های فایل (file locks) را مدیریت می‌کند، نسخه دومی از ایمیج Nextcloud که فقط حلقه cron را اجرا می‌کند، و nginx روی host که وظیفه پایان‌دهی TLS (TLS termination) را در مقابل همه آن‌ها بر عهده دارد. فرآیند نصب حدود 20 دقیقه زمان می‌برد، اما این بخش مهم نیست. دو تصمیم در ساعت اول تعیین می‌کنند که آیا یک سال دیگر فایل‌های خود را خواهید داشت یا خیر: استفاده از یک دیتابیس واقعی به جای SQLite، و پشتیبان‌گیری که دایرکتوری داده‌ها، دیتابیس و config.php را به عنوان یک مجموعه یکپارچه و سازگار ذخیره کند.

این فرآیند فرض را بر این می‌گذارد که از Ubuntu 24.04 LTS یا Debian 13 استفاده می‌کنید، Docker Engine به همراه پلاگین Compose v2 که از مخزن خود Docker نصب شده است، و یک رکورد DNS از نوع A (به همراه AAAA در صورت داشتن IPv6) که از قبل cloud.example.com را به VPS اشاره می‌کند. تمام این موارد به سروری نیاز دارد که تحت کنترل خودتان باشد؛ امکان انجام TLS termination و تهیه dump از دیتابیس در سرویس‌های SaaS دیگر وجود ندارد.

Sizing: آنچه واقعاً حافظه را مصرف می‌کند

مصرف حافظه در Nextcloud تحت تأثیر سه عامل اصلی است و هیچ‌کدام از آن‌ها خودِ "Nextcloud" نیستند.

PHP workers. ایمیج -apache هر درخواست همزمان را توسط یک پروسس worker که مفسر PHP را در خود دارد، سرویس‌دهی می‌کند. هر worker ممکن است تا سقف PHP_MEMORY_LIMIT رشد کند، پیش از آنکه PHP درخواست را متوقف کند. بدترین حالت مصرف حافظه resident تقریباً برابر است با تعداد درخواست‌های همزمان × محدودیت حافظه؛ و یک کلاینت همگام‌سازی دسکتاپ، چندین اتصال موازی برای هر کاربر باز می‌کند. این میزان همزمانی است که سقف مصرف را تعیین می‌کند، نه تعداد کاربران.

The database. سرویس Postgres برای هر اتصال یک backend ایجاد می‌کند و shared buffers را در حافظه نگه می‌دارد. حجم داده‌های کاری آن با تعداد فایل‌ها مقیاس‌بندی می‌شود، نه با تعداد بایت‌ها: oc_filecache به ازای هر فایل برای هر کاربر، یک سطر دارد. صد هزار فایل کوچک، دیتابیس سنگین‌تری نسبت به صد فایل بزرگ ایجاد می‌کند.

Preview generation. تولید یک تصویر بندانگشتی (thumbnail)، تصویر اصلی را با رزولوشن کامل در حافظه رمزگشایی می‌کند. پیش‌نمایش ویدیو از ffmpeg استفاده می‌کند. اجرای occ preview:generate-all این فشار ناگهانی را به صورت مکرر و پشت سر هم ایجاد می‌کند و رایج‌ترین دلیل برای فعال شدن OOM killer در VPSهای کوچک است.

Redis در مقایسه با موارد دیگر کم‌هزینه است. هر چیزی که بعداً اضافه کنید — مانند Collabora، جستجوی تمام‌متن (full-text search) یا اسکنر آنتی‌ویروس — یک سرویس resident مجزا با اثر حافظه مخصوص به خود است و باید پیش از فعال‌سازی، در برنامه تعیین ظرفیت شما لحاظ شود.

اگر با کمبود 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 killer در میانه‌ی ارتقا (upgrade)، بدتر است.

چرا SQLite دچار مشکل می‌شود

Nextcloud از SQLite پشتیبانی می‌کند و ایمیج رسمی آن بدون مشکل از آن استفاده خواهد کرد. اما این کار را انجام ندهید. SQLite عملیات نوشتن را با یک قفل در سطح کل پایگاه داده سریال‌سازی می‌کند: در هر لحظه فقط یک نویسنده برای کل فایل مجاز است. Nextcloud به طور مداوم در حال نوشتن است — قفل‌های فایل، ردیف‌های فعالیت، entries کش، و وضعیت Jobها — و یک کلاینت دسکتاپ که در حال همگام‌سازی یک ساختار درختی از پوشه‌ها است، درخواست‌های موازی زیادی ارسال می‌کند. در چنین الگویی، شما با خطای SQLSTATE[HY000]: General error: 5 database is locked و HTTP 500 مواجه می‌شوید؛ این شکست دقیقاً زمانی رخ می‌دهد که Instance شروع به کاربردی شدن می‌کند.

تبدیل بعدی با استفاده از occ db:convert-type امکان‌پذیر است، اما این یک مهاجرت طولانی و تمام‌یا-هیچ بر روی یک مجموعه داده زنده است. کار خود را با Postgres یا MariaDB شروع کنید.

The Compose file

این فایل را در /srv/nextcloud/compose.yaml قرار دهید، به همراه یک فایل .env هم‌سطحه برای secrets در حالت 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:

پیش از کپی کردن عین عبارت 31، تگ اصلی را ثابت نگه دارید و تگ فعلی را در Docker Hub بررسی کنید. در برخی نسخه‌های آتی از docker compose pull، دستور latest شما را به یک نسخه با تگ اصلی متفاوت منتقل می‌کند و Nextcloud از این حالت پشتیبانی نمی‌کند.

دایرکتوری داده (data directory) به عمد به صورت یک bind mount انتخاب شده است و نه یک named volume: مسیری که بتوانید مستقیماً یک ابزار پشتیبان‌گیری را به آن متصل کنید، ارزش بیشتری نسبت به مرتب بودن ساختار دارد. این دایرکتوری را با 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 ساده، بدون توجه به تنظیمات ufw، Nextcloud را بدون رمزنگاری در معرض اینترنت عمومی قرار می‌دهد. اتصال به loopback باعث می‌شود Nextcloud از رابط کاربری عمومی جدا بماند. در این صورت، فایروال فقط باید اجازه دسترسی به proxy را بدهد — و اگر نمی‌خواهید SSH را برای کل اینترنت باز بگذارید، اتصال به VPS از طریق یک 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 ساده برای port-80 با server_name مناسب ایجاد کنید، سپس اجازه دهید 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.com

Certbot خطوط ssl_certificate و redirect از :80 به :443 را اضافه می‌کند و یک systemd timer برای تمدید گواهی 90 روزه نصب می‌کند. با استفاده از systemctl list-timers | grep certbot وجود آن را تایید کنید؛ یک timer تمدید که هرگز فعال نشده باشد، مانند یک بمب ساعتی 90 روزه عمل می‌کند.

بلاک proxy:

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 و timeoutهای طولانی برای خواندن (read timeouts)، مانع از قطع شدن آپلودهای حجیم در میانه کار می‌شوند. proxy_request_buffering off به جای ذخیره کل فایل در دیسک proxy، آپلود را به صورت stream عبور می‌دهد.

استفاده از nginx روی host ساده‌ترین روش برای یک اپلیکیشن است. اگر قرار است Nextcloud با کانتینرهای دیگر در یک VPS مشترک باشد، running Traefik as a Docker Compose reverse proxy for multiple apps مسیریابی و صدور گواهی را به labelهای کانتینر منتقل می‌کند؛ در آنجا نیز مفاهیم مشابه client_max_body_size و timeout به صورت تنظیمات middleware و transport ظاهر می‌شوند.

trusted_proxies and overwriteprotocol

بیشتر نصب‌های self-hosted Nextcloud در این بخش دچار مشکل می‌شوند و علائم خطا با علت اصلی بی‌ارتباط به نظر می‌رسند.

مقدار X-Forwarded-Proto: https تنها زمانی اعمال می‌شود که درخواست از آدرسی ارسال شود که در trusted_proxies لیست شده باشد. اگر این مقدار اعمال نشود، Nextcloud تصور می‌کند درخواست از طریق HTTP معمولی ارسال شده و URLهای http:// را تولید می‌کند؛ سپس پروکسی آن‌ها را به HTTPS redirect می‌کند؛ مرورگر درخواست را دنبال می‌کند و Nextcloud مجدداً http:// را تولید می‌کند. این همان حلقه redirect loop است. مقدار OVERWRITEPROTOCOL: https پروتکل را بدون توجه به شرایط، ثابت نگه می‌دارد.

نکته در TRUSTED_PROXIES این است که آدرسی که Nextcloud مشاهده می‌کند، 127.0.0.1 نیست. nginx روی host اجرا می‌شود و به یک port منتشر شده متصل می‌گردد، بنابراین container درگاه Docker bridge gateway را می‌بیند — یعنی چیزی در محدوده 172.x. زیرشبکه (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 کل instance شما را مسدود می‌کند و در بخش admin overview با این پیام مواجه می‌شوید: "The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy."

مقدار OVERWRITECLIURL برای container مربوط به cron اهمیت دارد، زیرا این container هیچ درخواست ورودی برای استنتاج hostname ندارد. بدون این مقدار، کارهای پس‌زمینه (background jobs) لینک‌هایی به localhost تولید می‌کنند و اعلان‌های ایمیل شامل URLهای غیرقابل استفاده هستند.

Background jobs: cron, not AJAX

اجرای پیش‌فرض وظایف (jobs) در Nextcloud از طریق AJAX انجام می‌شود: وظایف به عنوان پیامد بارگذاری یک صفحه توسط کاربر اجرا می‌شوند. از آنجایی که در ساعت 04:00 کسی در سایت حضور ندارد، فرآیندهایی مانند انقضای زباله‌ها، پاکسازی نسخه‌ها، پیش‌نمایش‌ها و تلاش‌های مجدد برای اتصال‌های federated متوقف می‌شوند. اولین نشانه این مشکل، رشد بی‌رویه حجم دایرکتوری داده‌ها است. سرویس 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: سه مورد، یا هیچ‌کدام

یک backup که فقط شامل filesystem باشد، روی یک instance خراب بازگردانی نمی‌شود. دایرکتوری data شامل بایت‌ها است؛ Postgres شامل file cache، shares، کاربران و app state است؛ و config.php شامل credentials دیتابیس، instance ID و password salt است. اگر فایل‌ها را بدون دیتابیس restore کنید، Nextcloud نمی‌تواند آن‌ها را ببیند. اگر دیتابیس را بدون config.php restore کنید، دیتابیس باز نمی‌شود. اگر یک دیتابیس قدیمی را روی یک data directory جدیدتر restore کنید، با shareهایی مواجه می‌شوید که به فایل‌های جابه‌جا شده اشاره می‌کنند.

هر سه مورد را از یک instance در حالت quiesced backup بگیرید:

#!/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 باعث می‌شود dump و file copy با هم هماهنگ باشند. اگر این مرحله را نادیده بگیرید، در نهایت دیتابیسی را ذخیره می‌کنید که به فایلی اشاره می‌کند که rsync هنوز به آن نرسیده است. توجه داشته باشید که اسکریپت، database dumpهای دارای timestamp را نگه می‌دارد، اما فقط یک نسخه rolling mirror از دایرکتوری data دارد — rsync --delete در هر اجرا آن را overwrite می‌کند — بنابراین فقط جدیدترین dump با file copy مطابقت دارد.

سپس آن را از روی سیستم خارج کنید. بک‌آپی که روی همان VPS ذخیره شده که از آن بک‌آپ گرفته شده، یک copy است، نه یک backup. استفاده از restic در مقابل object storage یا یک host دوم، پاسخ معمول است؛ قابلیت deduplication آن، دایرکتوری data را بسیار بهتر از یک nightly tarball مدیریت می‌کند. تنظیمات کامل، از repository init تا nightly timer و تمرین restore، در off-box VPS backups with restic موجود است.

بازگردانی (Restore) صرفاً معکوس کردن فرآیند نیست. یک stack که تازه اجرا شده است، installer را اجرا کرده و یک config.php کاملاً جدید — شامل instance ID و password salt جدید — می‌سازد؛ وارد کردن dump روی آن هویت جدید، باعث ایجاد sessionها و share tokenهای خراب می‌شود. ابتدا هویت قدیمی را به این ترتیب بازگردانی کنید:

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 فایل cache را با آنچه واقعاً روی دیسک است، تطبیق می‌دهد. قبل از اینکه به آن نیاز پیدا کنید، این فرآیند را یک بار روی یک VPS اضافه تمرین کنید.

ارتقا: در هر مرحله فقط یک نسخه اصلی

Nextcloud فقط از ارتقا به یک نسخه اصلی در هر مرحله پشتیبانی می‌کند. پرش مستقیم از نسخه 29 به 31 با خطا مواجه می‌شود؛ این خطا با کد Exception: Updates between multiple major versions and downgrades are unsupported. نمایش داده شده و سیستم را در حالت maintenance mode قرار می‌دهد.

مراحل ارتقا در Docker به این صورت است: ابتدا یک backup تهیه کنید، سپس tag را در هر دو سرویس app و cron از 31 به 32 تغییر دهید، سپس docker compose pull && docker compose up -d و در نهایت docker compose logs -f app را اجرا کنید. بخش image entrypoint، کدهای جدید را با داده‌های موجود تطبیق داده و دستور occ upgrade را به صورت خودکار اجرا می‌کند. فرآیند را متوقف نکنید. پس از توقف فعالیت در logs، دستور docker compose exec -u www-data app php occ status را اجرا کنید و وضعیت versionstring و فعال بودن apps را بررسی کنید.

دو قانون برای جلوگیری از خطا: ابتدا یک نسخه اصلی را ارتقا دهید و آن را تایید کنید، سپس نسخه بعدی را ارتقا دهید. همچنین هرگز tag سرویس app را بدون تغییر دادن cron برای مطابقت با آن، ویرایش نکنید؛ استفاده از دو نسخه متفاوت از Nextcloud برای یک database واحد، باعث خرابی داده‌ها می‌شود.

خطاهایی که واقعاً مشاهده خواهید کرد

"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 هرگز آن را مقداردهی اولیه نکرده است — یک غلط تایپی در مسیر، یا جایگزینی یک دایرکتوری خالی و جدید به جای یک instance فعال. بررسی کنید که مسیر host با خط volume مطابقت داشته باشد.

"Access through untrusted domain." نام میزبان (hostname) در درخواست، در trusted_domains وجود ندارد. NEXTCLOUD_TRUSTED_DOMAINS فقط در اولین نصب اعمال می‌شود؛ پس از آن، آن را به صورت live تنظیم کنید: 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)، و یا خط publish با پورت proxy_pass مطابقت ندارد. با استفاده از ss -ltnp | grep 8080 تایید کنید.

یک حلقه بازگشتی (redirect loop)، یا هشدارهای "insecure" در نمای کلی ادمین. OVERWRITEPROTOCOL: https وجود ندارد، یا TRUSTED_PROXIES شامل زیرشبکه (subnet) Docker gateway نیست. بخش proxy در بالا را ببینید.

LockedException: "files/..." is locked. با تنظیم REDIS_HOST، ایمیج Redis را به عنوان backend برای locking پیکربندی می‌کند و قفل‌های قدیمی (stale locks) نادر هستند. بدون آن، قفل‌ها در جدول دیتابیس 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 شامل تغییر اندازه Volume و سپس گسترش Filesystem است؛ این فرآیند زمانی که دیسک 100% پر باشد، بسیار دشوارتر خواهد بود. همین حالا برای میزان استفاده از دیسک هشدار (Alert) تنظیم کنید، نه بعداً.

مانع دوم oc_filecache است. با افزایش تعداد ردیف‌ها، لیست کردن فایل‌ها و اسکن‌های همگام‌سازی (Sync scans) کند می‌شوند. راه حل این مشکل، مدیریت دیتابیس است: Postgres را روی حافظه پرسرعت نگه دارید، اجازه دهید از Shared memory کافی استفاده کند، و به جای انباشته شدن همیشگی، با استفاده از تنظیمات Retention، داده‌های اضافی و نسخه‌های قدیمی را پاکسازی کنید.

مانع سوم، رقابت فرآیند تولید پیش‌نمایش (Preview generation) با سایر فرآیندها است. در سرورهای کوچک، تعداد Preview providerها را محدود نگه دارید و هرگز occ preview:generate-all را در ساعات کاری اجرا نکنید.

فراتر از آن، پاسخ صادقانه این است که سرویس‌های جانبی به ماشین اختصاصی خود نیاز دارند. Collabora و Full-text search سرویس‌های مقیم جداگانه‌ای با پروفایل حافظه متفاوت هستند؛ قرار دادن آن‌ها در همان ماشینی که تنها کپی فایل‌های شما را نگه می‌دارد، بدون هیچ فایده‌ای، محدوده شکست (Failure domain) را بزرگتر می‌کند. هرگاه حجم دیسک دیگر مناسب نبود، ذخیره‌سازی فایل‌ها را به یک Primary storage سازگار با S3 منتقل کنید. توجه داشته باشید که این کار پشتیبان‌گیری را سخت‌تر می‌کند، نه آسان‌تر: دیتابیس همچنان متادیتا را نگه می‌دارد و باید همزمان با Bucket، از آن Dump تهیه شود.

زمانی که Instance شروع به سرویس‌دهی به کاربران واقعی کرد، Uptime Kuma را در مقابل آن قرار دهید تا قبل از کلاینت‌های Sync، از قطعی سرویس باخبر شوید. یک ابر خصوصی (Private cloud) با Mail server اختصاصی خود ترکیب خوبی دارد؛ و اگر نمی‌خواهید سرویس‌ها را به صورت دستی به هم متصل کنید، Cloudron, CasaOS and Coolify پلتفرم‌هایی هستند که این کار را برای شما انجام می‌دهند.

FAQ

آیا می‌توانم Nextcloud را به جای Postgres روی SQLite اجرا کنم؟

بله، امکان‌پذیر است و ایمیج رسمی نیز این اجازه را می‌دهد، اما یک کلاینت همگام‌سازی دسکتاپ که درخواست‌های موازی ارسال می‌کند، با خطای SQLSTATE[HY000]: General error: 5 database is locked و HTTP 500 مواجه خواهد شد. SQLite برای عملیات نوشتن، کل پایگاه داده را قفل می‌کند و Nextcloud به طور مداوم در حال نوشتن است (قفل فایل‌ها، ردیف‌های فعالیت، وضعیت Jobها). کار خود را با Postgres یا MariaDB شروع کنید؛ راهکار occ db:convert-type وجود دارد اما یک مهاجرت طولانی و بدون بازگشت روی داده‌های زنده است.

یک VPS برای Nextcloud واقعاً به چه میزان RAM نیاز دارد؟

میزان حافظه باید بر اساس تعداد درخواست‌های همزمان تعیین شود، نه تعداد کاربران. در بدترین حالت، حافظه resident تقریباً برابر است با تعداد درخواست‌های همزمان ضرب در PHP_MEMORY_LIMIT، به علاوه Postgres shared buffers و یک backend برای هر اتصال، و همچنین مقداری حافظه اضافی برای پیک‌های تولید پیش‌نمایش (preview generation). یک سرور 2 GB می‌تواند یک نسخه خانگی کوچک را اجرا کند، به شرطی که تولید پیش‌نمایش را محدود کرده و swap اضافه کنید؛ اگر Collabora یا جستجوی تمام‌متن (full-text search) را اضافه کنید، باید برای مجموعه‌ای از سرویس‌های resident دیگر نیز فضا در نظر بگیرید.

چرا آپلودهای حجیم در پشت nginx reverse proxy با خطا مواجه می‌شوند؟

معمولاً دو تنظیم در پروکسی علت این مشکل هستند: اگر client_max_body_size روی مقدار پیش‌فرض 1 MB باقی بماند، درخواست قطع می‌شود، و مقادیر کوتاه در proxy_read_timeout / proxy_send_timeout باعث قطع انتقال‌های طولانی در میانه کار می‌شوند. هر دو را با مقدار بالا تنظیم کنید، proxy_request_buffering off را به جای spool روی stream قرار دهید، و PHP_UPLOAD_LIMIT را در کانتینر اپلیکیشن متناسب با آن افزایش دهید.

چرا Nextcloud در یک حلقه (loop) بازنشانی می‌شود یا درباره reverse proxy هشدار می‌دهد؟

کانتینر، nginx را در 127.0.0.1 نمی‌بیند، بلکه درگاه Docker bridge را در محدوده 172.x می‌بیند. وقتی آن آدرس در TRUSTED_PROXIES موجود نباشد، هدر X-Forwarded-Proto: https نادیده گرفته می‌شود، Nextcloud آدرس‌های http:// را تولید می‌کند و پروکسی آن‌ها را به عقب برمی‌گرداند. TRUSTED_PROXIES را روی زیرشبکه (subnet) واقعی 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 بررسی کنید و سپس همین مراحل را تکرار کنید.