SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor

آموزش نصب Nextcloud روی VPS با Docker و تنظیمات امنیتی

راهنمای کامل اجرای Nextcloud با Docker Compose، Postgres و Redis روی سرور شخصی. یاد بگیرید چگونه با مدیریت صحیح TLS و پشتیبان‌گیری اصولی، از سلامت داده‌ها در Ubuntu 24.04 مطمئن شوید.

آنچه در واقع می‌سازید

این راهنما Nextcloud را روی یک VPS با استفاده از Docker Compose اجرا می‌کند، TLS مربوط به Let's Encrypt را در مقابل آن قرار می‌دهد و یک سیستم پشتیبان‌گیری که واقعاً قابل بازیابی باشد، راه‌اندازی می‌کند. این ساختار شامل چهار کانتینر و یک پروکسی است: ایمیج رسمی nextcloud که روی loopback گوش می‌دهد، Postgres که تمام متادیتای فایل‌ها را نگهداری می‌کند، Redis که قفل فایل‌ها را مدیریت می‌کند، یک کپی دوم از ایمیج Nextcloud که صرفاً حلقه cron را اجرا می‌کند، و nginx روی میزبان که TLS را برای همه آن‌ها مدیریت می‌کند. نصب اولیه حدود 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 دیگران وجود ندارد.

تخمین اندازه: چه چیزی واقعاً حافظه را مصرف می‌کند

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

ورکرهای PHP. ایمیج -apache هر درخواست همزمان را توسط یک پردازش ورکر که مفسر PHP را در خود نگه می‌دارد، پاسخ می‌دهد. هر ورکر ممکن است تا PHP_MEMORY_LIMIT رشد کند تا زمانی که PHP درخواست را متوقف کند. بدترین حالت مصرف حافظه (Resident Memory) تقریباً برابر است با تعداد درخواست‌های همزمان × محدودیت حافظه، و کلاینت همگام‌سازی دسکتاپ به ازای هر کاربر چندین اتصال موازی باز می‌کند. این «تعداد درخواست‌های همزمان» است که سقف مصرف را تعیین می‌کند، نه تعداد کاربران.

پایگاه داده. Postgres به ازای هر اتصال یک پردازش backend ایجاد می‌کند و بافرهای اشتراکی را در حافظه نگه می‌دارد. مجموعه کاری (Working Set) آن با تعداد فایل‌ها مقیاس می‌شود، نه تعداد بایت‌ها: oc_filecache به ازای هر فایل برای هر کاربر یک ردیف در جدول دارد. صد هزار فایل کوچک، پایگاه داده سنگین‌تری نسبت به صد فایل بزرگ ایجاد می‌کند.

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

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

اگر با کمبود RAM مواجه هستید، این اهرم‌ها را به کار بگیرید: کاهش PHP_MEMORY_LIMIT، محدود کردن preview_max_x / preview_max_y / preview_max_filesize_image، محدود کردن enabledPreviewProviders به فرمت‌هایی که واقعاً مرور می‌کنید، و تنظیم trashbin_retention_obligation و versions_retention_obligation به گونه‌ای که دایرکتوری داده به‌طور نامحسوس به چندین برابر حجم فایل‌های شما نرسد. یک فایل Swap اضافه کنید. Swap کند است، اما متوقف شدن سرویس توسط OOM killer در میانه عملیات ارتقا، وضعیت بدتری است.

چرا SQLite دچار اختلال می‌شود

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

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

فایل Compose

این محتوا را در /srv/nextcloud/compose.yaml قرار دهید، و secrets را در یک فایل هم‌سطح به نام .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:35-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:35-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) را ثابت نگه دارید و پیش از کپی کردن دقیق 35، نسخه فعلی را در Docker Hub بررسی کنید. تا تاریخ 21 اکتبر 2026، Nextcloud نسخه‌های 33، 34 و 35 را پشتیبانی می‌کند. نسخه 31 در فوریه 2026 و نسخه 32 در سپتامبر 2026 به پایان عمر خود رسیده‌اند؛ بنابراین تگی که از یک راهنمای قدیمی کپی شود، نسخه‌ای را نصب می‌کند که دیگر اصلاحیه‌ای دریافت نمی‌کند. استفاده از latest باعث می‌شود در یک docker compose pull آینده، نسخه شما به‌طور خودکار به نسخه اصلی بعدی ارتقا یابد که Nextcloud از آن پشتیبانی نمی‌کند.

دایرکتوری داده به عمد یک bind mount است و نه یک named volume: مسیری که بتوانید مستقیماً ابزار پشتیبان‌گیری را به آن اشاره دهید، ارزشمندتر از مرتب بودن است. آن را با UID مشخص‌شده در www-data مربوط به image و مجوزهایی که 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 باعث می‌شود سرویس روی رابط عمومی در دسترس نباشد. در این حالت، فایروال فقط باید اجازه دسترسی به پروکسی را بدهد؛ و اگر ترجیح می‌دهید 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 ساده برای پورت 80 با server_name مناسب بسازید و سپس اجازه دهید certbot آن را بازنویسی کند. جزئیات فنی چالش HTTP-01، زمان‌بندی تمدید و حالت‌های شکست، به‌طور کامل در صدور گواهی‌های 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 بررسی کنید؛ تایمر تمدیدی که فعال نشده باشد، مانند یک فیوز 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.1 و جدیدتر، http2 on; را اضافه کنید. این دستور در نسخه 1.25.0 وجود ندارد و در صورت استفاده، nginx اجرا نخواهد شد. Ubuntu 24.04 نسخه‌ای قدیمی‌تر ارائه می‌دهد که معادل آن listen 443 ssl http2; است. دستور nginx -t به شما می‌گوید که کدام‌یک توسط build شما پذیرفته می‌شود.

اگر client_max_body_size بیش از حد کوچک باشد، nginx آپلودهای بزرگ را مستقیماً با خطای 413 رد می‌کند. اگر زمان‌های انتظار (timeouts) برای خواندن خیلی کوتاه باشند، آپلود در میانه راه قطع می‌شود. مقادیر بالا از هر دو مورد جلوگیری می‌کنند. دستور proxy_request_buffering off آپلود را به‌صورت جریانی (stream) عبور می‌دهد و از ذخیره کامل فایل روی دیسک proxy پیش از ارسال، جلوگیری می‌کند.

استفاده از nginx روی host، ساده‌ترین راهکار برای یک برنامه است. اگر قرار است Nextcloud در کنار سایر containerها روی VPS میزبانی شود، اجرای Traefik به عنوان reverse proxy در Docker Compose برای چندین برنامه، مسیریابی و صدور گواهی را به برچسب‌های (labels) container منتقل می‌کند و همان دغدغه‌های client_max_body_size و تنظیمات timeout در قالب middleware و تنظیمات transport دوباره ظاهر می‌شوند.

تنظیم trusted_proxies و overwriteprotocol

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

مقدار 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) اجرا می‌شود و به یک پورت منتشرشده متصل می‌گردد، بنابراین کانتینر، gateway شبکه bridge داکر را می‌بیند که چیزی در محدوده 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 کل نمونه شما را یک‌باره مسدود می‌کند و در بخش نمای کلی مدیریت (admin overview) پیام "پیکربندی هدر reverse proxy نادرست است، یا شما از طریق یک پروکسی مورد اعتماد به Nextcloud دسترسی دارید" نمایش داده می‌شود.

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

وظایف پس‌زمینه: استفاده از cron به‌جای AJAX

اجرای پیش‌فرض وظایف در Nextcloud از نوع AJAX است: وظایف تنها زمانی اجرا می‌شوند که کاربری صفحه‌ای را بارگذاری کند. از آنجا که در ساعت 04:00 کسی در حال مرور سایت نیست، پاک‌سازی فایل‌های حذف‌شده، مدیریت نسخه‌ها، تولید پیش‌نمایش‌ها و تلاش‌های مجدد برای فدراسیون متوقف می‌شوند و اولین نشانهٔ آن، رشد بی‌وقفهٔ حجم دایرکتوری داده‌ها است. سرویس cron که در بالا ذکر شد، حلقهٔ رسمی /cron.sh را روی همان volumeها اجرا می‌کند. به 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 حاوی اعتبارنامه‌های دیتابیس، شناسهٔ نمونه (instance ID) و salt رمز عبور است. اگر فایل‌ها را بدون دیتابیس بازیابی کنید، Nextcloud قادر به مشاهدهٔ آن‌ها نخواهد بود. اگر دیتابیس را بدون config.php بازیابی کنید، امکان باز کردن دیتابیس وجود ندارد. بازیابی یک دیتابیس قدیمی روی یک دایرکتوری دادهٔ جدیدتر، باعث می‌شود اشتراک‌گذاری‌ها به فایل‌هایی اشاره کنند که جابه‌جا شده‌اند.

از هر سه جزء، در حالی که نمونه در حالت quiesced قرار دارد، پشتیبان بگیرید:

#!/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 دیتابیس و کپی فایل‌ها با یکدیگر همخوانی داشته باشند. اگر این مرحله را نادیده بگیرید، در نهایت دیتابیسی را ثبت می‌کنید که به فایلی اشاره دارد که rsync هنوز به آن نرسیده است. توجه داشته باشید که اسکریپت، dumpهای دیتابیس را با برچسب زمانی نگه می‌دارد، اما فقط یک mirror چرخشی از دایرکتوری داده دارد؛ rsync --delete در هر اجرا آن را بازنویسی می‌کند، بنابراین فقط جدیدترین dump با کپی فایل‌ها جفت می‌شود.

سپس نسخهٔ پشتیبان را از سرور خارج کنید. پشتیبانی که روی همان VPS اصلی قرار دارد، فقط یک کپی است، نه پشتیبان. استفاده از restic برای انتقال به فضای ذخیره‌سازی شیء (object storage) یا یک میزبان دوم، راهکار معمول است و قابلیت deduplication آن، دایرکتوری داده را بسیار بهتر از یک tarball شبانه مدیریت می‌کند. تنظیمات کامل، از مقداردهی اولیه مخزن تا تایمر شبانه و تمرین بازیابی، در پشتیبان‌گیری خارج از سرور VPS با restic آمده است.

بازیابی صرفاً معکوسِ عملیات پشتیبان‌گیری نیست. استکی که به‌تازگی راه‌اندازی شده، نصب‌کننده را اجرا کرده و یک config.php کاملاً جدید، یک شناسهٔ نمونه و salt رمز عبور جدید می‌سازد؛ وارد کردن dump روی این هویت جدید، باعث خرابی نشست‌ها (sessions) و توکن‌های اشتراک‌گذاری می‌شود. ابتدا هویت قدیمی را به این ترتیب بازگردانید:

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 که کتابخانه را ثبت می‌کند اما دیتابیس را نه، منجر به بازیابی یک تایم‌لاین خالی می‌شود.

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

Nextcloud تنها از ارتقا به یک نسخه اصلی بالاتر در هر مرحله پشتیبانی می‌کند. با استفاده از ایمیج رسمی Docker، پرش از نسخه 29 به 31 هرگز به مرحله upgrader نمی‌رسد. نقطه ورود (entrypoint) پیش از اجرای occ upgrade، نسخه‌های اصلی را مقایسه می‌کند، Can't start Nextcloud because upgrading from <installed version> to <image version> is not supported. و به دنبال آن It is only possible to upgrade one major version at a time. را چاپ کرده و خارج می‌شود. کانتینر از شروع خودداری می‌کند (با restart: unless-stopped مدام در حال ری‌استارت شدن و چاپ همان خطوط است)، حالت maintenance هرگز فعال نمی‌شود و داده‌ها و دیتابیس دست‌نخورده باقی می‌مانند. تگ را روی نسخه اصلی بعدی تنظیم کرده و دوباره شروع کنید.

فرآیند ارتقا در Docker به این صورت است: یک نسخه پشتیبان تهیه کنید، تگ را در هر دو سرویس app و cron یک نسخه اصلی بالاتر ببرید (از 35 به 36 پس از انتشار نسخه 36 که Nextcloud برای فوریه 2027 برنامه‌ریزی کرده است)، سپس docker compose pull && docker compose up -d و بعد docker compose logs -f app را اجرا کنید. نقطه ورود ایمیج، کد جدیدتر را در مقایسه با داده‌های موجود شناسایی کرده و خودکار occ upgrade را اجرا می‌کند. آن را قطع نکنید. زمانی که لاگ‌ها آرام شدند، docker compose exec -u www-data app php occ status را اجرا کنید و versionstring را بررسی نمایید تا مطمئن شوید برنامه‌ها دوباره فعال شده‌اند.

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

خطاهایی که واقعاً با آن‌ها مواجه خواهید شد

"Your data directory is readable by other people. Please change the permissions to 0770 so that the directory cannot be listed by other people." رقم آخر در مجوز (mode) دایرکتوری، که مربوط به سایر کاربران (others) است، صفر نیست. هرگونه بیت خواندن، نوشتن یا اجرا برای سایرین، این خطا را فعال می‌کند. بیت‌های گروه بررسی نمی‌شوند، زیرا 0770 خود دسترسی کامل را به گروه می‌دهد. Nextcloud ابتدا سعی می‌کند chmod 0770 را به‌صورت خودکار اعمال کند و تنها در صورت شکست خوردن هشدار می‌دهد؛ این اتفاق در bind mount معمولاً به این معناست که مالکیت دایرکتوری در اختیار UID 33 نیست. sudo chown -R 33:33 /srv/nextcloud/data و sudo chmod 0770 /srv/nextcloud/data را بررسی کنید.

"Your data directory is invalid. Ensure there is a file called ".ncdata" in the root of the data directory." این یعنی 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 به هیچ سرویسی دسترسی پیدا نکرده است. یا container هنوز در حال راه‌اندازی است (docker compose logs app را بررسی کنید)، یا خارج شده است (docker compose ps)، یا خط publish با پورت proxy_pass مطابقت ندارد. با ss -ltnp | grep 8080 تأیید کنید.

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

LockedException: "files/..." is locked. با تنظیم REDIS_HOST، ایمیج، Redis را به‌عنوان backend قفل‌گذاری پیکربندی می‌کند و قفل‌های قدیمی (stale locks) به‌ندرت ایجاد می‌شوند. بدون آن، قفل‌ها در جدول دیتابیس oc_file_locks باقی می‌مانند و اگر درخواستی در حین نوشتن متوقف شود، ردیف‌هایی در دیتابیس باقی می‌ماند. پیش از آن‌که بخواهید ردیف‌های قفل را به‌صورت دستی پاک کنید، تأیید کنید که Redis واقعاً در حال استفاده است؛ occ config:system:get memcache.locking باید کلاس Redis را برگرداند.

"The PHP memory limit is below the recommended value of 512 MB." مقدار PHP_MEMORY_LIMIT را افزایش دهید و container را دوباره ایجاد کنید. به یاد داشته باشید که این کار چه تأثیری بر سقف مصرف حافظه در بدترین شرایط (worst-case ceiling) خواهد داشت.

چه چیزی در مقیاس بزرگ دچار اختلال می‌شود

نخستین مانع، پر شدن دایرکتوری داده و فراتر رفتن آن از ظرفیت volume است. بزرگ کردن یک volume در VPS شامل تغییر اندازه و سپس گسترش فایل‌سیستم است؛ این کار اگر طبق برنامه انجام شود بسیار کم‌دردسرتر از زمانی است که دیسک 100 درصد پر شده باشد. همین حالا برای میزان مصرف دیسک هشدار تنظیم کنید، نه بعداً.

مانع دوم oc_filecache است. لیست کردن فایل‌ها و اسکن‌های همگام‌سازی با افزایش تعداد ردیف‌ها کند می‌شوند. راه‌حل، کار روی دیتابیس است: Postgres را روی حافظه پرسرعت نگه دارید، اجازه دهید از حافظه اشتراکی کافی استفاده کند و به جای انباشت همیشگی زباله‌ها و نسخه‌ها، آن‌ها را با تنظیمات retention پاکسازی کنید.

سومین مانع، رقابت تولید پیش‌نمایش (preview) با سایر پردازش‌هاست. روی یک سرور کوچک، ارائه‌دهندگان پیش‌نمایش را محدود نگه دارید و هرگز occ preview:generate-all را در ساعات کاری اجرا نکنید. اگر بخش عمده ذخیره‌سازی شما مربوط به تصاویر دوربین گوشی است، پردازش بندانگشتی (thumbnail) باید در یک سرور اختصاصی عکس انجام شود. مطلب مقایسه PhotoPrism و Immich از نظر رم، اپلیکیشن‌های موبایل و دستورات پشتیبان‌گیری بررسی می‌کند که هر کدام در مقایسه با یک سرور Nextcloud چه هزینه‌ای دارند.

فراتر از این موارد، پاسخ صادقانه این است که سرویس‌های جانبی به ماشین اختصاصی خود نیاز دارند. Collabora و جستجوی متن کامل (full-text search)، سرویس‌های مقیم جداگانه‌ای با پروفایل حافظه خاص خود هستند. قرار دادن آن‌ها روی سروری که تنها نسخه فایل‌های شما را نگه می‌دارد، بدون هیچ مزیتی، دامنه خرابی را بزرگ‌تر می‌کند. اگر ویرایش اسناد در مرورگر همان قابلیت اضافی است که می‌خواهید، حداقل رم مورد نیاز و محدودیت‌های اتصال که OnlyOffice را از Collabora متمایز می‌کند تعیین می‌کند که یک VPS با 2 تا 4 گیگابایت رم اصلاً کدام‌یک را می‌تواند اجرا کند. زمانی که شکل volume دیگر مناسب نیست، ذخیره‌سازی فایل را به storage سازگار با S3 منتقل کنید. توجه داشته باشید که این کار پشتیبان‌گیری را سخت‌تر می‌کند، نه آسان‌تر: دیتابیس همچنان متادیتای فایل‌ها را نگه می‌دارد و باید همگام با bucket دامپ (dump) شود.

هنگامی که نمونه (instance) به کاربران واقعی سرویس می‌دهد، Uptime Kuma را در مقابل آن قرار دهید تا پیش از کلاینت‌های همگام‌سازی، از قطعی‌ها مطلع شوید. یک ابر خصوصی با میل‌سرور شخصی شما به‌خوبی جفت می‌شود. اگر ترجیح می‌دهید سرویس‌ها را دستی به هم متصل نکنید، Cloudron، CasaOS و Coolify پلتفرم‌هایی را مقایسه می‌کنند که این کار را برای شما انجام می‌دهند. اگر موتور جستجوی خودمیزبان (self-hosted) گزینه بعدی لیست شماست، انتظار مشکلات متفاوتی نسبت به موارد بالا را داشته باشید: خطاهای 429 در SearXNG یا ناشی از محدودکننده نرخ (rate limiter) خودِ آن است و یا موتورهای بالادستی که IP سرور VPS شما را مسدود کرده‌اند؛ تنها لاگ‌ها به شما می‌گویند کدام‌یک عامل مشکل است.

FAQ

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

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

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

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

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

معمولاً دو تنظیم در پروکسی عامل این مشکل هستند. اگر client_max_body_size روی مقدار پیش‌فرض 1 MB باقی بماند، Nginx هر درخواست بزرگ‌تر از آن را بلافاصله با خطای 413 (Request Entity Too Large) رد می‌کند که به‌جای فایل ناقص، در لاگ خطای Nginx دیده می‌شود. مقادیر کوتاه برای proxy_read_timeout / proxy_send_timeout باعث قطع شدن انتقال‌های طولانی در میانه راه می‌شوند. هر دو را با دست‌ودلبازی تنظیم کنید، proxy_request_buffering off را روی حالت stream قرار دهید تا از spool کردن جلوگیری شود و PHP_UPLOAD_LIMIT را در کانتینر برنامه متناسب با آن افزایش دهید.

چرا Nextcloud در یک حلقه تغییر مسیر (redirect loop) گیر می‌کند یا درباره reverse proxy هشدار می‌دهد؟

کانتینر، Nginx را در 127.0.0.1 نمی‌بیند، بلکه gateway پل Docker را در جایی از 172.x می‌بیند. وقتی این آدرس در TRUSTED_PROXIES وجود نداشته باشد، هدر X-Forwarded-Proto: https نادیده گرفته می‌شود، Nextcloud آدرس‌های http:// را تولید می‌کند و پروکسی آن‌ها را بازمی‌گرداند. مقدار TRUSTED_PROXIES را روی زیرشبکه واقعی پل تنظیم کنید و OVERWRITEPROTOCOL: https را ثابت (pin) کنید.

آیا می‌توانم Nextcloud را مستقیماً از نسخه 29 به 31 ارتقا دهم؟

خیر. Nextcloud در هر مرحله ارتقا فقط از یک نسخه اصلی (major version) پشتیبانی می‌کند. در ایمیج رسمی Docker، نقطه ورود (entrypoint) پیش از اجرای occ upgrade، نسخه‌های اصلی را بررسی می‌کند، It is only possible to upgrade one major version at a time. را چاپ کرده و خارج می‌شود؛ بنابراین کانتینر بالا نمی‌آید و داده‌ها و دیتابیس شما دست‌نخورده باقی می‌مانند. تگ را روی 30 تنظیم کنید: بک‌آپ بگیرید، تگ را در هر دو سرویس app و cron یک نسخه اصلی بالا ببرید، docker compose pull && docker compose up -d را اجرا کنید، با occ status وضعیت را تأیید کنید و سپس این مراحل را تکرار کنید.