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-এ রাখুন, এবং 600 মোডে একটি পার্শ্ববর্তী .env ফাইলে সিক্রেটগুলো রাখুন।
services:
db:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- db:/var/lib/postgresql/data
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${DB_PASSWORD}
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
app:
image: nextcloud:31-apache
restart: unless-stopped
depends_on: [db, redis]
ports:
- "127.0.0.1:8080:80"
volumes:
- html:/var/www/html
- /srv/nextcloud/data:/var/www/html/data
environment:
POSTGRES_HOST: db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: ${DB_PASSWORD}
REDIS_HOST: redis
REDIS_HOST_PASSWORD: ${REDIS_PASSWORD}
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: ${ADMIN_PASSWORD}
NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.com
TRUSTED_PROXIES: 172.16.0.0/12
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://cloud.example.com
APACHE_DISABLE_REWRITE_IP: "1"
PHP_MEMORY_LIMIT: 512M
PHP_UPLOAD_LIMIT: 10G
cron:
image: nextcloud:31-apache
restart: unless-stopped
entrypoint: /cron.sh
depends_on: [db, redis]
volumes:
- html:/var/www/html
- /srv/nextcloud/data:/var/www/html/data
volumes:
db:
html:মেজর ট্যাগটি পিন করুন এবং 31 হুবহু কপি করার আগে Docker Hub-এ বর্তমান ট্যাগটি যাচাই করে নিন। latest ভবিষ্যতে কোনো এক docker compose pull-এ আপনাকে মেজর ভার্সন পরিবর্তনের দিকে নিয়ে যাবে, যা Nextcloud সমর্থন করে না।
ডেটা ডিরেক্টরিটিকে ইচ্ছাকৃতভাবে named volume-এর পরিবর্তে bind mount হিসেবে রাখা হয়েছে: একটি পাথ যেখানে আপনি সরাসরি ব্যাকআপ টুল পয়েন্ট করতে পারেন, তা পরিচ্ছন্নতার চেয়ে বেশি গুরুত্বপূর্ণ। ইমেজটির www-data UID এবং 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 রুল লেখে যা ufw-এর INPUT চেইন প্যাকেট দেখার আগেই কার্যকর হয়। তাই একটি সাধারণ 8080:80 ব্যবহার করলে ufw যা-ই বলুক না কেন, Nextcloud এনক্রিপশন ছাড়াই পাবলিক ইন্টারনেটে উন্মুক্ত হয়ে যাবে। loopback-এ বাইন্ড করলে এটি পাবলিক ইন্টারফেস থেকে দূরে থাকে। সেক্ষেত্রে ফায়ারওয়ালকে শুধু প্রক্সির জন্য অনুমতি দিতে হয়। আপনি যদি SSH-কে পুরো ইন্টারনেটের জন্য উন্মুক্ত রাখতে না চান, তবে একটি self-hosted WireGuard VPN-এর মাধ্যমে VPS-এ প্রবেশ করলে পাবলিক রুল থেকে পোর্ট 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 পর্যবেক্ষণ করুন। প্রথমবার বুট হওয়ার সময় পুরো অ্যাপ্লিকেশন ট্রি ভলিউমে কপি হয় এবং ইনস্টলার রান করে; সেই প্রক্রিয়া শেষ না হওয়া পর্যন্ত কন্টেইনার কোনো সাড়া দেয় না।
TLS এবং রিভার্স প্রক্সি
আপনার ডিস্ট্রিবিউশন থেকে nginx এবং certbot ইনস্টল করুন, সঠিক server_name সহ একটি সাধারণ port-80 সার্ভার ব্লক তৈরি করুন এবং তারপর certbot-কে এটি rewrite করতে দিন। HTTP-01 চ্যালেঞ্জের কার্যপদ্ধতি, রিনিউয়াল টাইমার এবং ব্যর্থতার কারণগুলো 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 টাইমার ইনস্টল করে যা 90 দিনের সার্টিফিকেট রিনিউ করে। systemctl list-timers | grep certbot ব্যবহার করে নিশ্চিত করুন যে এটি বিদ্যমান আছে; একটি রিনিউয়াল টাইমার যা কখনো চালু (enable) করা হয়নি, তা একটি 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 এবং দীর্ঘ রিড টাইমআউটগুলো বড় ফাইল আপলোড মাঝপথে বন্ধ হয়ে যাওয়া থেকে রক্ষা করে। proxy_request_buffering off ফাইলটি প্রথমে প্রক্সির ডিস্কে জমা না করে সরাসরি স্ট্রিম করে আপলোড করে।
একটি অ্যাপের জন্য হোস্টের nginx ব্যবহার করাই সবচেয়ে সহজ সমাধান। যদি Nextcloud অন্যান্য কন্টেইনারের সাথে VPS শেয়ার করে, তবে একাধিক অ্যাপের জন্য Docker Compose রিভার্স প্রক্সি হিসেবে Traefik চালানো রাউটিং এবং সার্টিফিকেট ইস্যু করার কাজকে কন্টেইনার লেবেলে নিয়ে যায়, এবং একই client_max_body_size ও টাইমআউটের বিষয়গুলো সেখানে মিডলওয়্যার এবং ট্রান্সপোর্ট সেটিংস হিসেবে পুনরায় দেখা দেয়।
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।
আপগ্রেড: একবারে একটি মেজর ভার্সন
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 করুন। ইমেজের এন্ট্রি পয়েন্ট বিদ্যমান ডেটার বিপরীতে নতুন কোড শনাক্ত করে এবং নিজেই 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 চালাবেন না। যদি আপনার স্টোরেজের বড় অংশ ফোনের ক্যামেরা রোল হয়, তবে সেই থাম্বনেইল তৈরির কাজ একটি আলাদা ফটো সার্ভারে স্থানান্তর করুন। 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 চালাতে পারি?
আপনি পারেন এবং অফিসিয়াল ইমেজ আপনাকে তা করতে দেবে, কিন্তু একটি ডেস্কটপ সিঙ্ক ক্লায়েন্ট যখন সমান্তরাল অনুরোধ পাঠাবে তখন আপনি SQLSTATE[HY000]: General error: 5 database is locked এবং HTTP 500 এর সম্মুখীন হবেন। SQLite পুরো ডাটাবেস জুড়ে রাইট লক (write lock) ব্যবহার করে, আর Nextcloud ক্রমাগত ফাইল লক, অ্যাক্টিভিটি রো এবং জব স্টেট লিখতে থাকে। Postgres বা MariaDB দিয়ে শুরু করুন; occ db:convert-type বিদ্যমান থাকলেও এটি লাইভ ডাটার ওপর একটি দীর্ঘ এবং ঝুঁকিপূর্ণ মাইগ্রেশন প্রক্রিয়া।
একটি Nextcloud VPS-এর আসলে কতটুকু RAM প্রয়োজন?
ব্যবহারকারীর সংখ্যার ওপর ভিত্তি করে নয়, বরং কনকারেন্সি বা সমান্তরাল ব্যবহারের ওপর ভিত্তি করে সাইজ নির্ধারণ করুন। সবচেয়ে খারাপ পরিস্থিতিতে মেমোরি ব্যবহারের পরিমাণ হলো সমান্তরাল অনুরোধের সংখ্যা গুণ 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:// URL তৈরি করে এবং প্রক্সি সেগুলোকে আবার ফেরত পাঠায়। 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 দিয়ে যাচাই করুন এবং তারপর পুনরায় একই ধাপ অনুসরণ করুন।