VPS-এ Docker দিয়ে Nextcloud সেটআপ করার নিয়ম
Docker Compose, Postgres এবং Redis ব্যবহার করে নিজের VPS-এ Nextcloud চালানোর পূর্ণাঙ্গ গাইড। ডাটাবেস কনফিগারেশন, TLS প্রক্সি সেটআপ এবং ব্যাকআপ নেওয়ার সঠিক পদ্ধতি এখানে দেওয়া হলো।
আপনি আসলে যা তৈরি করছেন
এই নির্দেশিকাটি একটি VPS-এ Docker Compose ব্যবহার করে Nextcloud পরিচালনা করে, এর সামনে Let's Encrypt TLS যুক্ত করে এবং এমন একটি ব্যাকআপ সেটআপ করে যা প্রকৃতপক্ষে রিস্টোর করা যায়। এতে চারটি কন্টেইনার এবং একটি প্রক্সি রয়েছে: অফিসিয়াল nextcloud ইমেজ যা লুপব্যাকে লিসেন করে, Postgres যা ফাইলের সমস্ত মেটাডেটা ধরে রাখে, Redis যা ফাইলের লকগুলো ধরে রাখে, Nextcloud ইমেজের দ্বিতীয় একটি কপি যা শুধুমাত্র ক্রন লুপ চালায় এবং হোস্ট মেশিনে থাকা nginx যা এই সবকিছুর সামনে TLS টার্মিনেশন করে। ইনস্টলেশন প্রক্রিয়াটি সম্পন্ন করতে বিশ মিনিট সময় লাগে, তবে এটিই মূল বিষয় নয়। প্রথম ঘণ্টায় নেওয়া দুটি সিদ্ধান্ত নির্ধারণ করবে এক বছর পরেও আপনার ফাইলগুলো সুরক্ষিত থাকবে কি না: 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) অনুরোধ একটি ওয়ার্কার প্রসেস থেকে পরিবেশন করে, যা একটি 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 ব্যবহার করুন।
The Compose file
Put this in /srv/nextcloud/compose.yaml, with secrets in a sibling .env file at 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: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:Pin the major tag and check the current one on Docker Hub before you copy 31 verbatim. latest will roll you across a major boundary on some future docker compose pull, and Nextcloud does not support that.
The data directory is a bind mount, not a named volume, on purpose: a path you can point a backup tool at directly is worth more than tidiness. Create it with the image's www-data UID and the permissions Nextcloud demands:
sudo mkdir -p /srv/nextcloud/data
sudo chown -R 33:33 /srv/nextcloud/data
sudo chmod 0770 /srv/nextcloud/dataNote the port publish: 127.0.0.1:8080:80. Docker publishes ports by writing DNAT rules that are evaluated before ufw's INPUT chain ever sees the packet, a bare 8080:80 puts an unencrypted Nextcloud on the public internet no matter what ufw says. Binding to loopback keeps it off the public interface. Then the firewall only has to allow the proxy, and if you would rather not leave SSH open to the whole internet, reaching the VPS over a self-hosted WireGuard VPN lets you drop port 22 from the public rules entirely:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableBring it up with docker compose up -d, then watch docker compose logs -f app. First boot copies the whole application tree into the volume and runs the installer; the container answers nothing until that finishes.
TLS এবং reverse proxy
আপনার ডিস্ট্রিবিউশনের রিপোজিটরি থেকে nginx এবং certbot ইনস্টল করুন, সঠিক server_name সহ একটি সাধারণ port-80 server block তৈরি করুন এবং তারপর certbot-কে এটি rewrite করতে দিন। HTTP-01 challenge-এর কার্যপদ্ধতি, renewal timer এবং ব্যর্থতার কারণগুলো Ubuntu 24.04-এ certbot এবং nginx ব্যবহার করে Let's Encrypt সার্টিফিকেট ইস্যু করা নিবন্ধে বিস্তারিত আলোচনা করা হয়েছে:
sudo apt install nginx certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.example.comCertbot প্রয়োজনীয় ssl_certificate লাইনগুলো যোগ করে, :80 → :443 রিডাইরেক্ট সেটআপ করে এবং একটি systemd timer ইনস্টল করে যা 90 দিনের সার্টিফিকেট রিনিউ করে। systemctl list-timers | grep certbot কমান্ড দিয়ে নিশ্চিত করুন যে এটি সক্রিয় আছে; রিনিউয়াল টাইমার এনাবল করা না থাকলে এটি একটি 90 দিনের টাইম-বোমা হিসেবে কাজ করবে।
প্রক্সি ব্লকটি নিচে দেওয়া হলো:
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 এবং দীর্ঘ read timeout সেটিংস বড় ফাইল আপলোডের সময় সংযোগ বিচ্ছিন্ন হওয়া রোধ করে। proxy_request_buffering off ফাইলটিকে প্রক্সির ডিস্কে জমা না করে সরাসরি স্ট্রিম করে আপলোড সম্পন্ন করে।
একটি অ্যাপের জন্য হোস্ট মেশিনে nginx ব্যবহার করাই সবচেয়ে সহজ সমাধান। যদি Nextcloud অন্যান্য কন্টেইনারের সাথে একই VPS-এ শেয়ার করতে হয়, তবে একাধিক অ্যাপের জন্য Docker Compose-এ Traefik-কে reverse proxy হিসেবে চালানো নিবন্ধটি দেখুন। সেখানে রাউটিং এবং সার্টিফিকেট ইস্যু করার প্রক্রিয়া কন্টেইনার লেবেলে চলে যায় এবং একই client_max_body_size ও টাইমআউট সংক্রান্ত বিষয়গুলো middleware এবং transport সেটিংস হিসেবে পুনরায় সামনে আসে।
trusted_proxies এবং overwriteprotocol
অধিকাংশ self-hosted Nextcloud ইনস্ট্যান্স এখানেই ভুল করে, এবং এর লক্ষণগুলো মূল সমস্যার সাথে সম্পর্কহীন মনে হয়।
X-Forwarded-Proto: https শুধুমাত্র তখনই কার্যকর হয় যখন অনুরোধটি trusted_proxies-এ তালিকাভুক্ত কোনো ঠিকানা থেকে আসে। যখন এটি কার্যকর হয় না, Nextcloud মনে করে অনুরোধটি plain HTTP এবং এটি http:// URL তৈরি করে; প্রক্সি সেগুলোকে HTTPS-এ রিডাইরেক্ট করে; ব্রাউজার তা অনুসরণ করে; Nextcloud পুনরায় http:// তৈরি করে। এটিই সেই রিডাইরেক্ট লুপ। OVERWRITEPROTOCOL: https প্রোটোকল যাই হোক না কেন, স্কিমটিকে নির্দিষ্ট করে দেয়।
TRUSTED_PROXIES-এর ক্ষেত্রে ফাঁদটি হলো, Nextcloud যে ঠিকানাটি দেখে তা 127.0.0.1 নয়। nginx হোস্ট মেশিনে চলে এবং একটি published port-এ সংযোগ স্থাপন করে, তাই কন্টেইনারটি Docker bridge gateway-কে দেখে, যা 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 পাঠায়।
ব্যাকগ্রাউন্ড জব: AJAX নয়, cron ব্যবহার করুন
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/মেইনটেন্যান্স মোড নিশ্চিত করে যে ডাম্প এবং ফাইলের কপি একে অপরের সাথে সামঞ্জস্যপূর্ণ। এটি এড়িয়ে চললে আপনি এমন একটি ডেটাবেস ক্যাপচার করবেন যা এমন ফাইলকে নির্দেশ করে যেখানে rsync তখনও পৌঁছায়নি। মনে রাখবেন, স্ক্রিপ্টটি টাইমস্ট্যাম্পযুক্ত ডেটাবেস ডাম্প রাখে কিন্তু ডেটা ডিরেক্টরির মাত্র একটি রোলিং মিরর রাখে, rsync --delete প্রতিবার চালানোর সময় এটিকে ওভাররাইট করে, তাই শুধুমাত্র নতুন ডাম্পটিই ফাইল কপির সাথে মেলে।
এরপর এগুলোকে সার্ভার থেকে সরিয়ে নিন। যে সার্ভারে ব্যাকআপ নেওয়া হচ্ছে, সেই একই VPS-এ রাখা ব্যাকআপ কেবল একটি কপি, প্রকৃত ব্যাকআপ নয়। অবজেক্ট স্টোরেজ বা দ্বিতীয় কোনো হোস্টের বিপরীতে restic ব্যবহার করা সাধারণ সমাধান, এবং এর ডিডুপ্লিকেশন (deduplication) ডেটা ডিরেক্টরির ক্ষেত্রে 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।
আপগ্রেড: একবারে একটি মেজর ভার্সন
Nextcloud-এ একবারে শুধুমাত্র একটি মেজর ভার্সন আপগ্রেড করা সমর্থন করে। 29 থেকে সরাসরি 31-এ জাম্প করলে তা সঠিকভাবে সম্পন্ন হয় না, বরং Exception: Updates between multiple major versions and downgrades are unsupported. এর মাধ্যমে ব্যর্থ হয় এবং আপনাকে maintenance mode-এ আটকে রাখে।
Docker আপগ্রেড করার নিয়ম হলো: প্রথমে ব্যাকআপ নিন, এরপর app এবং cron উভয় সার্ভিসেই ট্যাগটিকে 31 থেকে পরিবর্তন করে 32 করুন, তারপর 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 চেক করে নিশ্চিত করুন যে অ্যাপগুলো পুনরায় চালু হয়েছে।
দুটি নিয়ম আপনাকে বড় ধরনের সমস্যা থেকে বাঁচাবে: একটি মেজর ভার্সন আপগ্রেড করে যাচাই করুন, তারপর পরবর্তী ভার্সনে যান। এছাড়া, cron-এর সাথে মিল না রেখে কখনোই app সার্ভিসের ট্যাগ পরিবর্তন করবেন না; একটি ডেটাবেসের বিপরীতে দুটি ভিন্ন Nextcloud ভার্সন চালানো ডেটা নষ্ট হওয়ার একটি বড় কারণ।
যেসব ত্রুটি আপনি সচরাচর দেখবেন
"Your data directory is readable by other users. Please change the permissions to 0770." বাইন্ড-মাউন্ট করা ডিরেক্টরিটিতে গ্রুপ বা অন্য ব্যবহারকারীদের জন্য রিড পারমিশন সেট করা আছে। 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." বাইন্ড মাউন্টটি এমন কোনো লোকেশনে পয়েন্ট করছে যেখানে Nextcloud কখনো ইনিশিয়ালাইজ হয়নি, অথবা পাথে কোনো টাইপো আছে, কিংবা সচল ইনস্ট্যান্সের নিচে একটি নতুন খালি ডিরেক্টরি চলে এসেছে। হোস্ট পাথটি ভলিউম লাইনের সাথে মিলছে কি না তা যাচাই করুন।
"Access through untrusted domain." অনুরোধে ব্যবহৃত হোস্টনামটি trusted_domains-এ নেই। NEXTCLOUD_TRUSTED_DOMAINS শুধুমাত্র প্রথমবার ইনস্টলের ক্ষেত্রে প্রযোজ্য; পরবর্তীতে এটি সরাসরি সেট করুন: 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-এ কোনো কিছু খুঁজে পায়নি। হয় কন্টেইনারটি এখনো ইনিশিয়ালাইজ হচ্ছে (docker compose logs app চেক করুন), অথবা এটি বন্ধ হয়ে গেছে (docker compose ps), কিংবা পাবলিশ লাইনটি proxy_pass পোর্টের সাথে মিলছে না। ss -ltnp | grep 8080 দিয়ে নিশ্চিত করুন।
একটি রিডাইরেক্ট লুপ, অথবা অ্যাডমিন ওভারভিউতে "insecure" সতর্কতা। OVERWRITEPROTOCOL: https অনুপস্থিত, অথবা TRUSTED_PROXIES-এ Docker গেটওয়ে সাবনেট নেই। উপরের প্রক্সি সেকশনটি দেখুন।
LockedException: "files/..." is locked। REDIS_HOST সেট করা থাকলে, ইমেজটি Redis-কে লকিং ব্যাকএন্ড হিসেবে কনফিগার করে এবং স্টেল লক খুব কমই দেখা যায়। এটি ছাড়া, লকগুলো ডাটাবেস টেবিল oc_file_locks-এ থাকে এবং রাইট করার সময় কোনো রিকোয়েস্ট বন্ধ হয়ে গেলে সেখানে সারি (row) থেকে যায়। হাতে লক সারিগুলো মুছে ফেলার আগে নিশ্চিত করুন যে Redis আসলেই ব্যবহৃত হচ্ছে, occ config:system:get memcache.locking কমান্ডটি Redis ক্লাস রিটার্ন করা উচিত।
"The PHP memory limit is below the recommended value of 512MB." PHP_MEMORY_LIMIT বৃদ্ধি করুন এবং কন্টেইনারটি পুনরায় তৈরি করুন। এটি আপনার সিস্টেমের সর্বোচ্চ মেমরি ব্যবহারের ওপর কী প্রভাব ফেলবে তা মনে রাখবেন।
স্কেলেবিলিটি বা বড় পরিসরে যা বাধা হয়ে দাঁড়ায়
প্রথম বাধা হলো ডেটা ডিরেক্টরি ভলিউমের ধারণক্ষমতা ছাড়িয়ে যাওয়া। একটি VPS-এ ভলিউম বড় করার অর্থ হলো রিসাইজ করা এবং ফাইলসিস্টেম বাড়ানো। ডিস্ক 100% পূর্ণ হওয়ার পর এটি করার চেয়ে আগে থেকে শিডিউল করা অনেক কম যন্ত্রণাদায়ক। তাই ডিস্ক ব্যবহারের ওপর এখনই অ্যালার্ট সেট করুন, পরে নয়।
দ্বিতীয় বাধা হলো oc_filecache। ফাইলের সংখ্যা বাড়লে ফাইল লিস্টিং এবং সিঙ্ক স্ক্যান ধীর হয়ে যায়। এর সমাধান হলো ডেটাবেস অপ্টিমাইজ করা: Postgres-কে দ্রুতগতির স্টোরেজে রাখুন, পর্যাপ্ত shared memory ব্যবহার করতে দিন এবং রিটেনশন সেটিংস ব্যবহার করে অপ্রয়োজনীয় ফাইল ও ভার্সন মুছে ফেলুন, যাতে সেগুলো চিরকাল জমা না থাকে।
তৃতীয় বাধা হলো প্রিভিউ জেনারেশন, যা অন্যান্য কাজের সাথে রিসোর্সের জন্য প্রতিযোগিতা করে। ছোট সার্ভারে প্রিভিউ প্রোভাইডার সীমিত রাখুন এবং কাজের সময় কখনোই occ preview:generate-all চালাবেন না। যদি আপনার স্টোরেজের বড় অংশ ফোনের ক্যামেরা রোল হয়, তবে থাম্বনেইল তৈরির কাজ একটি ডেডিকেটেড ফটো সার্ভারে সরিয়ে নিন। RAM, ফোন অ্যাপ এবং ব্যাকআপ কমান্ডের ভিত্তিতে PhotoPrism এবং Immich-এর তুলনা অংশে Nextcloud বক্সের তুলনায় এগুলোর খরচ সম্পর্কে বিস্তারিত আলোচনা করা হয়েছে।
এর বাইরে সত্যি কথা হলো, অতিরিক্ত ফিচারগুলোর জন্য আলাদা মেশিনের প্রয়োজন হয়। Collabora এবং full-text search আলাদা সার্ভিস হিসেবে চলে এবং এদের নিজস্ব মেমোরি প্রোফাইল থাকে। এগুলোকে সেই একই বক্সে রাখা যেখানে আপনার ফাইলের একমাত্র কপিটি আছে, তা কোনো সুবিধা ছাড়াই ব্যর্থতার ঝুঁকি বাড়িয়ে দেয়। যদি ইন-ব্রাউজার ডকুমেন্ট এডিটিং আপনার প্রয়োজন হয়, তবে OnlyOffice এবং Collabora-এর মধ্যে পার্থক্যকারী ভেন্ডর RAM ফ্লোর এবং কানেকশন লিমিট দেখে নিন, যা নির্ধারণ করবে 2 থেকে 4 GB RAM-এর VPS-এ কোনটি চালানো সম্ভব। ভলিউমের আকার যখন আর উপযুক্ত থাকে না, তখন ফাইল স্টোরেজকে S3-compatible প্রাইমারি স্টোরেজে সরিয়ে নিন। মনে রাখবেন, এটি ব্যাকআপ প্রক্রিয়াকে সহজ করার বদলে আরও জটিল করে তোলে: ডেটাবেসে মেটাডেটা থাকে এবং সেটিকে বাকেটের সাথে সামঞ্জস্য রেখে ডাম্প করতে হয়।
ইনস্ট্যান্সটি যখন প্রকৃত ব্যবহারকারীদের সেবা দেওয়া শুরু করবে, তখন এর সামনে Uptime Kuma বসিয়ে দিন, যাতে সিঙ্ক ক্লায়েন্টরা জানার আগেই আপনি ডাউনটাইম সম্পর্কে জানতে পারেন। একটি প্রাইভেট ক্লাউড আপনার নিজস্ব মেইল সার্ভারের সাথে ভালো কাজ করে। আপনি যদি হাতে সার্ভিসগুলো কানেক্ট করতে না চান, তবে Cloudron, CasaOS এবং Coolify প্ল্যাটফর্মগুলো তুলনা করে দেখতে পারেন, যা আপনার হয়ে এই কাজগুলো করে দেয়। যদি এরপর আপনার তালিকায় একটি সেলফ-হোস্টেড সার্চ ইঞ্জিন থাকে, তবে উপরের সমস্যাগুলোর চেয়ে ভিন্ন ধরনের সমস্যার জন্য প্রস্তুত থাকুন: SearXNG-এর 429 এররগুলো হয় এর নিজস্ব রেট লিমিটার থেকে আসে, অথবা আপস্ট্রিম ইঞ্জিনগুলো আপনার VPS-এর IP ব্লক করে দিলে ঘটে। শুধুমাত্র লগ ফাইলই আপনাকে বলতে পারবে কোনটি ঘটছে।
FAQ
আমি কি Postgres-এর পরিবর্তে SQLite-এ Nextcloud চালাতে পারি?
আপনি পারেন এবং অফিসিয়াল ইমেজ আপনাকে এটি করার অনুমতি দেবে, কিন্তু একটি ডেস্কটপ সিঙ্ক ক্লায়েন্ট যখন সমান্তরাল অনুরোধ পাঠাবে, তখন আপনি SQLSTATE[HY000]: General error: 5 database is locked এবং HTTP 500 এর সম্মুখীন হবেন। SQLite পুরো ডাটাবেস জুড়ে একটি রাইট লক (write lock) তৈরি করে, আর Nextcloud ক্রমাগত ফাইল লক, অ্যাক্টিভিটি রো এবং জব স্টেট লিখতে থাকে। Postgres বা MariaDB দিয়ে শুরু করুন; occ db:convert-type বিদ্যমান থাকলেও এটি লাইভ ডাটার ওপর একটি দীর্ঘ এবং ঝুঁকিপূর্ণ মাইগ্রেশন প্রক্রিয়া।
একটি Nextcloud VPS-এর জন্য আসলে কতটুকু RAM প্রয়োজন?
ব্যবহারকারীর সংখ্যার ওপর ভিত্তি করে নয়, বরং কনকারেন্সি (concurrency) বা একই সাথে কতজন ব্যবহার করছে তার ওপর ভিত্তি করে সাইজ নির্ধারণ করুন। সবচেয়ে খারাপ পরিস্থিতিতে মেমোরি ব্যবহারের পরিমাণ হলো সমান্তরাল অনুরোধের সংখ্যা গুণ PHP_MEMORY_LIMIT, এর সাথে Postgres শেয়ার্ড বাফার, প্রতি কানেকশনে একটি ব্যাকএন্ড এবং প্রিভিউ জেনারেশনের সময় যে স্পাইক হয় তা যোগ করতে হবে। আপনি যদি প্রিভিউ সীমিত রাখেন এবং swap যোগ করেন, তবে একটি 2 GB এর সার্ভারে ছোট পরিবারের জন্য ইনস্ট্যান্স চালানো সম্ভব; কিন্তু Collabora বা ফুল-টেক্সট সার্চ যোগ করলে আপনাকে দ্বিতীয় সেট সার্ভিসগুলোর জন্য আলাদা মেমোরি বরাদ্দ করতে হবে।
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 কেন লুপে রিডাইরেক্ট করে বা reverse proxy সম্পর্কে সতর্কবার্তা দেয়?
কন্টেইনারটি 127.0.0.1 এ nginx-কে দেখতে পায় না, এটি Docker ব্রিজ গেটওয়েকে দেখে, যা 172.x এর কোথাও থাকে। যখন সেই ঠিকানাটি TRUSTED_PROXIES এ অনুপস্থিত থাকে, তখন X-Forwarded-Proto: https হেডারটি উপেক্ষা করা হয়, Nextcloud http:// ইউআরএল তৈরি করে এবং প্রক্সি সেগুলোকে আবার ফেরত পাঠায়। TRUSTED_PROXIES কে প্রকৃত ব্রিজ সাবনেটে সেট করুন এবং OVERWRITEPROTOCOL: https কে পিন করুন।
আমি কি Nextcloud-কে সরাসরি 29 থেকে 31 ভার্সনে আপগ্রেড করতে পারি?
না। Nextcloud প্রতি আপগ্রেডে একটি মাত্র মেজর ভার্সন সমর্থন করে, এবং ভার্সন বাদ দিয়ে আপগ্রেড করলে Updates between multiple major versions and downgrades are unsupported. এর কারণে তা আটকে যাবে এবং ইনস্ট্যান্সটি মেইনটেন্যান্স মোডে চলে যাবে। ব্যাকআপ নিন, app এবং cron উভয় সার্ভিসেই ট্যাগটি একটি মেজর ভার্সন বাড়ান, docker compose pull && docker compose up -d করুন, occ status দিয়ে যাচাই করুন এবং তারপর আবার একই প্রক্রিয়া অনুসরণ করুন।