SSD Nodes Learn 🎉 VPS $5.50/ماہ سے
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-13

Database کو Docker میں چلائیں یا host پر؟

Production میں PostgreSQL، MySQL، MongoDB یا Redis کے لیے Docker قابلِ عمل ہے، مگر volumes، upgrades، backups اور memory limits کی یہ 4 عملی جانچ ضروری ہے۔

کیا database کو Docker میں چلانا چاہیے یا host پر؟

database کو Docker میں چلائیں۔ ایک VPS پر ایک application stack کے لیے containerised PostgreSQL، MySQL، MongoDB یا Redis کا استعمال production کا عام انتخاب ہے، اور اس موضوع پر ہونے والی بحث عموماً اصل مسئلے سے ہٹ جاتی ہے۔ Container ایک Linux process ہوتا ہے جس کے گرد namespaces اور cgroups موجود ہوتے ہیں؛ یہ virtual machine نہیں ہوتا۔ اس لیے database اور disk کے درمیان کوئی hypervisor نہیں ہوتا۔ bind mount یا local named volume کے ذریعے read اور write اسی host filesystem پر ہوتے ہیں جسے package installation بھی استعمال کرتی ہے۔

اصل لاگت operational ہوتی ہے۔ چار چیزیں طے کرتی ہیں کہ یہ setup مناسب رہے گا یا آہستہ آہستہ بڑا مسئلہ بن جائے گا: data کہاں محفوظ ہے، اس directory کا مالک کون ہے، major version upgrade کیسے کیا جائے گا، اور کیا آپ نے کبھی backup restore کیا ہے۔ اگر یہ چیزیں درست ہوں تو container صرف ایک جزوی تفصیل رہ جاتا ہے۔ اگر یہ غلط ہوں تو آپ مسئلے کا الزام container پر دیں گے۔

یہی فیصلہ ہر server database کے لیے ہوتا ہے۔ ذیل کی مثالوں میں PostgreSQL، MySQL، MongoDB اور Redis استعمال کیے گئے ہیں۔ جہاں product-specific فرق اہم ہو، وہاں اس کی وضاحت کی گئی ہے۔

کنٹینر حقیقت میں کیا تبدیل کرتا ہے

جب تک آپ storage path کو mount کرتے ہیں، اسے تبدیل نہیں کرتا۔ Kernel وہی رہتا ہے، page cache وہی رہتا ہے، اور filesystem بھی وہی رہتا ہے۔

کارکردگی کا ایک حقیقی مسئلہ اس وقت پیدا ہوتا ہے جب آپ کچھ بھی mount نہیں کرتے۔ Volume کے بغیر data directory کنٹینر کی writable layer میں چلی جاتی ہے، جو image کے اوپر بنی ہوئی overlay filesystem ہوتی ہے۔ وہاں writes سست ہوتی ہیں، اور کنٹینر remove ہونے پر پوری layer delete ہو جاتی ہے۔ اسی وجہ سے صبح کے وقت یہ معلوم ہوتا ہے کہ database خالی ہے۔

حقیقت میں یہ چیزیں تبدیل ہوتی ہیں:

  • Lifecycle۔ docker compose down کنٹینر کو destroy کرتا ہے۔ جو کچھ volume میں موجود نہ ہو، وہ بھی اس کے ساتھ ختم ہو جاتا ہے۔
  • Version۔ Image tag ہی version ہوتا ہے۔ Database container کے اندر کوئی apt upgrade موجود نہیں ہوتا جو اگلے docker compose pull کے بعد بھی برقرار رہے۔
  • Memory accounting۔ Cgroup limit ایک سخت حد ہے جسے kernel نافذ کرتا ہے، اور database کو اس حد کا علم نہیں ہوتا۔
  • User۔ Process کنٹینر کے اندر numeric user id کے طور پر چلتا ہے، جو host پر کسی چیز کا مالک نہ بھی ہو سکتا ہے۔

ڈیٹا کہاں موجود ہے، اس سے سب کچھ طے ہوتا ہے

دو اچھے اختیارات ہیں اور ایک عام غلطی۔

  • Named volume: pgdata:/var/lib/postgresql/data۔ Docker /var/lib/docker/volumes/<project>_pgdata/_data پر directory بناتا ہے، اور image کا entrypoint پہلی بار چلنے پر ownership مقرر کرتا ہے۔ یہ default انتخاب ہے۔
  • Bind mount: /srv/appname/pg:/var/lib/postgresql/data۔ path آپ منتخب کرتے ہیں، اس لیے permissions کے مسئلے کی ذمہ داری بھی آپ کی ہے۔
  • کوئی mount نہیں۔ اوپر دیکھیں۔ ڈیٹا container میں موجود ہوتا ہے۔

اس کا مکمل تقابلی جائزہ ایک الگ موضوع ہے، اور bind mounts اور named volumes کا تقابل اس کا احاطہ کرتا ہے۔ Database کے لیے مختصر اصول یہ ہے: named volume استعمال کریں، جب تک host path معلوم ہونا ضروری نہ ہو۔ اگر bind mount استعمال کریں تو اسے /srv/appname/pg جیسی مستحکم جگہ پر رکھیں، نہ کہ project directory کے اندر، جہاں git clean اس تک پہنچ سکتا ہے۔

ایک سخت حد یہ ہے: database data directory کو NFS (network file system) یا ایسے network mount پر نہ رکھیں جس کے locking اور fsync رویے کی آپ نے جانچ نہ کی ہو۔ Databases یہ فرض کرتی ہیں کہ کامیاب fsync کا مطلب ہے کہ bytes مستحکم storage پر لکھے جا چکے ہیں۔ جب یہ مفروضہ غلط ہو تو data corruption پیدا ہوتی ہے، جو کئی ہفتوں بعد ظاہر ہوتی ہے۔

Volume غائب ہونے سے پہلے اس کا نام مقرر کریں

Compose کسی volume کو <project>_<volume> نام دیتا ہے، اور project name بطور default directory name ہوتا ہے۔ اس لیے volume کی شناخت directory name پر منحصر ہوتی ہے، جسے لوگ اکثر سوچے بغیر تبدیل کر دیتے ہیں۔

/srv/app کو /srv/app-old میں منتقل کریں، یا compose file میں pgdata key کا نام تبدیل کریں، تو اگلا docker compose up -d ایک بالکل نیا خالی volume بناتا ہے۔ Postgres اس میں نیا cluster initialize کر دیتا ہے۔ container صحت مند ہوتا ہے، application شروع ہو جاتی ہے، لیکن تمام tables غائب ہوتی ہیں۔ پرانا volume اب بھی پرانے نام کے تحت disk پر موجود ہوتا ہے، اور یہی اچھی خبر ہے۔

docker volume ls
docker volume inspect app_pgdata

نام واضح طور پر مقرر کریں تاکہ ایسا دوبارہ نہ ہو۔ project name اور volume name کو صراحت کے ساتھ set کریں:

name: myapp

services:
  db:
    image: postgres:17
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
    name: myapp_pgdata

اگر کسی غیر متعلقہ نام والے volume میں پہلے سے آپ کا data موجود ہے تو database بند کر کے اسے copy کریں:

docker compose stop db
docker run --rm -v app_pgdata:/from -v myapp_pgdata:/to alpine sh -c 'cp -a /from/. /to/'
docker compose start db

database چلتے ہوئے copy کرنے سے ان files کی نامکمل copy بن سکتی ہے جن میں اس وقت لکھا جا رہا تھا۔ پہلے database بند کریں۔

ڈیٹا ڈائریکٹری کا مالک کون ہے

سرکاری Postgres، MySQL اور MongoDB images اپنا server عموماً غیر مراعات یافتہ user id، عام طور پر 999، کے طور پر چلاتی ہیں۔ جب container root کے طور پر شروع ہوتا ہے تو entrypoint ڈیٹا ڈائریکٹری کی ملکیت اس user کو منتقل کرتا ہے اور پھر مراعات کم کر دیتا ہے۔ اسی لیے خالی bind mount عموماً پہلی کوشش میں کام کر جاتا ہے۔

یہ اس وقت ناکام ہو جاتا ہے جب آپ compose file میں user: مقرر کرتے ہیں، کیونکہ اس کے بعد entrypoint کے پاس کسی چیز کی درستگی کے لیے درکار مراعات باقی نہیں رہتیں۔ Postgres یہ بات براہ راست بتاتا ہے:

initdb: error: could not change permissions of directory "/var/lib/postgresql/data": Operation not permitted

غلط mode کے ساتھ موجود ڈیٹا ڈائریکٹری مختلف پیغام دیتی ہے۔ اس پیغام کو پہچاننا ضروری ہے، کیونکہ اس کا حل chmod ہے، chown نہیں:

FATAL:  data directory "/var/lib/postgresql/data" has invalid permissions
DETAIL:  Permissions should be u=rwx (0700) or u=rwx,g=rx (0750).

root-owned bind mount پر MongoDB lock file کے مرحلے پر ناکام ہو جاتا ہے:

Unable to create/open the lock file: /data/db/mongod.lock (Permission denied). Ensure the user executing mongod is the owner of the lock file and has the appropriate permissions.

حل یہ ہے کہ host directory کی ملکیت کسی نام کے بجائے numeric id کو دیں:

sudo chown -R 999:999 /srv/appname/pg
sudo chmod 700 /srv/appname/pg
ls -ldn /srv/appname/pg

ls -ldn ناموں کے بجائے numbers دکھاتا ہے، اور اس میں 999 999 نظر آنا چاہیے۔ آپ کے host پر postgres نام کا account اور image کے اندر postgres نام کا account ایک دوسرے سے متعلق نہیں ہیں۔ kernel numbers کا موازنہ کرتا ہے، جبکہ نام ہر طرف الگ الگ تلاش کیے جاتے ہیں۔ PUID اور PGID host users کو container میں کیسے map کرتے ہیں اس mapping کی درست وضاحت کرتا ہے۔ rootless Docker یا user namespace remapping کے تحت numbers دوبارہ تبدیل ہو جاتے ہیں، اس لیے 999 فرض کرنے کے بجائے running container سے ids پڑھیں۔

Named volumes کے ساتھ پہلی بار یہ پورا مسئلہ ختم ہو جاتا ہے، کیونکہ Docker خالی directory بناتا ہے اور entrypoint اس کی ملکیت حاصل کر لیتا ہے۔

اپ گریڈز: package tag تبدیل کرنے کے مقابلے میں package upgrade

host پر، apt upgrade آپ کو minor version کے اندر آگے لے جاتا ہے۔ آپ کی distribution خود سے database major version تبدیل نہیں کرے گی۔ جب آپ یہ تبدیلی کرنا چاہیں تو دونوں versions کے binaries ایک ساتھ install کیے جا سکتے ہیں، اور pg_upgrade کو بالکل یہی درکار ہے۔

container میں tag ہی version ہوتا ہے، اس لیے upgrade کے لیے صرف ایک line تبدیل کرنی ہوتی ہے۔ اس سے minor upgrades آسان ہو جاتے ہیں، لیکن major upgrades کے لیے باقاعدہ طریقۂ کار درکار ہوتا ہے۔

postgres:16 کو postgres:17 میں تبدیل کریں، docker compose up -d چلائیں، اور container فوراً exit ہو جاتا ہے:

PostgreSQL Database directory appears to contain a database; Skipping initialization
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.

کسی چیز کو نقصان نہیں پہنچتا۔ نئے binaries پرانے on-disk catalog layout کو پڑھنے سے انکار کرتے ہیں، کیونکہ یہ layout major versions کے درمیان تبدیل ہوتا ہے۔ tag کو واپس postgres:16 پر رکھیں، اور یہ دوبارہ start ہو جائے گا۔ یہ rollback containers کا وہ حقیقی فائدہ ہے جو upgrade کے معاملے میں ملتا ہے۔

موزوں طریقہ dump اور restore ہے۔ PostgreSQL ترجیح دیتا ہے کہ dump نئے client سے لیا جائے۔ اس لیے compose network پر ابھی چل رہے پرانے server کے خلاف نئے image سے یہ command چلائیں:

docker run --rm --network myapp_default -e PGPASSWORD="$POSTGRES_PASSWORD" \
  postgres:17 pg_dumpall -h db -U postgres > /srv/backups/all.sql
ls -lh /srv/backups/all.sql
tail -n 2 /srv/backups/all.sql

فائل کم از کم دسیوں kilobytes کی ہونی چاہیے اور اس کے آخر میں PostgreSQL database cluster dump complete والی line ہونی چاہیے۔ چند سو bytes کی فائل کا مطلب ہے کہ dump ناکام ہو گیا ہے، اور آپ بلاوجہ volume حذف کرنے والے ہیں۔ صرف اس check کے بعد:

docker compose down
docker volume rm myapp_pgdata
# edit the compose file: image: postgres:17
docker compose up -d db
docker compose exec -T db psql -U postgres -f /dev/stdin < /srv/backups/all.sql

دیگر engines میں طریقہ مختلف ہے:

  • MySQL 8 startup کے وقت اپنی data dictionary upgrade کرتا ہے، اس لیے minor tag bump عموماً صرف restart ہوتا ہے۔ release series کے درمیان jump کرنے سے پہلے release notes پڑھیں، اور ہر صورت میں پہلے dump لیں۔
  • نئے version پر server start ہونے کے بعد MariaDB میں mariadb-upgrade چلانا ضروری ہوتا ہے۔
  • MongoDB کو ایک وقت میں صرف ایک major version upgrade کرنا چاہیے۔ ہر step کے بعد آگے بڑھنے سے پہلے feature compatibility version set کریں۔ کوئی version چھوڑنے پر mongod start ہونے سے انکار کرتا ہے اور UPGRADE PROBLEM line log کرتا ہے، جس میں featureCompatibilityVersion کا نام ہوتا ہے۔ MongoDB 7.0 سے، command کے لیے explicit confirmation flag درکار ہے: db.adminCommand({ setFeatureCompatibilityVersion: "8.0", confirm: true })۔
  • Redis پرانی snapshot files کو آسانی سے load کرتا ہے، لیکن نئی files کو نہیں۔ اس لیے upgrade ایک restart ہوتا ہے، جبکہ downgrade میں data load ہونے میں failure ہو سکتا ہے۔

عمومی اصول یہ ہے: container downgrade کو آسان بناتا ہے، لیکن upgrade کو مزید آسان نہیں بناتا۔

میرا database container code 137 کے ساتھ کیوں بند ہو جاتا ہے؟

کیونکہ kernel کے out of memory (OOM) killer نے اسے ختم کر دیا ہے۔ 137 دراصل 128 جمع signal 9 ہے۔

docker compose ps
docker inspect myapp-db-1 | grep -i oomkilled
journalctl -k | tail -n 20

docker compose ps میں Exited (137) دکھائی دیتا ہے، inspect لائن میں "OOMKilled": true لکھا ہے، اور kernel log میں اس سے مطابقت رکھنے والی entry موجود ہے:

Memory cgroup out of memory: Killed process 4711 (postgres) total-vm:2170416kB

یہ عمل اس طرح ہوتا ہے، اور اس سے اکثر لوگ حیران ہوتے ہیں۔ PostgreSQL اور MySQL اپنے buffers کا سائز host کی بتائی ہوئی کل memory کی بنیاد پر مقرر کرتے ہیں۔ cgroup limit اس تعداد کو ان کے لیے تبدیل نہیں کرتی۔ 16 GB host پر 2 GB limit ہونے کی صورت میں database ایسے منصوبہ بناتا ہے جیسے اسے 16 GB دستیاب ہو، اور cgroup اسے اس وقت ختم کر دیتا ہے جب host پر ابھی کوئی نمایاں memory pressure بھی نہ ہو۔ اس لیے صرف memory limit کافی نہیں ہے۔ آپ کو database کو یہ بھی بتانا ہوگا کہ اسے کتنی memory دستیاب ہے:

  • PostgreSQL: shared_buffers مقرر کریں، اور work_mem پر توجہ دیں۔ work_mem ہر sort operation اور ہر connection کے لیے الگ allocate ہوتا ہے، اس لیے پچاس connections کے ساتھ ایک بڑی قدر عموماً اس container کی وجہ بنتی ہے جو startup کے بجائے load کے دوران بند ہو جاتا ہے۔
  • MySQL اور MariaDB: innodb_buffer_pool_size مقرر کریں، جس کی default قدر 128M ہے۔ container میں innodb_dedicated_server کو بند رکھیں، کیونکہ اس کا پورا مقصد detected machine memory کی بنیاد پر خود کو سائز کرنا ہے۔
  • MongoDB: host memory سے اندازہ لگانے دینے کے بجائے WiredTiger cache size واضح طور پر مقرر کریں۔
  • Redis: maxmemory کی default قدر unlimited ہے، اس لیے Redis اس وقت تک بڑھتا رہتا ہے جب تک cgroup اسے روک نہیں دیتا۔ maxmemory کو container limit سے خاصا کم مقرر کریں اور ایک maxmemory-policy منتخب کریں۔

Postgres بھی اپنی جانب سے اس واقعے کی اطلاع دیتا ہے، اور log میں آپ کو یہ دو سطریں ملیں گی:

LOG:  server process (PID 123) was terminated by signal 9: Killed
LOG:  terminating any other active sessions due to crash of another server process

ایک backend کے ختم کیے جانے سے ہر دوسرے backend کو restart ہونا پڑتا ہے، کیونکہ shared memory اب inconsistent ہو سکتی ہے۔ یہ آپ کی application کے لیے connection storm ہے، کوئی خاموش واقعہ نہیں۔ Docker Compose میں memory limits مقرر کرنا syntax اور mem_limit اور deploy.resources form کے درمیان فرق بیان کرتا ہے۔

یہ سب host پر ختم نہیں ہوتا۔ صرف جگہ بدلتا ہے۔ cgroup کے بغیر database اس machine پر موجود ہر دوسری چیز کے ساتھ memory کے لیے مقابلہ کرتا ہے، اور host OOM killer score کی بنیاد پر کسی process کو متاثرہ منتخب کرتا ہے، جو sshd بھی ہو سکتا ہے۔ ایسی limit جو database کو قابل پیش گوئی انداز میں ختم کرے، اس host OOM سے بہتر ہے جو آپ کو system سے باہر کر دے۔

بیک اپس: اندرونی طور پر dump لیں، بیرونی طور پر بیک اپ کریں

چلتے ہوئے database کی data directory کو copy کر کے بیک اپ نہ بنائیں۔ server کے data لکھتے وقت لی گئی file-level copy نامکمل ہوتی ہے، اور اس کا پتا restore کے وقت چلتا ہے۔

دو قابلِ اعتماد طریقے ہیں: database کے اپنے tool سے اس کے چلتے ہوئے dump لیں اور dump کا بیک اپ بنائیں، یا container روک کر volume کو cold حالت میں copy کریں۔

docker compose exec -T db pg_dump -U postgres -Fc appdb > /srv/backups/appdb.dump
docker compose exec -T db mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" --single-transaction --all-databases > /srv/backups/mysql.sql
docker compose exec -T db mongodump --archive --gzip --db appdb > /srv/backups/appdb.archive.gz
docker compose exec -T redis redis-cli BGSAVE

-T اہم ہے۔ اس کے بغیر docker compose exec command کے ساتھ terminal attach کر سکتا ہے، اور terminal layer output stream میں carriage returns شامل کر دیتی ہے۔ پھر text dump restore کرتے وقت عجیب errors آتے ہیں، جبکہ binary dump مکمل طور پر corrupt ہو جاتا ہے۔ بیک اپ کے وقت یہ خاموشی سے fail ہوتا ہے اور ایک ماہ بعد واضح طور پر سامنے آتا ہے۔

--single-transaction پورے server کو lock کیے بغیر mysqldump کو InnoDB tables کا مستقل snapshot لینے دیتا ہے۔

یہ commands ہر ایک ایک file لکھتی ہیں۔ یہ بیک اپ system نہیں ہیں: ان میں retention، off-box copy یا verification شامل نہیں ہے۔ dump directory ایسے tool کے حوالے کریں جو یہ تینوں کام کرے۔ VPS سے restic بیک اپس اسی مقصد کے لیے ہے۔ /srv/backups کا بیک اپ بنائیں، /var/lib/docker/volumes کا نہیں۔

اس کے بعد restore چلائیں، کیونکہ جس بیک اپ کو آپ نے کبھی restore نہ کیا ہو وہ بیک اپ نہیں ہوتا:

docker compose exec -T db createdb -U postgres restore_test
docker compose exec -T db pg_restore -U postgres -d restore_test < /srv/backups/appdb.dump
docker compose exec -T db psql -U postgres -d restore_test -c '\dt'

\dt کو آپ کی application کے tables کی فہرست دکھانی چاہیے۔ خالی result یا Did not find any relations. کا مطلب ہے کہ dump وہ نہیں ہے جو آپ سمجھ رہے ہیں۔ کام مکمل ہونے پر restore_test کو حذف کریں۔

ہر چیز حذف کرنے والی کمانڈ

docker compose down -v۔

سادہ down containers اور network کو حذف کر دیتا ہے۔ -v اس compose file میں declare کیے گئے ہر named volume کے علاوہ ان containers کے ساتھ منسلک ہر anonymous volume بھی حذف کر دیتا ہے۔ اس میں کوئی prompt نہیں آتا اور undo کا کوئی طریقہ نہیں ہوتا۔ Self-hosted database کے تباہ ہونے کی یہ سب سے عام وجہ ہے۔ عموماً یہ کسی غیر متعلقہ مسئلے کی troubleshooting کے دوران ہوتا ہے، کیونکہ کسی forum کے جواب میں اسے چلانے کا کہا گیا ہوتا ہے۔

چار اقدامات نقصان کا دائرہ کم کرتے ہیں:

  • Database volume کو external: true کے طور پر declare کریں۔ Compose ایسا volume حذف نہیں کرے گا جس کا مالک وہ نہ ہو، اس لیے -v اس تک نہیں پہنچ سکتا۔ اسے ایک بار docker volume create myapp_pgdata کے ذریعے بنائیں۔
  • معمول کے restarts کے لیے docker compose stop اور docker compose start استعمال کریں۔ Compose میں down اور stop کا فرق واضح کرتا ہے کہ ہر کمانڈ کیا حذف کرتی ہے۔
  • Dumps کو ایسے host path پر رکھیں جو compose کے زیرِ انتظام ہر volume سے باہر ہو۔
  • Troubleshooting کے کسی جواب سے -v کو ایسے stack میں کبھی paste نہ کریں جس میں آپ کا اہم data موجود ہو۔

ڈیٹابیس port کو public نہ کریں

یہ line آپ کے database کو public internet پر expose کر دیتی ہے:

    ports:
      - "5432:5432"

یہ ہر interface پر bind کرتی ہے۔ Docker، firewall کے input rules تک packet پہنچنے سے پہلے ہی اس کی destination تبدیل کرکے port publish کرتا ہے۔ ufw کے rules input chain میں ہوتے ہیں، اس لیے ufw deny 5432 بالکل کچھ نہیں کرتا۔ Docker کے published ports، ufw کو bypass کیوں کرتے ہیں میں chain traversal دکھایا گیا ہے۔

اسی compose project کی application، compose network پر service name کے ذریعے database تک پہنچتی ہے، اس لیے اسے published port کی ضرورت نہیں ہوتی۔ یہ block حذف کریں۔ اگر host پر موجود client کو رسائی دینی ہو تو صرف loopback پر bind کریں:

    ports:
      - "127.0.0.1:5432:5432"

دیکھیں کہ حقیقت میں کون سا port listening کر رہا ہے:

sudo ss -ltnp | grep 5432

127.0.0.1:5432 مطلوبہ نتیجہ ہے۔ 0.0.0.0:5432 کا مطلب ہے کہ کوئی بھی آپ کا password آزما سکتا ہے۔

کہاں کیا چلائیں

ایک VPS پر ایک application۔ Container استعمال کریں۔ متعین نام والا named volume بنائیں، کوئی published port نہ رکھیں، database settings کے مطابق memory limit مقرر کریں، اور ہر رات host path پر dump بنائیں جسے restic جمع کر لے۔ VPS پر صاف Docker installation سے آغاز کریں اور پورا stack ایک ہی compose file میں رکھیں، جسے آپ commit کرتے ہیں۔ فائدہ واضح ہے: database version git میں review کے قابل ایک لائن بن جاتا ہے۔

ایسا host جس پر کئی services چل رہی ہوں۔ Containers استعمال کریں، اور ہر application کے لیے ایک database رکھیں؛ سب کے لیے ایک shared server نہ رکھیں۔ Shared server ہر application کو ایک ہی upgrade schedule سے باندھ دیتا ہے، اور ایک بے قابو query سب کی outage کا سبب بن سکتی ہے۔ ہر container کے لیے الگ memory limit مقرر کریں تاکہ خراب query صرف اسے لکھنے والی app تک محدود رہے۔ کئی چھوٹے Postgres instances میں disk کچھ زیادہ لگتی ہے، لیکن coordination بہت کم ہو جاتی ہے۔

Database ہی product ہو۔ اسے vendor کے package repository سے host پر چلائیں، یا managed service کے لیے ادائیگی کریں۔ pg_upgrade کے لیے binaries کے دونوں major versions بیک وقت installed ہونے چاہییں۔ Packages یہ سہولت دیتے ہیں، جبکہ single-version image نہیں دیتی۔ WAL (write ahead log) archiving کے ساتھ replication اور point in time recovery دونوں اس وقت آسان ہوتے ہیں جب database خود machine اور اس کی disks کو manage کر رہا ہو۔ ایسے system کے لیے سادہ اور قابل اعتماد راستہ منتخب کریں جو 03:00 بجے آپ کو page کرے گا۔

Application چھوٹی ہو۔ غور کریں کہ server database بالکل نہ چلایا جائے۔ ایک VPS پر single-writer web application کے لیے اکثر VPS پر production میں SQLite زیادہ موزوں ہوتی ہے، کیونکہ backup ایک file ہوتا ہے اور upgrade path ایک library version ہوتا ہے۔

FAQ

کیا Docker میں production database چلانا محفوظ ہے؟

ہاں، single-server application stack کے لیے۔ Container ایک Linux process ہوتا ہے جس کے گرد namespaces اور cgroups ہوتے ہیں۔ Volume mount ہونے کی صورت میں database اسی host filesystem میں لکھتا ہے جسے package سے install کرنے کی صورت میں استعمال کرتا۔ خطرات رفتار سے متعلق نہیں بلکہ operational ہوتے ہیں: ایسا volume جس کا نام pin نہ کیا گیا ہو، غلط user id کی ملکیت والا bind mount، ایسا restore جسے آپ نے کبھی test نہ کیا ہو، اور docker compose down -v۔ ان چاروں مسائل کو حل کر دیں تو container مناسب ہے۔ جب database بنیادی workload ہو اور آپ کو pg_upgrade، replication یا point-in-time recovery درکار ہو تو host install استعمال کریں۔

Database data کے لیے bind mount یا named volume استعمال کرنا چاہیے؟

جب تک host path معلوم رکھنے کی کوئی مخصوص وجہ نہ ہو، named volume استعمال کریں۔ Docker directory بناتا ہے اور image entrypoint پہلی بار start ہونے پر ownership مقرر کرتا ہے، اس لیے permission کا مسئلہ پیدا نہیں ہوتا۔ Volume کو واضح name: کے ساتھ pin کریں یا اسے external: true قرار دیں۔ بصورت دیگر project directory کا نام بدلنے سے خاموشی سے نیا خالی volume بنے گا اور database خالی ہو جائے گا۔ Bind mount بھی مناسب ہے، بشرطیکہ host directory کو اس numeric user id کی ملکیت دیں جس کے تحت image چلتی ہے۔ Official Postgres، MySQL اور MongoDB images کے لیے یہ id 999 ہے۔ اس کی تصدیق ls -ldn سے کریں، کیونکہ ls -l آپ کے host پر اس number کا نام دکھاتا ہے، اور وہ نام container کے اندر بے معنی ہے۔

docker compose down -v کیا delete کرتا ہے؟

یہ سادہ down کی طرح containers اور network کو remove کرتا ہے۔ اس کے علاوہ -v اس compose file میں declared ہر named volume اور ان containers کے ساتھ attached ہر anonymous volume بھی remove کرتا ہے۔ اس میں database بھی شامل ہے۔ کوئی confirmation prompt نہیں آتا اور recovery ممکن نہیں ہوتی۔ external: true سے marked volumes remove نہیں کیے جاتے۔ Database volume کو external mark کرنے کی بنیادی وجہ یہی ہے۔ معمول کے restart کے لیے docker compose stop اور docker compose start استعمال کریں۔

Docker میں PostgreSQL کو نئے major version پر کیسے upgrade کروں؟

Dump اور restore کریں۔ postgres:16 کو postgres:17 میں تبدیل کر کے restart کرنے سے FATAL: database files are incompatible with server آتا ہے، جس میں DETAIL line دونوں versions کے نام دیتی ہے، کیونکہ نئے binaries پرانے catalog layout کو نہیں پڑھ سکتے۔ کوئی data خراب نہیں ہوتا۔ پرانا tag واپس رکھیں اور یہ دوبارہ start ہو جائے گا۔ چلتے ہوئے پرانے container کے خلاف نئے version کے client سے pg_dumpall لیں۔ تصدیق کریں کہ file PostgreSQL database cluster dump complete پر ختم ہوتی ہے۔ پھر خالی volume پر نیا tag start کریں اور dump load کریں۔ ایک ہی major version کے اندر minor upgrades کے لیے صرف pull اور restart کافی ہے۔

میرا database container code 137 کے ساتھ کیوں exit ہو جاتا ہے؟

137، 128 جمع signal 9 کے برابر ہے۔ اس کا مطلب ہے کہ کسی چیز نے process کو فوراً ختم کر دیا۔ docker inspect <container> | grep -i oomkilled چلائیں۔ true کی value کا مطلب ہے کہ container اپنی cgroup memory limit تک پہنچ گیا۔ عام وجہ یہ ہے کہ PostgreSQL اور MySQL host کی total memory پڑھتے ہیں اور container کی limit نہیں دیکھتے۔ اس لیے وہ 16 GB کے لیے منصوبہ بناتے ہیں، جبکہ ان کے پاس 2 GB ہوتے ہیں۔ shared_buffers اور work_mem، یا innodb_buffer_pool_size مقرر کریں، تاکہ وہ container کو دی گئی limit کے مطابق ہوں۔ متعلقہ Memory cgroup out of memory line کے لیے journalctl -k دیکھیں، تاکہ تصدیق ہو سکے کہ kernel نے کون سا process ختم کیا۔