SSD Nodes Learn 🎉 VPS $5.50/ماہ سے
تعلیمی Matt Connorتحریر: Matt Connor

Docker Compose stack کا backup اور upgrade کیسے کریں

صرف login screen تک محدود نہ رہیں: compose file، secrets، volumes اور database dump محفوظ کریں، restore ثابت کریں، پھر upgrade سے پہلے rollback کا جائزہ لیں۔

Docker Compose stack کے backup میں کیا شامل ہونا چاہیے

Docker Compose stack کے backup میں چار الگ چیزیں شامل ہونی چاہییں۔ ان میں سے کوئی ایک بھی ضائع ہو جائے تو ایپ دوبارہ بحال نہیں ہوتی: compose file، اس کے ساتھ موجود .env، ہر volume کا تمام data، اور database کے اپنے client سے بنایا گیا database dump۔ container چلنے کے دوران database کی files copy کرنا backup نہیں ہے۔ Upgrades میں بھی یہی فہرست استعمال ہوتی ہے، لیکن ایک اصول کے ساتھ: pull چلانے سے پہلے backup لیں، کیونکہ schema migrations آگے کی سمت چلنے کے لیے بنائی جاتی ہیں اور زیادہ تر projects میں واپس جانے کا کوئی طریقہ نہیں ہوتا۔

ذیل کی تمام ہدایات اس مفروضے پر مبنی ہیں کہ stack پہلے سے deployed ہے اور docker compose ps میں وہ running دکھائی دے رہا ہے۔ مثالوں میں /srv/myapp میں موجود project directory اور app اور db نام کی services استعمال کی گئی ہیں۔ اپنے نام استعمال کریں۔ Commands جان بوجھ کر generic رکھی گئی ہیں، کیونکہ اہم حصے، یعنی volumes اور database، app سے قطع نظر اسی طرح کام کرتے ہیں۔

معلوم کریں کہ آپ کا stack حقیقت میں کیا محفوظ کرتا ہے

cd /srv/myapp
docker compose ps
docker compose config --volumes
docker volume ls --filter label=com.docker.compose.project=myapp

docker compose config --volumes آپ کی file میں اعلان کردہ named volumes کے مختصر نام دکھاتا ہے۔ docker volume ls ان volumes کے وہ نام دکھاتا ہے جو disk پر حقیقتاً موجود ہیں۔ دونوں فہرستیں مختلف ہوتی ہیں، کیونکہ Compose نام کے شروع میں project name شامل کرتا ہے: file میں db_data کے طور پر لکھا volume دراصل myapp_db_data کے نام سے موجود ہوتا ہے۔ project name بطور default directory name ہوتا ہے۔ اس لیے directory کا نام بدلنے سے stack خالی volumes کے ایک نئے مجموعے کی طرف متوجہ ہو جاتا ہے، جبکہ پرانے volumes آپ کا تمام data لیے وہیں موجود رہتے ہیں۔ ذیل کی ہر command میں docker volume ls سے حاصل کیا گیا حقیقی نام استعمال کریں۔

Bind mounts کسی بھی فہرست میں ظاہر نہیں ہوتے۔ compose file میں یہ وہ entries ہوتی ہیں جن میں colon کے بائیں جانب host path ہوتا ہے، ./config:/app/config۔ یہ host پر موجود عام directories ہوتی ہیں، اس لیے عام tools ان تک رسائی حاصل کر سکتے ہیں۔ Named volumes /var/lib/docker/volumes/ کے تحت موجود ہوتے ہیں، اور docker volume inspect --format '{{.Mountpoint}}' myapp_db_data کسی ایک volume کا درست path دکھاتا ہے۔ آپ کا stack کس قسم کا volume استعمال کرتا ہے، اس سے copy کرنے کا طریقہ بدل جاتا ہے۔ bind mounts اور named volumes کا موازنہ میں اس فرق کی مکمل وضاحت موجود ہے۔

اب حاصل کردہ معلومات کو دو گروپوں میں تقسیم کریں۔ کچھ volumes ایسی state رکھتے ہیں جسے کوئی چیز دوبارہ تخلیق نہیں کر سکتی: uploaded files، generated keys، خود database، اور وہ تمام data جو user نے app میں درج کیا ہو۔ دوسرے volumes derived data رکھتے ہیں، مثلاً thumbnails اور search indexes، جنہیں app خود دوبارہ بنا لیتی ہے۔ دوسرے گروپ کا backup لینے سے disk space اور restore time خرچ ہوتے ہیں، مگر کوئی فائدہ نہیں ملتا۔ Redis cache volume اس کی واضح مثال ہے: اسے کھونے سے صرف پہلی request سست ہو جاتی ہے۔

compose file اور .env file کا بیک اپ لیں

دونوں files host پر ایک دوسرے کے ساتھ موجود ہیں، اور ان میں سے کوئی بھی volume کے اندر نہیں ہے۔ .env میں database password، application secret اور API tokens ہوتے ہیں، اس لیے یہی وہ file ہے جو volumes کے مجموعے کو دوبارہ کام کرنے والی app میں تبدیل کرتی ہے۔ عموماً یہ .gitignore میں بھی درج ہوتی ہے، جس کا مطلب ہے کہ "میری configuration git میں ہے" والا منصوبہ اسی واحد file کو خارج کر دیتا ہے جو سب سے زیادہ اہم ہے۔ secrets کو env file میں رکھنا درست طریقہ ہے، اور اس کے مطابق backup میں بھی یہی file شامل کرنا ضروری ہے۔

sudo install -d -m 700 -o "$USER" -g "$(id -gn)" /srv/backups/myapp
cp -a compose.yaml .env /srv/backups/myapp/
chmod 600 /srv/backups/myapp/.env

Stack کے زیر استعمال ہر compose file copy کریں، صرف پہلی file نہیں۔ -f compose.yaml -f compose.prod.yaml سے شروع کیے گئے stack کو اسی طرح بحال کرنے کے لیے دونوں files درکار ہوتی ہیں، اور متعدد compose files کیسے merge ہوتی ہیں اس بات کا تعین کرتا ہے کہ آخر کون سی values container تک پہنچتی ہیں۔

ایک اہم انتباہ .env کو volumes سے جوڑتا ہے۔ official Postgres image POSTGRES_PASSWORD کو صرف اس وقت پڑھتی ہے جب وہ empty data directory کو initialize کرتی ہے۔ بعد میں اس value کو تبدیل کرنے سے database کے اندر password تبدیل نہیں ہوتا۔ پچھلے ماہ کا volume آج کی .env کے ساتھ بحال کریں تو app FATAL: password authentication failed for user "appuser" کے ساتھ connect نہیں ہو پاتی، حالانکہ جانچ کے وقت دونوں files درست نظر آتی ہیں۔ .env اور volumes کو ایک ہی وقت کی حالت میں، ایک ہی backup میں ساتھ رکھیں۔

ڈیٹابیس کو اس کے اپنے client سے dump کریں

ڈیٹابیس سرور اپنی files میں مسلسل لکھتا رہتا ہے۔ سرور کے چلنے کے دوران بنائی گئی tar کی /var/lib/postgresql/data کچھ pages کو write سے پہلے اور کچھ کو write کے بعد copy کرتی ہے، اس لیے archive میں مختلف لمحات کا ملا جلا data ہوتا ہے جسے دوبارہ لاگو نہیں کیا جا سکتا۔ dump tool ایک ہی transaction کے اندر data پڑھتا ہے، اس لیے file میں ایک ہی consistent لمحے کی حالت محفوظ ہوتی ہے۔ یہی فرق backup اور copy میں امتیاز کرتا ہے۔

docker compose exec -T db sh -c \
  'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' \
  > /srv/backups/myapp/db-$(date +%F).dump

-T برقرار رکھیں۔ یہ TTY allocation بند کرتا ہے۔ TTY منسلک ہونے پر Docker output stream کو shell تک پہنچاتے ہوئے تبدیل کرتا ہے، جس سے binary dump خراب ہو جاتا ہے۔ اس کا پتا restore ناکام ہونے تک نہیں چلتا۔ Single quotes بھی اہم ہیں۔ یہ host shell کو $POSTGRES_USER expand کرنے سے روکتے ہیں، اس لیے container کے اندر موجود shell اسے expand کرتا ہے اور compose file میں پہلے سے مقرر values استعمال کرتا ہے۔ -Fc custom format لکھتا ہے۔ یہ data کو لکھتے وقت compress کرتا ہے اور بعد میں pg_restore کو اس میں سے مخصوص objects منتخب کرنے دیتا ہے۔

Roles اور ان کے passwords کسی ایک database کے اندر محفوظ نہیں ہوتے، اس لیے انہیں بھی محفوظ کریں:

docker compose exec -T db sh -c 'pg_dumpall -U "$POSTGRES_USER" --globals-only' \
  > /srv/backups/myapp/globals.sql

پھر تصدیق کریں کہ file واقعی dump ہے، error message نہیں:

ls -lh /srv/backups/myapp/
head -c 5 /srv/backups/myapp/db-$(date +%F).dump

Custom-format dump کے آغاز میں پانچ bytes PGDMP ہوتے ہیں۔ صفر bytes والی file، یا pg_dump: سے شروع ہونے والی file، اس بات کی نشاندہی کرتی ہے کہ command ناکام ہو گئی۔ Shell command چلنے سے پہلے output file بنا دیتا ہے، اس لیے ناکام dump کے بعد بھی ایک ایسی file رہ جاتی ہے جس کا نام اور timestamp بظاہر درست ہوتا ہے۔ یہی خاموش backup failure کی سب سے عام صورت ہے۔

MariaDB یا MySQL کے لیے client بدل جاتا ہے، لیکن طریقہ وہی رہتا ہے:

docker compose exec -T db sh -c \
  'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" --single-transaction --databases "$MARIADB_DATABASE"' \
  > /srv/backups/myapp/db-$(date +%F).sql

--single-transaction writers کو block کیے بغیر InnoDB tables کا consistent dump بناتا ہے۔ MySQL image میں command mysqldump ہے اور variables MYSQL_ROOT_PASSWORD اور MYSQL_DATABASE ہیں۔ موجودہ MariaDB images میں mysqldump اب بھی mariadb-dump کے compatibility name کے طور پر کام کرتا ہے۔ یاد رکھیں کہ command line پر دیا گیا password dump کے چلنے تک container کی process list میں نظر آتا رہتا ہے۔

SQLite کے لیے الگ احتیاط درکار ہے۔ Database ایک file ہوتی ہے، لیکن حالیہ transactions اب بھی اس کے ساتھ موجود الگ -wal file میں ہو سکتی ہیں۔ صرف .db copy کرنے سے database میں تازہ ترین writes شامل نہیں ہوتیں۔ اگر image میں client موجود ہو تو sqlite3 /data/app.db ".backup '/data/app-backup.db'" ایپ کے چلتے ہوئے consistent copy بناتا ہے۔ اگر client موجود نہ ہو تو container روکیں اور .db file کو اس کی -wal اور -shm companion files کے ساتھ copy کریں۔

اگر آپ کا database stack کے اندر کے بجائے host پر چلتا ہے تو docker compose exec prefix کے بغیر یہی commands لاگو ہوتی ہیں۔ اگلی rebuild سے پہلے database کو Docker یا host پر چلانا ضرور پڑھیں۔

volumes محفوظ کریں

Named volume کے لیے کوئی host path موجود نہیں ہوتا جسے آپ ہاتھ سے edit کریں۔ اس لیے اسے عارضی container میں mount کریں اور وہیں سے archive بنائیں۔

docker run --rm \
  -v myapp_uploads:/data:ro \
  -v /srv/backups/myapp:/backup \
  alpine:3 tar czf /backup/uploads.tar.gz -C /data .

Helper container volume کو /data پر read-only اور آپ کی backup directory کو /backup پر mount کرتا ہے، پھر archive کو host side پر لکھتا ہے۔ --rm، tar کے exit ہوتے ہی helper کو حذف کر دیتا ہے۔ :ro اہم ہے، کیونکہ غلط لکھا ہوا tar command source کو نقصان نہیں پہنچا سکتا۔ -C /data . restore کو درست جگہ پر پہنچاتا ہے: یہ ہر path کو volume root کے نسبت سے محفوظ کرتا ہے۔ اگر اس کے بجائے tar czf /backup/uploads.tar.gz /data لکھیں تو ہر path کے شروع میں data/ شامل ہو جائے گا۔ اس سے restore کے دوران volume کے اندر /data/data بنے گا اور app کو خالی directory نظر آئے گی۔ Archive کا مالک root ہے، کیونکہ container کے اندر tar نے root کے طور پر کام کیا۔ اگر یہ مسئلہ بنے تو sudo chown "$USER" /srv/backups/myapp/uploads.tar.gz چلائیں۔ اگر restore کی گئی files app کے لیے unreadable ہوں تو PUID اور PGID file ownership کا فیصلہ کیسے کرتے ہیں پڑھیں۔

ہر named volume کے لیے اسے ایک بار چلائیں۔ Bind mounts کے لیے container کی ضرورت نہیں: tar czf /srv/backups/myapp/config.tar.gz -C /srv/myapp/config . host پر یہی کام کرتا ہے۔

ہر volume کے لیے فیصلہ کریں کہ app کو روکنا ضروری ہے یا نہیں۔ اگر app volume میں موجود files کو اسی جگہ دوبارہ لکھتی رہے تو live tar کسی file کو write کے دوران درمیان سے copy کر سکتا ہے۔ Uploads directory کے لیے، جہاں files ایک بار لکھی جاتی ہیں اور اس کے بعد صرف پڑھی جاتی ہیں، یہ خطرہ کم ہے۔ دوسری ہر صورت میں copy کے دورانیے کے لیے اس service کو docker compose stop app سے روکیں، پھر docker compose start app چلائیں۔ stop containers اور volumes کو اپنی جگہ برقرار رکھتا ہے، جو یہاں بالکل مطلوب ہے۔ اس لیے دونوں میں سے کسی command کو type کرنے سے پہلے down اور stop کے درمیان فرق ضرور سمجھ لیں۔

Database volume کے tar کو database backup نہ سمجھیں۔ Dump ہی backup ہے۔ Stopped database کے volume کا archive تیزی سے rebuild کرنے کے لیے مفید ہے، اس سے زیادہ کچھ نہیں۔

عملی کارروائیوں کی ترتیب

  1. Compose files اور .env کو backup directory میں copy کریں۔
  2. Database کے چلتے ہوئے اس کا dump بنائیں۔
  3. اگر app container کے volumes براہِ راست تبدیل ہوتے ہیں تو اسے stop کریں۔
  4. ہر named volume اور ہر bind-mount directory کا archive بنائیں۔
  5. جن سروسز کو stop کیا تھا انہیں دوبارہ start کریں، پھر docker compose ps سے تصدیق کریں۔
  6. چلنے والے stack کے image tags اور digests لکھ کر محفوظ کریں۔
  7. پوری backup directory کو اس server سے باہر copy کریں۔

مرحلہ 7 وہ کام ہے جسے لوگ بعد کے لیے چھوڑ دیتے ہیں۔

سرور سے backup کی نقل باہر منتقل کریں

stack والی اسی disk پر موجود backup آپ کو صرف اپنی غلطیوں سے بچاتا ہے، کسی اور failure سے نہیں۔ ایک volume کے fail ہونے، ایک server کے delete ہو جانے، یا ایک account کے ضائع ہونے سے دونوں copies ایک ہی وقت میں ختم ہو جاتی ہیں۔ directory کو مقررہ schedule کے مطابق ایسی storage پر push کریں جو اس VPS پر موجود نہ ہو، اور retention policy نافذ کریں۔ VPS سے restic backups میں repository setup، retention flags اور check command شامل ہیں، اس لیے انہیں یہاں دوبارہ بیان کرنے کی ضرورت نہیں۔

restic dump کو pipe سے براہ راست بھی پڑھ سکتا ہے۔ اس طرح plaintext database مکمل طور پر disk پر محفوظ ہونے سے بچ جاتی ہے:

docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' \
  | restic backup --stdin --stdin-filename db.dump

آپ کوئی بھی tool استعمال کریں، schedule کو systemd timer یا cron job میں شامل کریں، اور job کی failure report ایسی جگہ بھیجیں جہاں آپ اسے دیکھ سکیں۔ جس backup script کا output کہیں نہ جاتا ہو، وہ چھ ماہ تک کام کرنا بند رکھ سکتی ہے اور کسی کو معلوم بھی نہیں ہوتا۔

بیک اپ کے درست کام کرنے کی تصدیق restore drill سے کریں

جس بیک اپ سے کبھی restore نہیں کیا گیا، وہ صرف ایک مفروضہ ہے۔ ذیل کی drill پہلے stack کے ساتھ چلنے والے دوسرے stack میں restore کرتی ہے، اس لیے production سروس فراہم کرتا رہتا ہے اور آپ کی ٹائپ کی ہوئی کوئی چیز اس تک نہیں پہنچ سکتی۔

اس کا طریقہ project name ہے۔ Compose یہ نام directory name سے لیتا ہے اور اپنے بنائے ہوئے ہر container اور volume پر درج کرتا ہے۔ بیک اپ کو نئی directory میں copy کریں، تو restored stack کو خودکار طور پر اپنے الگ volumes مل جاتے ہیں۔

sudo install -d -m 700 -o "$USER" -g "$(id -gn)" /srv/myapp-restore
cd /srv/myapp-restore
cp /srv/backups/myapp/compose.yaml /srv/backups/myapp/.env .

کاپی کی گئی compose file میں ترمیم کریں تاکہ published host port چلنے والے stack سے متصادم نہ ہو، 18080:8080 کو 8080:8080 کی جگہ استعمال کریں، یا کاپی کی گئی .env میں وہ variable تبدیل کریں جو اسے set کرتا ہے۔ پھر containers اور ان کے خالی volumes بنائیں، مگر کچھ start نہ کریں:

docker compose create
docker volume ls --filter label=com.docker.compose.project=myapp-restore

دوسری command میں production کے وہی volume names سامنے آنے چاہییں، جن کے شروع میں myapp-restore_ ہو۔ انہیں data سے بھریں، database کو الگ سے start کریں، اور dump load کریں:

docker run --rm -v myapp-restore_uploads:/data -v /srv/backups/myapp:/backup \
  alpine:3 tar xzf /backup/uploads.tar.gz -C /data
docker compose up -d db
docker compose exec -T db sh -c \
  'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists' \
  < /srv/backups/myapp/db-2026-08-16.dump

--clean --if-exists ہر object کو دوبارہ بنانے سے پہلے delete کرتا ہے، جس سے restore دوبارہ چلانا ممکن رہتا ہے۔ اس کے بغیر پہلے سے موجود tables والے database میں دوسری بار چلانے پر pg_restore: error: could not execute query: ERROR: relation "users" already exists کے ساتھ عمل رک جاتا ہے۔

پھر باقی سروسز start کریں اور انہیں اسی طرح check کریں جیسے کوئی user کرتا ہے:

docker compose up -d --wait
docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt"'
docker compose logs --tail=50

docker compose up -d --wait اس وقت تک انتظار کرتا ہے جب تک ہر service running یا healthy نہ بتائی جائے، اور اگر کوئی service کبھی اس حالت میں نہ پہنچے تو non-zero exit کرتا ہے۔ اسی وجہ سے اس مرحلے کو script میں شامل کیا جا سکتا ہے۔ جب کوئی service healthy نہ ہو تو docker compose ps اس کی state دکھاتا ہے، اور Compose healthchecks بتاتا ہے کہ یہ column کس چیز کو پڑھ رہا ہے۔ پھر alternate port پر app کھولیں اور کسی حقیقی account سے log in کریں۔ ایک record لکھیں اور volume میں موجود ایک file کھولیں۔ یہی دونوں چیزیں ثبوت ہیں: dump restore ہوا، volume restore ہوا، اور دونوں ایک دوسرے سے مطابقت رکھتے ہیں۔ ایسی drill جس سے صرف login page کے render ہونے کی تصدیق ہو، آپ کے data کے بارے میں کچھ ثابت نہیں کرتی۔

کامیابی کے بعد drill کو ختم کریں:

docker compose down -v

یہ واحد جگہ ہے جہاں -v درست flag ہے۔ production directory میں یہی command ان volumes کو delete کر دیتی ہے جنہیں آپ محفوظ رکھنے کی کوشش کر رہے ہیں۔

Compose stack کو upgrade کرنے کا طریقہ

جس version کو آپ چلا رہے ہیں اور جس version پر جانا چاہتے ہیں، ان کے درمیان آنے والے ہر version کے release notes پڑھیں، اور ان میں breaking اور migration الفاظ تلاش کریں۔ جو projects کئی major versions کو ایک ساتھ عبور کرنے کی اجازت نہیں دیتے، وہ یہ بات وہیں بتاتے ہیں۔ جو migration چلنے سے انکار کرتی ہے، وہ اکثر یہ انکار schema کے کچھ حصے میں تبدیلی کرنے کے بعد ظاہر کرتی ہے۔

کسی بھی تبدیلی سے پہلے موجودہ configuration اور versions محفوظ کریں:

docker compose images
docker image inspect --format '{{index .RepoDigests 0}}' postgres:16.4

docker compose images ہر service کے موجودہ image اور tag کی فہرست دکھاتا ہے۔ کسی image کی درست شناخت صرف digest کرتا ہے، کیونکہ tag کو کسی بھی وقت کسی دوسری image کی طرف منتقل کیا جا سکتا ہے۔

اوپر والے sections میں بتایا گیا backup لیں اور اسے server سے باہر copy کریں۔ Patch release کے لیے بھی یہی کریں۔ آسان upgrades وہی ہوتے ہیں جن کی تیاری لوگ چھوڑ دیتے ہیں۔

اس کے بعد compose file میں version کو pin کریں، کیونکہ latest کوئی version نہیں ہے:

services:
  db:
    image: postgres:16.4

image: postgres:latest کے ساتھ docker compose pull آج اس tag کے موجودہ مقام پر موجود image fetch کرتا ہے، اور آپ کے پاس یہ بتانے کا کوئی طریقہ نہیں رہتا کہ کل کون سی image چل رہی تھی۔ Pinned tag upgrade کو ایک ایسی ایک سطری تبدیلی بنا دیتا ہے جسے آپ git diff میں پڑھ سکتے ہیں اور ایک مزید edit سے واپس کر سکتے ہیں۔ Application image کو بھی اسی طرح pin کریں، اور project کے release page سے exact version لیں۔

Images pull کریں اور containers دوبارہ بنائیں:

docker compose pull
docker compose up -d --wait

docker compose up -d compose file کا running containers سے موازنہ کرتا ہے اور صرف ان services کو دوبارہ بناتا ہے جن کی image یا configuration تبدیل ہوئی ہو۔ یہ named volumes کو نہیں چھیڑتا، اس لیے نیا container موجودہ data کے ساتھ start ہوتا ہے۔ یہی اس عمل کا مقصد ہے، اور یہی اس کا خطرہ بھی ہے، کیونکہ نئی version کی پہلی startup پر عموماً اس کی schema migration چلتی ہے۔

اس عمل کی نگرانی کریں:

docker compose ps
docker compose logs -f --tail=100 app

جو container fail ہو جائے، اس کے docker compose ps کے STATUS column میں Exited (1) دکھائی دیتا ہے، اور وجہ اس کے log کی آخری سطروں میں ہوتی ہے۔ Migration errors وہاں واضح ہوتی ہیں اور باقی جگہ نظر نہیں آتیں۔ جب logs میں نئی entries آنا رک جائیں، تو login کریں اور ایک منٹ تک app استعمال کریں۔

اگر docker compose pull، no space left on device کے ساتھ رک جائے، تو عموماً وجہ پرانی image layers ہوتی ہیں۔ غیر استعمال شدہ Docker images کو prune کرنا جگہ واپس حاصل کر دیتا ہے۔ Pruning upgrade کے کامیاب ثابت ہونے کے بعد کریں، پہلے نہیں، کیونکہ تیز rollback انہی پرانی layers پر چلتا ہے۔

اپ گریڈ میں خرابی آنے پر واپس کیسے جائیں

اس کے دو معاملات ہیں، اور ان کی لاگت میں بہت فرق ہوتا ہے۔ اگر نئے ورژن نے schema تبدیل نہیں کیا تو rollback ایک سطر کا کام ہے: compose file میں پرانا tag واپس رکھیں اور docker compose up -d چلائیں۔ container دوبارہ بنایا جاتا ہے، volumes اپنی جگہ برقرار رہتے ہیں، اور پرانا code وہ data پڑھ لیتا ہے جو اسی نے لکھا تھا۔

اگر نئے ورژن نے schema migrate کیا ہے تو پرانا code اسے مزید نہیں پڑھ سکتا۔ Migrations آگے کی سمت چلنے کے لیے لکھی جاتی ہیں، اور زیادہ تر projects کوئی downgrade script بالکل فراہم نہیں کرتے۔ اس لیے پرانا version start ہونے کے بعد پہلے ہی query پر fail ہو جاتا ہے، جب وہ rename یا drop کیے گئے column تک پہنچتا ہے۔ ایسی errors کی شکل ERROR: column "avatar_url" does not exist جیسی ہوتی ہے۔ واپسی کا راستہ وہ dump ہے جو pull کرنے سے پہلے لیا گیا تھا: پرانا tag واپس رکھیں، database volume ہٹا دیں، اسے خالی حالت میں دوبارہ بنائیں، dump اس میں restore کریں، اور start کریں۔ اس dump کے بغیر واپسی کا کوئی راستہ نہیں ہوتا۔ اسی لیے backup، pull سے پہلے لیا جاتا ہے۔

Postgres major versions میں یہ مسئلہ سب سے زیادہ نمایاں ہوتا ہے، اور لوگوں کو اس لیے حیران کرتا ہے کہ خرابی rollback کے وقت نہیں بلکہ upgrade کے وقت ظاہر ہوتی ہے۔ On-disk format ہر major release کے ساتھ تبدیل ہوتا ہے۔ postgres:16.4 کو postgres:17.2 میں تبدیل کریں، docker compose up -d چلائیں، اور نیا server start ہونے سے انکار کر دیتا ہے:

FATAL:  database files are incompatible with server
DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.2.

Image آپ کے لیے pg_upgrade نہیں چلاتی۔ Compose stack کے اندر supported طریقہ dump، replace، restore ہے: پرانا version چلتے ہوئے dump لیں، docker compose down، database volume ہٹائیں، نیا tag مقرر کریں، تازہ خالی data directory کے لیے docker compose create چلائیں، database start کریں، dump restore کریں، پھر باقی services start کریں۔ پرانا dump اس وقت تک محفوظ رکھیں جب تک نیا major ایک دن حقیقی network traffic handle نہ کر لے۔ ایک ہی major کے اندر معمولی upgrades، جیسے 16.4 سے 16.9، میں یہ سب ضروری نہیں ہوتا، کیونکہ format ان کے درمیان مستحکم رہتا ہے اور container معمول کے مطابق start ہو جاتا ہے۔

کیا VPS snapshots بیک اپ ہوتے ہیں؟

یہ بیک اپ کا متبادل نہیں بلکہ اس کے ساتھ اضافی تحفظ فراہم کرتے ہیں، اور دونوں مختلف طریقوں سے ناکام ہوتے ہیں۔ snapshot پورے disk کو hypervisor کی سطح پر copy کرتا ہے، اس لیے یہ چند منٹ میں پوری machine واپس لا سکتا ہے، ان حصوں سمیت جن کا بیک اپ لینا آپ بھول گئے تھے۔ اسی لیے یہ ایک مخصوص کام کے لیے درست tool ہے: upgrade سے server خراب ہو گیا ہو اور آپ اسے بیس منٹ پہلے کی حالت میں واپس لانا چاہتے ہوں۔

باقی تمام کاموں کے لیے یہ کم موزوں tool ہے۔ اس کی granularity پوری machine تک محدود ہوتی ہے، اس لیے حذف شدہ ایک table بحال کرنے کے لیے پورا server کہیں restore کرنا پڑتا ہے اور پھر اس میں سے table نکالنی پڑتی ہے۔ Retention عموماً مختصر ہوتی ہے۔ Copies عام طور پر اسی provider account میں رہتی ہیں جس میں server موجود ہوتا ہے، اس لیے account ضائع ہونے پر server اور اس کے snapshots بھی ایک ساتھ ضائع ہو جاتے ہیں۔ مزید یہ کہ چلتی ہوئی machine کا snapshot database کو write کے دوران capture کر سکتا ہے۔ چنانچہ database پہلی بار start ہونے پر crash recovery کرتا ہے، اور جو transaction اس وقت جاری ہو وہ ضائع ہو جاتا ہے۔

دونوں استعمال کریں۔ snapshot upgrade window کے لیے undo button ہے۔ dump وہ copy ہے جو deleted account کے بعد بھی باقی رہتی ہے۔ snapshots اور backups میں فرق کی وضاحت کرتا ہے کہ ہر ایک کن failures سے واقعی تحفظ فراہم کرتا ہے۔ یہی backup directory stack کو نئے VPS پر منتقل کرنا بھی memory سے دوبارہ build کرنے کے بجائے معمول کا کام بنا دیتی ہے۔

کیا غلط ہوتا ہے اور آپ کو کیا نظر آئے گا

down پر volumes flag۔ docker compose down -v فائل میں بیان کردہ named volumes کو حذف کر دیتا ہے، اور Compose Volume myapp_db_data Removed والی سطر دکھا کر اس کی تصدیق کرتا ہے۔ اسے واپس نہیں کیا جا سکتا۔ سادہ docker compose down انہیں برقرار رکھتا ہے۔ طویل صورت، docker compose down --volumes، لکھیں تاکہ حذف کرنے والا flag وہ مکمل لفظ ہو جسے آپ نے خود ٹائپ کیا ہو۔

جادوئی string کے بغیر dump۔ pg_restore: error: did not find magic string in file header کا مطلب ہے کہ فائل archive نہیں ہے۔ عام وجہ docker compose exec پر -T کا نہ ہونا ہے، کیونکہ TTY منسلک ہونے پر stream آپ کے shell تک پہنچتے ہوئے translate ہوتی ہے اور binary dump خراب ہو جاتا ہے۔ -T کے ساتھ dump دوبارہ لیں، پھر head -c 5 سے پہلے 5 bytes چیک کریں۔

وہ password جو تبدیل نہیں ہوگا۔ Restore کے بعد FATAL: password authentication failed for user "appuser" کا مطلب ہے کہ .env اور data directory مختلف اوقات سے لیے گئے تھے۔ Image یہ password صرف اس وقت set کرتی ہے جب وہ خالی data directory بناتی ہے، اس لیے بعد میں .env میں ترمیم کرنے سے database کے اندر کچھ تبدیل نہیں ہوتا۔ مطابقت رکھنے والا .env restore کریں، یا ALTER USER کے ذریعے database کے اندر password تبدیل کریں۔

دوسرا، خالی volume۔ Docker ضرورت کے وقت volume بناتا ہے، اس لیے s کے نہ ہونے پر docker run -v myapp_upload:/data بالکل نئے خالی volume میں لکھتا ہے اور کامیابی کی اطلاع دیتا ہے۔ docker volume ls پھر دونوں نام دکھاتا ہے، جن میں سے ایک کے اندر کچھ بھی نہیں ہوتا۔ volume کے نام docker volume ls سے copy کریں، انہیں حافظے سے نہ لکھیں۔

Production کے لیے کیا گیا restore۔ /srv/myapp-restore کے بجائے /srv/myapp میں restore commands چلانے سے backup کے ذریعے live data overwrite ہو جاتا ہے، اور دونوں جگہ commands یکساں دکھائی دیتی ہیں۔ ہر restore command سے پہلے pwd چیک کریں، اور drill کو اپنی الگ directory میں رکھیں۔

FAQ

کیا docker compose down میرا ڈیٹا حذف کرتا ہے؟

نہیں۔ docker compose down containers اور default network کو ہٹا دیتا ہے، جبکہ named volumes اور bind mounts کو جوں کا توں چھوڑ دیتا ہے۔ docker compose down -v آپ کی file میں declare کیے گئے named volumes کو ہٹا دیتا ہے، اور یہ مستقل عمل ہے۔ Bind mounts، host directories ہوتے ہیں، اس لیے Compose انہیں کبھی نہیں ہٹاتا۔ اگر آپ backup کے دوران services کو روکنا چاہتے ہیں اور باقی سب کچھ جوں کا توں رکھنا چاہتے ہیں، تو اس کے بجائے docker compose stop استعمال کریں۔

کیا میں pg_dump چلانے کے بجائے Postgres کی data directory copy کر سکتا ہوں؟

صرف container روکنے کے بعد۔ Server چلتے وقت اس کی files مسلسل تبدیل ہوتی رہتی ہیں، اس لیے copy میں مختلف اوقات کی ملی جلی حالت شامل ہو سکتی ہے جسے دوبارہ درست طور پر بحال نہیں کیا جا سکتا۔ File-level copy ایک ہی Postgres major version سے بھی منسلک ہوتی ہے، اس لیے یہ کسی مختلف version کے تحت start نہیں ہوگی۔ Container روکیں، volume کو archive کریں، اسے دوبارہ start کریں، اور اس archive کو اپنی واحد backup کے بجائے تیز rebuild path سمجھیں۔ Dump قابلِ منتقلی copy ہے، اور restore اسی سے کریں۔

Compose میں Postgres کو نئے major version پر کیسے upgrade کروں؟

صرف tag تبدیل کرنا کافی نہیں ہے۔ نیا server پرانی data directory کے ساتھ start ہونے سے انکار کرتا ہے اور The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.2 log کرتا ہے۔ پرانا version چلتے ہوئے pg_dump چلائیں، پھر docker compose down چلائیں، database volume ہٹائیں، نیا tag مقرر کریں، تازہ خالی volume کے لیے docker compose create چلائیں، database start کریں، اور dump کو اس میں restore کریں۔ پرانا dump اس وقت تک محفوظ رکھیں جب تک نیا version حقیقی traffic سنبھال نہ لے۔

Backups کتنی بار چلانی چاہییں، اور انہیں کتنے عرصے تک رکھنا چاہیے؟

وقفہ اس مقدار کے مطابق رکھیں جس کام کو دوبارہ کرنے پر آپ آمادہ ہیں۔ ذاتی یا چھوٹی ٹیم کے stack کے لیے nightly backup موزوں ہے، اور ہر upgrade سے فوراً پہلے ایک اضافی manual backup بھی لیں۔ Retention کے لیے اتنی history رکھیں کہ ایسے نقصان کا احاطہ ہو سکے جس کا فوراً علم نہ ہوا ہو، کیونکہ جمعہ کو دریافت ہونے والی corrupted table کے لیے جمعرات رات کی copy مددگار نہیں ہوگی۔ restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune ایک مناسب ابتدائی policy ہے۔ Schedule کچھ بھی ہو، ہر quarter میں ایک بار اسی backup سے restore کریں۔ جب تک آپ یہ نہیں کر لیتے، آپ کے پاس backups نہیں بلکہ صرف files ہیں۔

کیا backup لینے کے لیے پورا stack روکنا ضروری ہے؟

عموماً نہیں۔ Server چلتے وقت database dump consistent رہتا ہے، اس لیے database کو downtime کی ضرورت نہیں ہوتی۔ اصل سوال volumes کا ہے۔ اگر app صرف files شامل کرتی ہے، مثلاً uploads directory، تو live archive کافی محفوظ ہے۔ اگر app files کو اسی جگہ rewrite کرتی ہے، تو copy کے دورانیے کے لیے صرف اس service کو docker compose stop app سے روکیں اور بعد میں اسے دوبارہ start کریں۔ Database کو چلتا رہنے دیتے ہوئے app روکنا عموماً وہ مختصر ترین محفوظ window ہے جس کا انتظام کیا جا سکتا ہے۔