راهنمای نصب Nextcloud روی VPS با Docker و TLS
نصب Nextcloud روی VPS با 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 قرار دهید و secretها را در یک فایل همسطح به نام .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: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:نسخه اصلی (major tag) را ثابت نگه دارید و پیش از کپی کردن 31، نسخه فعلی را در Docker Hub بررسی کنید. استفاده از 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 باعث میشود سرویس از رابط عمومی دور بماند. در این صورت، فایروال فقط باید اجازه دسترسی به 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 کپی شده و نصبکننده اجرا میشود؛ تا زمانی که این فرآیند تمام نشود، container پاسخی نمیدهد.
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 و جدیدتر، http2 on; را اضافه کنید. Ubuntu 24.04 نسخه قدیمیتری را ارائه میدهد که معادل آن listen 443 ssl http2; است. دستور nginx -t به شما میگوید که build شما کدامیک را میپذیرد.
تنظیم client_max_body_size و زمانهای طولانی read timeout مانع از قطع شدن آپلودهای حجیم در میانه مسیر میشوند. دستور proxy_request_buffering off آپلود را بهجای ذخیره کامل فایل روی دیسک proxy، بهصورت stream عبور میدهد.
استفاده از nginx روی host سادهترین راهکار برای یک برنامه است. اگر قرار است Nextcloud فضای VPS را با containerهای دیگر به اشتراک بگذارد، اجرای Traefik بهعنوان یک reverse proxy در Docker Compose برای چندین برنامه، مسیریابی و صدور گواهی را به داخل labelهای 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 --allfiles:scan کش فایل را با آنچه واقعاً روی دیسک وجود دارد، تطبیق میدهد. این فرآیند را یک بار روی یک VPS یدکی تمرین کنید، پیش از آنکه واقعاً به آن نیاز پیدا کنید. همین تفکیک بین بایتهای روی دیسک و متادیتای موجود در Postgres، بر تمام برنامههای مشابه حاکم است؛ به همین دلیل است که پشتیبانگیری از Immich که کتابخانه را ثبت میکند اما دیتابیس را نه، منجر به بازیابی یک تایملاین خالی میشود.
ارتقا: هر بار یک نسخه اصلی
Nextcloud تنها از ارتقا به یک نسخه اصلی بالاتر در هر مرحله پشتیبانی میکند. پریدن از نسخه 29 به 31 به درستی انجام نمیشود، بلکه با خطای Exception: Updates between multiple major versions and downgrades are unsupported. مواجه شده و شما را در حالت maintenance mode باقی میگذارد.
مراحل ارتقا در Docker عبارتند از: تهیه نسخه پشتیبان، تغییر تگ از 31 به 32 در هر دو سرویس app و cron، سپس اجرای docker compose pull && docker compose up -d و در نهایت docker compose logs -f app. نقطه ورود (entrypoint) تصویر، کد جدید را در مقایسه با دادههای موجود شناسایی کرده و بهطور خودکار occ upgrade را اجرا میکند. این فرایند را قطع نکنید. زمانی که لاگها متوقف شدند، دستور docker compose exec -u www-data app php occ status را اجرا کرده و versionstring را بررسی کنید تا مطمئن شوید برنامهها دوباره فعال شدهاند.
دو قانون که شما را نجات میدهند: هر بار فقط یک نسخه اصلی را ارتقا دهید، صحت عملکرد را بررسی کنید و سپس به سراغ نسخه بعدی بروید. همچنین هرگز تگ سرویس app را بدون تغییر متناظر در cron ویرایش نکنید؛ اجرای دو نسخه متفاوت از Nextcloud روی یک دیتابیس واحد، مسیر قطعی برای خرابی دادهها است.
خطاهایی که واقعاً مشاهده خواهید کرد
"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 هرگز در آن مقداردهی اولیه نشده است، یا در مسیر تایپ اشتباهی وجود دارد، یا یک دایرکتوری خالی جدید جایگزین نمونهٔ در حال کار شده است. اطمینان حاصل کنید که مسیر میزبان با خط 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 دسترسی پیدا نکرده است. یا کانتینر هنوز در حال مقداردهی اولیه است (docker compose logs app را بررسی کنید)، یا خارج شده است (docker compose ps)، یا خط publish با پورت proxy_pass مطابقت ندارد. با استفاده از ss -ltnp | grep 8080 تأیید کنید.
حلقهٔ تغییر مسیر (redirect loop) یا هشدارهای "insecure" در نمای کلی مدیریت. OVERWRITEPROTOCOL: https وجود ندارد یا TRUSTED_PROXIES شامل زیرشبکهٔ Docker gateway نیست. به بخش proxy در بالا مراجعه کنید.
LockedException: "files/..." is locked. با تنظیم REDIS_HOST، ایمیج، Redis را بهعنوان backend قفلگذاری پیکربندی میکند و قفلهای قدیمی بهندرت ایجاد میشوند. بدون آن، قفلها در جدول دیتابیس 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 را افزایش دهید و کانتینر را دوباره ایجاد کنید. به یاد داشته باشید که این کار چه تأثیری بر سقف مصرف حافظه در بدترین شرایط (worst-case) دارد.
چه چیزی در مقیاس بزرگ دچار اختلال میشود
نخستین مانع، پر شدن دایرکتوری داده و فراتر رفتن آن از ظرفیت 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 و یک backend برای هر اتصال، به اضافه هر مقدار حافظهای که برای تولید پیشنمایش (preview) نیاز است. یک سرور 2 GB برای یک نمونه کوچک خانگی کافی است، به شرطی که پیشنمایشها را محدود کنید و swap اضافه کنید؛ اگر Collabora یا جستجوی تماممتن (full-text search) را اضافه کنید، باید منابع لازم برای مجموعه دومی از سرویسهای مقیم را نیز در نظر بگیرید.
چرا آپلودهای بزرگ پشت reverse proxy با Nginx شکست میخورند؟
معمولاً دو تنظیم در پروکسی عامل این مشکل هستند: client_max_body_size که روی مقدار پیشفرض 1 MB باقی مانده و درخواست را قطع میکند، و مقادیر کوتاه 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 را ثابت کنید.
آیا میتوانم Nextcloud را مستقیماً از نسخه 29 به 31 ارتقا دهم؟
خیر. Nextcloud در هر مرحله فقط از یک ارتقای نسخه اصلی (major version) پشتیبانی میکند و پرش از نسخهها با Updates between multiple major versions and downgrades are unsupported. متوقف میشود و نمونه شما را در حالت maintenance باقی میگذارد. نسخه پشتیبان تهیه کنید، تگ نسخه را در هر دو سرویس app و cron یک نسخه اصلی افزایش دهید، docker compose pull && docker compose up -d را اجرا کنید، با occ status تایید کنید و سپس این مراحل را تکرار کنید.