Docker Compose দিয়ে VPS-এ Nextcloud সেটআপ করার নিয়ম
Docker Compose, Postgres এবং Redis ব্যবহার করে নিজের VPS-এ Nextcloud চালানোর পূর্ণাঙ্গ নির্দেশিকা। TLS কনফিগারেশন এবং ডাটা ব্যাকআপের সঠিক পদ্ধতি এখানে দেওয়া হলো।
আপনি আসলে যা তৈরি করছেন
এই নির্দেশিকাটি একটি VPS-এ Docker Compose ব্যবহার করে Nextcloud পরিচালনা করবে, এর সামনে Let's Encrypt TLS যুক্ত করবে এবং এমন একটি ব্যাকআপ ব্যবস্থা তৈরি করবে যা প্রকৃতপক্ষে পুনরুদ্ধার করা যায়। এতে চারটি কন্টেইনার এবং একটি প্রক্সি থাকবে: লুপব্যাকে লিসেন করা অফিসিয়াল nextcloud ইমেজ, ফাইলের সমস্ত মেটাডেটা ধারণকারী Postgres, ফাইল লক ধরে রাখা Redis, শুধুমাত্র ক্রন লুপ চালানোর জন্য Nextcloud ইমেজের একটি দ্বিতীয় কপি এবং সবকিছুর সামনে TLS টার্মিনেট করার জন্য হোস্টের nginx। ইনস্টলেশন সম্পন্ন করতে 20 মিনিট সময় লাগে, তবে এটি গুরুত্বপূর্ণ অংশ নয়। প্রথম ঘণ্টায় নেওয়া দুটি সিদ্ধান্ত নির্ধারণ করবে যে এক বছর পরেও আপনার ফাইলগুলো অক্ষত থাকবে কি না: SQLite-এর পরিবর্তে একটি প্রকৃত ডাটাবেস ব্যবহার করা এবং এমন একটি ব্যাকআপ ব্যবস্থা যা ডাটা ডিরেক্টরি, ডাটাবেস এবং config.php-কে একটি সামঞ্জস্যপূর্ণ সেট হিসেবে সংরক্ষণ করে।
এটি ধরে নেয় যে আপনার কাছে Ubuntu 24.04 LTS বা Debian 13 আছে, Docker-এর নিজস্ব রিপোজিটরি থেকে ইনস্টল করা Compose v2 প্লাগইনসহ Docker Engine আছে এবং একটি DNS A রেকর্ড (আপনার IPv6 থাকলে AAAA সহ) ইতিমধ্যেই cloud.example.com-কে VPS-এর দিকে নির্দেশ করছে। এই সবকিছুর জন্য আপনার নিয়ন্ত্রণাধীন একটি সার্ভার প্রয়োজন; অন্য কারো SaaS-এ TLS টার্মিনেশন এবং ডাটাবেস ডাম্প করার কোনো উপায় নেই।
সাইজিং: আসলে কী মেমরি খরচ করে
Nextcloud-এর মেমরি ব্যবহারের প্রধান কারণ তিনটি, এবং এর কোনোটিই সরাসরি "Nextcloud" নয়।
PHP workers. -apache ইমেজ প্রতিটি সমসাময়িক অনুরোধ (concurrent request) একটি ওয়ার্কার প্রসেস থেকে পরিবেশন করে, যা একটি PHP ইন্টারপ্রেটার ধরে রাখে। PHP অনুরোধটি বাতিল করার আগে প্রতিটি ওয়ার্কার PHP_MEMORY_LIMIT পর্যন্ত বৃদ্ধি পেতে পারে। আপনার সবচেয়ে খারাপ পরিস্থিতির রেসিডেন্ট মেমরি হলো মোটামুটি সমসাময়িক অনুরোধ × মেমরি লিমিট, এবং একটি ডেস্কটপ সিঙ্ক ক্লায়েন্ট প্রতি ব্যবহারকারীর জন্য বেশ কয়েকটি সমান্তরাল সংযোগ খোলে। ব্যবহারকারীর সংখ্যা নয়, বরং কনকারেন্সিই মেমরির সর্বোচ্চ সীমা নির্ধারণ করে।
ডেটাবেস। Postgres প্রতিটি সংযোগের জন্য একটি ব্যাকএন্ড ফর্ক করে এবং শেয়ার্ড বাফারগুলো রেসিডেন্ট হিসেবে রাখে। এর ওয়ার্কিং সেট ফাইলের সংখ্যার সাথে স্কেল করে, বাইটের সংখ্যার সাথে নয়: oc_filecache প্রতি ব্যবহারকারীর প্রতি ফাইলের জন্য একটি সারি বহন করে। এক লক্ষ ছোট ফাইল একশটি বড় ফাইলের চেয়ে ডেটাবেসের ওপর বেশি চাপ সৃষ্টি করে।
প্রিভিউ জেনারেশন। থাম্বনেইল তৈরির সময় সোর্স ইমেজটিকে পূর্ণ রেজোলিউশনে মেমরিতে ডিকোড করা হয়। ভিডিও প্রিভিউয়ের জন্য ffmpeg ব্যবহার করা হয়। occ preview:generate-all চালানো হলে তা বারবার এই স্পাইক তৈরি করে, যা একটি ছোট VPS-কে OOM killer-এর কবলে ফেলার সবচেয়ে সাধারণ কারণ।
Redis তুলনামূলকভাবে কম মেমরি খরচ করে। পরবর্তীতে আপনি যা কিছু যুক্ত করবেন, যেমন Collabora, ফুল-টেক্সট সার্চ, অ্যান্টিভাইরাস স্ক্যানার, সেগুলো আলাদা রেসিডেন্ট সার্ভিস এবং তাদের নিজস্ব ফুটপ্রিন্ট রয়েছে। তাই কোনো কিছু এনাবল করার আগেই আপনার সাইজিং প্ল্যানে সেগুলো অন্তর্ভুক্ত করুন।
আপনার যদি RAM কম থাকে, তবে এই লিভারগুলো ব্যবহার করুন: PHP_MEMORY_LIMIT কমান, preview_max_x / preview_max_y / preview_max_filesize_image ক্যাপ করুন, enabledPreviewProviders-কে শুধুমাত্র সেই ফরম্যাটগুলোতে সীমাবদ্ধ করুন যা আপনি ব্রাউজ করেন, এবং trashbin_retention_obligation ও versions_retention_obligation এমনভাবে সেট করুন যাতে ডেটা ডিরেক্টরি নীরবে আপনার ফাইলের আকারের কয়েক গুণ না হয়ে যায়। একটি swap file যোগ করুন। Swap ধীরগতির, কিন্তু আপগ্রেড চলাকালীন OOM kill হওয়া তার চেয়েও খারাপ।
কেন SQLite ব্যর্থ হয়
Nextcloud-এ SQLite সাপোর্ট থাকে এবং অফিসিয়াল ইমেজটি অনায়াসেই এটি ব্যবহার করে। কিন্তু এটি ব্যবহার করবেন না। SQLite একটি ডাটাবেস-ব্যাপী লকের মাধ্যমে রাইট অপারেশনগুলোকে সিরিয়ালাইজ করে: পুরো ফাইলের জন্য একবারে মাত্র একজন রাইটার কাজ করতে পারে। Nextcloud প্রতিনিয়ত ফাইল লক, অ্যাক্টিভিটি রো, ক্যাশ এন্ট্রি এবং জব স্টেট রাইট করতে থাকে। একটি ডেস্কটপ ক্লায়েন্ট যখন ডিরেক্টরি ট্রি সিঙ্ক করে, তখন অনেকগুলো সমান্তরাল রিকোয়েস্ট তৈরি হয়। এই প্যাটার্নে আপনি SQLSTATE[HY000]: General error: 5 database is locked এবং HTTP 500 এরর পাবেন, এবং যখনই আপনার ইনস্ট্যান্সটি কার্যকর হয়ে উঠবে ঠিক তখনই এই ব্যর্থতা দেখা দেবে।
পরবর্তীতে occ db:convert-type ব্যবহার করে কনভার্ট করা সম্ভব, কিন্তু এটি একটি লাইভ ডাটাবেসে দীর্ঘ এবং সব বা কিছুই নয় (all-or-nothing) ধরনের মাইগ্রেশন। তাই শুরু থেকেই Postgres বা MariaDB ব্যবহার করুন।
Compose ফাইল
এটি /srv/nextcloud/compose.yaml-এ রাখুন। গোপন তথ্য .env sibling ফাইলে রাখুন এবং এর mode 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-এ বর্তমান tag পরীক্ষা করুন। 21 October 2026 অনুযায়ী, Nextcloud 33, 34 এবং 35 বজায় রাখে। Version 31 February 2026-এ এবং 32 September 2026-এ end of life হয়েছে। তাই পুরোনো guide থেকে কপি করা tag এমন release ইনস্টল করে, যেটি আর fix পায় না। latest ভবিষ্যতের কোনো docker compose pull-এ আপনাকে major boundary পার করিয়ে দেবে, আর Nextcloud এটি support করে না।
data directory-টি named volume নয়, bind mount। এটি ইচ্ছাকৃত, কারণ backup tool-কে সরাসরি দেখানো যায় এমন path পরিচ্ছন্নতার চেয়ে বেশি মূল্যবান। image-এর www-data UID এবং Nextcloud-এর চাওয়া permission দিয়ে এটি তৈরি করুন:
sudo mkdir -p /srv/nextcloud/data
sudo chown -R 33:33 /srv/nextcloud/data
sudo chmod 0770 /srv/nextcloud/dataport publish লক্ষ্য করুন: 127.0.0.1:8080:80। Docker DNAT rule লিখে port publish করে। এই rule ufw-এর INPUT chain packet দেখার আগেই প্রয়োগ হয়। একটি bare 8080:80 ব্যবহার করলে ufw যা-ই বলুক, unencrypted Nextcloud public Internet-এ প্রকাশিত হয়। loopback-এ bind করলে এটি public interface থেকে বিচ্ছিন্ন থাকে। এরপর firewall-এ শুধু proxy-কে অনুমতি দিতে হয়। আপনি যদি পুরো Internet-এর জন্য SSH খোলা রাখতে না চান, self-hosted WireGuard VPN ব্যবহার করে VPS-এ সংযোগ করা হলে public rule থেকে port 22 সম্পূর্ণ বাদ দিতে পারবেন:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enabledocker compose up -d দিয়ে এটি চালু করুন, তারপর docker compose logs -f app monitor করুন। প্রথম boot-এ সম্পূর্ণ application tree volume-এ কপি হয় এবং installer চলে। এই কাজ শেষ না হওয়া পর্যন্ত container কোনো অনুরোধের উত্তর দেয় না।
TLS এবং reverse proxy
distro থেকে nginx এবং certbot ইনস্টল করুন। সঠিক server_name ব্যবহার করে port-80-এর একটি সাধারণ server block তৈরি করুন। এরপর certbot-কে সেটি পুনর্লিখতে দিন। HTTP-01 challenge, renewal timer এবং সম্ভাব্য ব্যর্থতার কারণগুলো Ubuntu 24.04-এ certbot ও nginx দিয়ে Let's Encrypt certificate ইস্যু করা-এ বিস্তারিতভাবে ব্যাখ্যা করা হয়েছে:
sudo apt install nginx certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.example.comCertbot ssl_certificate লাইন এবং :80 → :443 redirect যোগ করে। এটি 90-day certificate renew করার জন্য একটি systemd timer-ও ইনস্টল করে। systemctl list-timers | grep certbot দিয়ে timer-টি আছে কি না নিশ্চিত করুন। যে renewal timer কখনো enabled করা হয়নি, সেটি 90 দিনের একটি নির্ধারিত ব্যর্থতা।
মূল proxy block:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name cloud.example.com;
# certbot manages ssl_certificate / ssl_certificate_key here
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
client_max_body_size 10G;
client_body_timeout 300s;
location = /.well-known/carddav { return 301 /remote.php/dav; }
location = /.well-known/caldav { return 301 /remote.php/dav; }
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_request_buffering off;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}nginx 1.25.1 এবং পরবর্তী সংস্করণে http2 on; যোগ করুন। 1.25.0-তে এই directive নেই। সেখানে এটি থাকলে nginx start হবে না। Ubuntu 24.04-এ এমন একটি পুরোনো build রয়েছে যেখানে এর সমতুল্য হলো listen 443 ssl http2;। আপনার build কোনটি গ্রহণ করে, তা nginx -t জানিয়ে দেবে।
client_max_body_size খুব ছোট হলে nginx বড় upload সরাসরি প্রত্যাখ্যান করে এবং 413 error দেয়। read timeout খুব কম হলে upload মাঝপথে বন্ধ হয়ে যায়। উপরের মানগুলো উভয় সমস্যা প্রতিরোধ করে। proxy_request_buffering off সম্পূর্ণ file-টি আগে proxy-এর disk-এ জমা না রেখে সরাসরি stream করে।
একটি app-এর জন্য host-এ nginx ব্যবহার করাই সবচেয়ে সহজ কার্যকর সমাধান। Nextcloud যদি VPS-এ অন্য container-এর সঙ্গে চলতে থাকে, একাধিক app-এর জন্য Docker Compose reverse proxy হিসেবে Traefik চালানো routing এবং certificate issuance-কে container label-এ স্থানান্তর করে। সেখানে একই client_max_body_size এবং timeout-সংক্রান্ত বিষয় middleware ও transport setting হিসেবে আবার দেখা দেয়।
trusted_proxies এবং overwriteprotocol
অধিকাংশ self-hosted Nextcloud ইনস্ট্যান্স এখানেই ভুল করে, এবং এর লক্ষণগুলো মূল সমস্যার সাথে সম্পর্কহীন মনে হয়।
X-Forwarded-Proto: https শুধুমাত্র তখনই কার্যকর হয় যখন অনুরোধটি trusted_proxies-এ তালিকাভুক্ত কোনো ঠিকানা থেকে আসে। যখন এটি কার্যকর হয় না, Nextcloud মনে করে অনুরোধটি সাধারণ HTTP এবং এটি http:// URL তৈরি করে; প্রক্সি সেগুলোকে HTTPS-এ রিডাইরেক্ট করে; ব্রাউজার তা অনুসরণ করে; Nextcloud পুনরায় http:// তৈরি করে। এটিই সেই রিডাইরেক্ট লুপ। OVERWRITEPROTOCOL: https প্রোটোকল যাই হোক না কেন, স্কিমটিকে নির্দিষ্ট করে দেয়।
TRUSTED_PROXIES-এর ক্ষেত্রে ফাঁদটি হলো, Nextcloud যে ঠিকানাটি দেখে তা 127.0.0.1 নয়। nginx হোস্ট মেশিনে চলে এবং একটি প্রকাশিত পোর্টে সংযোগ স্থাপন করে, তাই কন্টেইনারটি Docker ব্রিজ গেটওয়েকে দেখে, যা 172.x-এর অন্তর্ভুক্ত। সঠিক সাবনেটটি খুঁজে বের করুন:
docker network inspect nextcloud_default \
-f '{{range .IPAM.Config}}{{.Subnet}}{{end}}'সেই CIDR (অথবা এর আওতাভুক্ত 172.16.0.0/12) TRUSTED_PROXIES-এ বসান। এটি খুব বেশি বিস্তৃত করলে যেকোনো ক্লায়েন্ট X-Forwarded-For স্পুফ করতে পারবে; আবার ভুলভাবে সেট করলে প্রতিটি লগইন গেটওয়ে ঠিকানা থেকে আসছে বলে মনে হবে, brute-force প্রোটেকশন আপনার পুরো ইনস্ট্যান্সকে একসাথে ব্লক করে দেবে, এবং অ্যাডমিন ওভারভিউতে দেখাবে "The reverse proxy header configuration is incorrect, or you are accessing Nextcloud from a trusted proxy."
OVERWRITECLIURL ক্রন (cron) কন্টেইনারের জন্য গুরুত্বপূর্ণ, কারণ এর কাছে হোস্টনাম অনুমান করার মতো কোনো ইনকামিং অনুরোধ থাকে না। এটি ছাড়া, ব্যাকগ্রাউন্ড জবগুলো localhost-এর দিকে লিঙ্ক তৈরি করে এবং ইমেইল নোটিফিকেশনে অব্যবহারযোগ্য URL পাঠানো হয়।
Background jobs: cron, AJAX নয়
Nextcloud-এর ডিফল্ট জব রানার হলো AJAX: কোনো ব্যবহারকারী পেজ লোড করলে তার পার্শ্বপ্রতিক্রিয়া হিসেবে জবগুলো কার্যকর হয়। রাত 04:00 টায় কেউ ব্রাউজ করে না, তাই ট্র্যাশ এক্সপায়ারি, ভার্সন ক্লিনআপ, প্রিভিউ এবং ফেডারেটেড রিট্রাইগুলো আটকে যায়। এর প্রথম লক্ষণ হলো ডেটা ডিরেক্টরির আকার ক্রমাগত বাড়তে থাকা। উপরের 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 করে রাখা সুবিধাজনক।
ব্যাকআপ: তিনটি বিষয়, অথবা কিছুই নয়
শুধুমাত্র ফাইলসিস্টেমের ব্যাকআপ নিলে একটি অকেজো ইনস্ট্যান্স পুনরুদ্ধার হয়। ডেটা ডিরেক্টরিতে বাইটগুলো থাকে; Postgres ফাইল ক্যাশ, শেয়ার, ব্যবহারকারী এবং অ্যাপের অবস্থা ধরে রাখে; config.php ডেটাবেস ক্রেডেনশিয়াল, ইনস্ট্যান্স আইডি এবং পাসওয়ার্ড সল্ট ধরে রাখে। ডেটাবেস ছাড়া ফাইলগুলো পুনরুদ্ধার করলে 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-ই ডাম্প এবং ফাইল কপির মধ্যে সামঞ্জস্য বজায় রাখে। এটি এড়িয়ে গেলে আপনি এমন একটি ডেটাবেস ক্যাপচার করবেন যা এমন একটি ফাইলকে নির্দেশ করে যেখানে rsync তখনও পৌঁছায়নি। মনে রাখবেন, স্ক্রিপ্টটি টাইমস্ট্যাম্পযুক্ত ডেটাবেস ডাম্প রাখে কিন্তু ডেটা ডিরেক্টরির শুধুমাত্র একটি রোলিং মিরর রাখে, rsync --delete প্রতিবার চালানোর সময় এটি ওভাররাইট করে, তাই শুধুমাত্র নতুন ডাম্পটি ফাইল কপির সাথে কাজ করে।
এরপর এটিকে সার্ভার থেকে সরিয়ে নিন। যে সার্ভারে ব্যাকআপ রাখা হচ্ছে, সেই একই VPS-এ ব্যাকআপ রাখা মানে সেটি একটি কপি, ব্যাকআপ নয়। অবজেক্ট স্টোরেজ বা দ্বিতীয় কোনো হোস্টের বিপরীতে restic ব্যবহার করা সাধারণ সমাধান, এবং এর ডিডুপ্লিকেশন ডেটা ডিরেক্টরিকে nightly tarball-এর চেয়ে অনেক ভালোভাবে সামলায়। রিপোজিটরি ইনিট থেকে শুরু করে nightly টাইমার এবং রিস্টোর ড্রিল পর্যন্ত সম্পূর্ণ সেটআপ off-box VPS backups with restic-এ দেওয়া আছে।
রিস্টোর করা মানেই কেবল উল্টো প্রক্রিয়া নয়। নতুন শুরু হওয়া স্ট্যাক ইনস্টলার চালায় এবং একটি নতুন config.php, একটি নতুন ইনস্ট্যান্স আইডি এবং পাসওয়ার্ড সল্ট লেখে। সেই নতুন আইডেন্টিটির ওপর ডাম্প ইমপোর্ট করলে সেশন এবং শেয়ার টোকেন নষ্ট হয়ে যায়। প্রথমে পুরনো আইডেন্টিটি ফিরিয়ে আনুন, এই ক্রমে:
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-এর মেটাডেটার মধ্যে এই একই বিভাজন এই ধরনের প্রতিটি অ্যাপের ক্ষেত্রে প্রযোজ্য, যে কারণে an Immich backup that captures the library but not the database restores to an empty timeline।
আপগ্রেড: একবারে একটি major version
Nextcloud একবারে ঠিক একটি major version আপগ্রেড সমর্থন করে। অফিসিয়াল Docker image ব্যবহার করলে 29 থেকে 31-এ সরাসরি যাওয়া upgrader পর্যন্ত পৌঁছায় না। occ upgrade চালানোর আগে entrypoint major version দুটির তুলনা করে, 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. দেখায় এবং বন্ধ হয়ে যায়। Container চালু হতে অস্বীকৃতি জানায় (restart: unless-stopped থাকলে এটি বারবার restart হয়ে একই লাইন দেখাতে থাকে), maintenance mode কখনও চালু হয় না, এবং data ও database অপরিবর্তিত থাকে। Tag পরবর্তী major version-এ সেট করে আবার চালু করুন।
Docker upgrade-এর ধাপ হলো: backup নিন, app এবং cron উভয় service-এ tag এক major version বাড়িয়ে সম্পাদনা করুন (36 প্রকাশিত হলে 35 থেকে 36, যা Nextcloud February 2027-এর জন্য পরিকল্পনা করেছে), এরপর docker compose pull && docker compose up -d, তারপর docker compose logs -f app চালান। Image entrypoint বিদ্যমান data-এর সঙ্গে নতুন code শনাক্ত করে এবং নিজেই occ upgrade চালায়। এটি বাধাগ্রস্ত করবেন না। Log শান্ত হলে docker compose exec -u www-data app php occ status চালান এবং versionstring পরীক্ষা করুন; apps আবার enabled হয়েছে কি না তাও যাচাই করুন।
দুটি নিয়ম মনে রাখলে সমস্যা এড়ানো যায়: একটি major version বাড়ান, যাচাই করুন, তারপর পরেরটি বাড়ান। আর app service-এ tag কখনও cron-এর সঙ্গে মিলিয়ে সম্পাদনা না করে পরিবর্তন করবেন না। একই database-এর বিরুদ্ধে দুটি ভিন্ন Nextcloud version ব্যবহার করলে data corruption হতে পারে।
আপনি যে ত্রুটিগুলো বাস্তবে দেখবেন
"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-এর শেষ digit, অর্থাৎ অন্যান্য ব্যবহারকারীর জন্য নির্ধারিত digit, শূন্য নয়। অন্যান্য ব্যবহারকারীর জন্য যেকোনো read, write বা execute bit থাকলেই এই ত্রুটি দেখা যায়। Group bit কখনো পরীক্ষা করা হয় না, কারণ 0770 নিজেই group-কে পূর্ণ access দেয়। 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 কখনো initialize করেনি। path-এ typo থাকতে পারে, অথবা চলমান instance-এর নিচে একটি নতুন খালি ডিরেক্টরি বসানো হয়েছে। host path volume line-এর সঙ্গে মেলে কি না পরীক্ষা করুন।
"Access through untrusted domain." অনুরোধে ব্যবহৃত hostname trusted_domains-এ নেই। NEXTCLOUD_TRUSTED_DOMAINS শুধু প্রথম install-এর সময় প্রযোজ্য। পরে এটি সরাসরি চালু অবস্থায় সেট করুন: occ config:system:set trusted_domains 1 --value=cloud.example.com।
502 Bad Gateway, এবং /var/log/nginx/error.log-এ connect() failed (111: Connection refused) while connecting to upstream। nginx 127.0.0.1:8080-এ কোনো service-এ পৌঁছাতে পারেনি। Container এখনও initialize হচ্ছে; docker compose logs app পরীক্ষা করুন। অথবা এটি বন্ধ হয়ে গেছে; docker compose ps পরীক্ষা করুন। আরেকটি সম্ভাবনা হলো publish line-টি proxy_pass port-এর সঙ্গে মেলে না। ss -ltnp | grep 8080 দিয়ে নিশ্চিত করুন।
Redirect loop, অথবা admin overview-তে "insecure" সতর্কতা। OVERWRITEPROTOCOL: https অনুপস্থিত, অথবা TRUSTED_PROXIES-এ Docker gateway subnet নেই। উপরের proxy section দেখুন।
LockedException: "files/..." is locked। REDIS_HOST সেট করা থাকলে image locking backend হিসেবে Redis configure করে, তাই stale lock বিরল হয়। এটি সেট না থাকলে lock database table oc_file_locks-এ থাকে এবং write চলাকালে কোনো request বন্ধ হয়ে গেলে সেখানে row থেকে যায়। হাতে lock row মুছে ফেলার আগে Redis বাস্তবে ব্যবহৃত হচ্ছে কি না নিশ্চিত করুন। occ config:system:get memcache.locking-এর ফল Redis class হওয়া উচিত।
"The PHP memory limit is below the recommended value of 512 MB." PHP_MEMORY_LIMIT-এর মান বাড়িয়ে container পুনরায় তৈরি করুন। এতে worst-case ceiling কীভাবে পরিবর্তিত হয়, তা মনে রাখুন।
স্কেলেবিলিটির ক্ষেত্রে যা যা বাধা হয়ে দাঁড়ায়
প্রথম বাধা হলো ডেটা ডিরেক্টরি ভলিউমের ধারণক্ষমতা ছাড়িয়ে যাওয়া। একটি VPS-এ ভলিউম বড় করার অর্থ হলো রিসাইজ করা এবং ফাইলসিস্টেম বাড়ানো, যা 100% পূর্ণ হওয়ার পর করার চেয়ে আগে থেকে শিডিউল করা অনেক সহজ। তাই ডিস্ক ব্যবহারের ওপর এখনই অ্যালার্ট সেট করুন, পরে নয়।
দ্বিতীয় বাধা হলো oc_filecache। ফাইলের সংখ্যা বাড়লে লিস্টিং এবং সিঙ্ক স্ক্যান ধীর হয়ে যায়। এর সমাধান হলো ডেটাবেস অপ্টিমাইজেশন: Postgres-কে দ্রুতগতির স্টোরেজে রাখুন, পর্যাপ্ত shared memory বরাদ্দ করুন এবং রিটেনশন সেটিংস ব্যবহার করে অপ্রয়োজনীয় ফাইল ও ভার্সন মুছে ফেলুন, যাতে সেগুলো জমা হয়ে না থাকে।
তৃতীয় বাধা হলো প্রিভিউ জেনারেশন, যা অন্যান্য কাজের সাথে রিসোর্সের জন্য প্রতিযোগিতা করে। ছোট সার্ভারে প্রিভিউ প্রোভাইডার সীমিত রাখুন এবং কাজের সময় কখনোই occ preview:generate-all চালাবেন না। যদি আপনার স্টোরেজের বড় অংশ ফোনের ক্যামেরা রোল হয়, তবে সেই থাম্বনেইল তৈরির কাজ একটি আলাদা ফটো সার্ভারে স্থানান্তর করুন। PhotoPrism এবং Immich-এর RAM, ফোন অ্যাপ এবং ব্যাকআপ কমান্ডের তুলনা অংশে Nextcloud বক্সের তুলনায় এদের খরচ সম্পর্কে বিস্তারিত আলোচনা করা হয়েছে।
এর বাইরে বাস্তবসম্মত উত্তর হলো, অতিরিক্ত ফিচারগুলোর জন্য আলাদা মেশিনের প্রয়োজন। Collabora এবং full-text search আলাদা সার্ভিস হিসেবে চলে এবং এদের নিজস্ব মেমোরি প্রোফাইল থাকে। এগুলোকে আপনার ফাইলের মূল কপি রাখা সার্ভারে রাখলে কোনো বাড়তি সুবিধা ছাড়াই ব্যর্থতার ঝুঁকি বাড়ে। যদি ব্রাউজারে ডকুমেন্ট এডিট করা আপনার প্রধান প্রয়োজন হয়, তবে OnlyOffice এবং Collabora-এর RAM ও কানেকশন লিমিটের পার্থক্য দেখে নিন, যা 2 থেকে 4 GB RAM-এর VPS-এ কোনটি চালানো সম্ভব তা নির্ধারণ করবে। যখন ভলিউমের আকার আর উপযুক্ত থাকে না, তখন ফাইল স্টোরেজকে S3-compatible প্রাইমারি স্টোরেজে সরিয়ে নিন। মনে রাখবেন, এটি ব্যাকআপ প্রক্রিয়াকে সহজ করার বদলে আরও জটিল করে তোলে: ডেটাবেসে মেটাডেটা থাকে এবং সেটিকে বাকেটের সাথে সামঞ্জস্য রেখে ডাম্প করতে হয়।
ইনস্ট্যান্সটি যখন প্রকৃত ব্যবহারকারীদের সেবা দেওয়া শুরু করবে, তখন এর সামনে Uptime Kuma বসিয়ে দিন, যাতে সিঙ্ক ক্লায়েন্টরা জানার আগেই আপনি ডাউনটাইম সম্পর্কে জানতে পারেন। একটি প্রাইভেট ক্লাউড আপনার নিজস্ব মেইল সার্ভারের সাথে ভালো কাজ করে। আপনি যদি ম্যানুয়ালি সার্ভিসগুলো যুক্ত করতে না চান, তবে Cloudron, CasaOS এবং Coolify প্ল্যাটফর্মগুলো তুলনা করে দেখতে পারেন, যা আপনার হয়ে এই কাজগুলো করে দেয়। যদি এরপর আপনার তালিকায় একটি সেলফ-হোস্টেড সার্চ ইঞ্জিন থাকে, তবে উপরের সমস্যাগুলো থেকে ভিন্ন ধরনের সমস্যার জন্য প্রস্তুত থাকুন: SearXNG-এর 429 error সাধারণত এর নিজস্ব রেট লিমিটার অথবা আপস্ট্রিম ইঞ্জিন কর্তৃক আপনার VPS IP ব্লক করার কারণে ঘটে, এবং শুধুমাত্র লগ ফাইল দেখলেই আপনি নিশ্চিত হতে পারবেন কোনটি ঘটছে।
FAQ
Postgres-এর পরিবর্তে কি SQLite-এ Nextcloud চালাতে পারি?
হ্যাঁ, পারবেন। Official image এটি সমর্থন করে। তবে একটি desktop sync client parallel request পাঠালে SQLSTATE[HY000]: General error: 5 database is locked এবং HTTP 500 error দেখা দেবে। SQLite পুরো database-এ write lock প্রয়োগ করে। Nextcloud নিয়মিত লেখে—file lock, activity row এবং job state তৈরি করে। শুরু থেকেই Postgres বা MariaDB ব্যবহার করুন। occ db:convert-type আছে, কিন্তু live data-এর ক্ষেত্রে এটি দীর্ঘ এবং সম্পূর্ণ সফল না হলে কার্যকর হয় না—এমন migration।
Nextcloud VPS-এর আসলে কত RAM প্রয়োজন?
User-এর সংখ্যা নয়, concurrency অনুযায়ী sizing করুন। Worst-case resident memory মোটামুটি concurrent request-এর সংখ্যা × PHP_MEMORY_LIMIT। এর সঙ্গে Postgres shared buffer, প্রতিটি connection-এর জন্য একটি backend এবং preview generation-এর সর্বোচ্চ memory spike যোগ হবে। 2 GB-এর একটি server-এ preview সীমিত করে এবং swap যোগ করলে ছোট household instance চালানো যায়। Collabora বা full-text search যোগ করলে আরও এক সেট resident service-এর জন্য sizing করতে হবে।
nginx reverse proxy-এর পেছনে বড় upload ব্যর্থ হয় কেন?
সাধারণত proxy-এর দুটি setting এর কারণ। client_max_body_size যদি 1 MB default-এ থাকে, nginx বড় request সরাসরি প্রত্যাখ্যান করে এবং 413 (Request Entity Too Large) পাঠায়। এটি আংশিক file হিসেবে নয়, nginx error log-এ দেখা যায়। কম proxy_read_timeout / proxy_send_timeout value দীর্ঘ transfer মাঝপথে বন্ধ করে দেয়। উভয়ের value যথেষ্ট বাড়ান, proxy_request_buffering off-কে spool না করে stream করার জন্য সেট করুন, এবং app container-এ PHP_UPLOAD_LIMIT-এর value একই অনুযায়ী বাড়ান।
Nextcloud loop-এ redirect করে বা reverse proxy সম্পর্কে সতর্কবার্তা দেখায় কেন?
Container nginx-কে 127.0.0.1-এ দেখতে পায় না। এটি Docker bridge gateway-কে দেখে, যা সাধারণত 172.x-এর মধ্যে থাকে। সেই address TRUSTED_PROXIES-এ না থাকলে X-Forwarded-Proto: https header উপেক্ষা করা হয়। Nextcloud http:// URL তৈরি করে এবং proxy সেগুলো আবার ফিরিয়ে পাঠায়। TRUSTED_PROXIES-কে প্রকৃত bridge subnet-এ সেট করুন এবং OVERWRITEPROTOCOL: https নির্দিষ্ট করে দিন।
Nextcloud কি 29 থেকে সরাসরি 31-এ upgrade করা যাবে?
না। Nextcloud প্রতিটি upgrade-এ এক major version সমর্থন করে। Official Docker image-এ entrypoint occ upgrade চালানোর আগে major version পরীক্ষা করে। এটি It is only possible to upgrade one major version at a time. দেখিয়ে exit করে। ফলে container start হয় না এবং আপনার data ও database অপরিবর্তিত থাকে। Tag-কে 30 করুন। Backup নিন, app এবং cron উভয় service-এ tag এক major version বাড়ান, docker compose pull && docker compose up -d চালান, occ status দিয়ে যাচাই করুন, তারপর একই প্রক্রিয়া আবার করুন।