SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-10-26

طريقة نسخ Immich احتياطياً واستعادته على VPS

تعرّف إلى محتويات نسخة Immich الصحيحة، ولماذا لا تكفي نسخة Postgres الحية، وكيف تتجنب استعادة تترك الخط الزمني فارغاً رغم امتلاء القرص.

ما الذي يجب أن يتضمنه النسخ الاحتياطي لـImmich

يتكوّن النسخ الاحتياطي لـImmich من ثلاثة عناصر تُلتقط في اللحظة نفسها. النسخ الأصلية الموجودة ضمن UPLOAD_LOCATION. وتفريغ SQL لقاعدة بيانات Postgres. و.env وdocker-compose.yml اللذان يصفان المكدس. تعني الاستعادة إعادة تشغيل ذلك التفريغ في قاعدة بيانات جديدة، مع إيقاف خادم Immich، ثم تشغيل بقية المكدس بعد ذلك فقط. إذا أخطأت في الترتيب، فستحصل على Immich يعمل ويعرض مخططاً زمنياً فارغاً فوق قرص ممتلئ.

يهم هذا الفصل لأن Immich يحتفظ بحالته في موضعين لا يعرف أحدهما شيئاً عن الآخر. يحتفظ Postgres بكل ألبوم، وكل مجموعة وجوه، وكل رابط مشترك، وكل حساب مستخدم ومفتاح API، والمسار المخزّن لكل عنصر. أما نظام الملفات فيحتفظ بالبكسلات. إذا استعدت الملفات من دون قاعدة البيانات، فلن يعرض Immich شيئاً. وإذا استعدت قاعدة البيانات من دون الملفات، فسيفتح كل عنصر كصورة معطّلة.

كُتبت الأوامر هنا لـImmich v3.1.0، وهو الإصدار الحالي في أوائل أغسطس 2026. اعتباراً من 2026-10-25، الإصدار الحالي هو v3.3.5، وهو تصحيح أمني نُشر في 2026-10-21، ويتطابق ملف compose الخاص به، وصورة قاعدة البيانات، وخطوات النسخ الاحتياطي والاستعادة الموثّقة مع ما تستخدمه هذه الصفحة. يصدّر المشروع إصدارات بسرعة، وقد تغيّر إجراء النسخ الاحتياطي الموثّق أكثر من مرة، لذلك تحقّق من الإصدار الذي تشغّله فعلياً قبل نسخ أي شيء. إذا لم يكن المكدس يعمل بعد، فابدأ بـدليل تثبيت Immich ثم عُد إلى هنا.

اعرف إلى ماذا تشير مساراتك

يحدّد متغيران في .env كل شيء في هذه الصفحة. يشير UPLOAD_LOCATION إلى الدليل الأب الذي يكتب Immich جميع الوسائط داخله. ويشير DB_DATA_LOCATION إلى دليل بيانات Postgres.

يعيّن example.env الافتراضي UPLOAD_LOCATION=./library، وهو إعداد افتراضي مربك، لأن Immich ينشئ بعد ذلك مجلداً باسم library داخله. ستجد ملفاتك الأصلية في ./library/library. عيّن مساراً مطلقاً بدلاً من ذلك، حتى لا يعتمد أي script للنسخ الاحتياطي على الدليل الذي شغّلته منه.

UPLOAD_LOCATION=/srv/immich/data
DB_DATA_LOCATION=/srv/immich/postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
IMMICH_VERSION=v3.1.0

ينشئ Immich داخل UPLOAD_LOCATION عدة مجلدات. تحتوي ثلاثة منها على بيانات لا يمكن لأي job إعادة بنائها:

  • library: الملفات الأصلية مرتبة وفق قالب التخزين لديك
  • upload: الملفات الأصلية التي لم تُنقل بعد إلى تخطيط القالب، بالإضافة إلى عمليات الرفع الجارية
  • profile: صور ملفات المستخدمين الشخصية

إذا فقدت library، فستفقد الصورة. لا يحتفظ Immich بأي نسخة ثانية من الملف الأصلي في أي مكان.

لماذا لا يُعد نسخ دليل بيانات Postgres نسخة احتياطية

يبدو DB_DATA_LOCATION هدفاً سهلاً. فهو دليل، وسينسخه rsync، وينتهي النسخ من دون خطأ. إذا نسخته أثناء تشغيل Postgres، فلن يكون النسخ نسخة احتياطية. وإذا نسخته بعد إيقاف Postgres، فسيكون نسخة احتياطية، لكنه سيكون هشاً. إليك السببين، ويمكنك ملاحظة فشل كل منهما.

السبب الأول هو عدم الاتساق. يكتب Postgres كل تغيير أولاً في سجل الكتابة المسبقة (WAL)، ثم يطبّقه على ملفات الجداول لاحقاً عند نقطة التحقق. لذلك تكون الملفات الموجودة على القرص، في أي لحظة، في منتصف عملية التحديث، بينما يقرأ النسخ المتتابع الذي يستغرق أربع دقائق الملف الأول عند 02:00 والملف الأخير عند 02:04. لا ينتمي هذان الملفان إلى المعاملة نفسها. عند تشغيل Postgres على الناتج، إما أن يرفض البدء مع PANIC: could not locate a valid checkpoint record، أو يبدأ ثم يتوقف عند أول قراءة لصفحة تالفة مع invalid page in block 1234 of relation base/16384/.... ولا يمكن استعادة أي من الحالتين من ذلك النسخ.

السبب الثاني يتعلق بالنسخ الذي تنشئه بعد إيقاف كل شيء. توثّق PostgreSQL أن النسخ الكامل لدليل بيانات متوقف يُعد نسخة احتياطية صالحة، لكن ذلك الدليل لا يُفتح إلا باستخدام ملفات تنفيذية تشترك معه في تنسيق التخزين. يثبّت Immich صورة قاعدة البيانات بواسطة digest، وهي حالياً ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0. وهي Postgres 14 مع امتدادات VectorChord وpgvector وpgvecto.rs للبحث المتجهي مضمّنة. يعمل Immich نفسه الآن على VectorChord؛ ويبقى pgvecto.rs في تلك الصورة فقط لكي تتمكن التثبيتات الأقدم من الترحيل منه ولكي تظل النسخ الاحتياطية القديمة قابلة للاستعادة. يمكن لإصدار فرعي لاحق من Postgres 14 فتح الدليل من دون تغيير، لكن لا يمكن لإصدار رئيسي مختلف من Postgres فتحه. ويجب أيضاً أن تكون الامتدادات التي تعتمد عليها جداولك موجودة وبإصدارات متوافقة. يجب أن يشغّل خادم الاستعادة صورة متوافقة. أما تفريغ SQL فلا يتأثر بذلك: فهو نص، ويعيد أي خادم متوافق تشغيله.

يتجاوز pg_dump مشكلة عدم الاتساق مباشرة. فهو يقرأ قاعدة البيانات بأكملها ضمن لقطة MVCC واحدة (التحكم في التزامن متعدد الإصدارات)، ولذلك يرى قاعدة البيانات كما كانت تماماً في لحظة واحدة، بينما تستمر عمليات الكتابة الأخرى حوله. لهذا لا تحتاج إلى إيقاف Postgres لإنشاء التفريغ.

ما يمكنك استبعاده من النسخة الاحتياطية

تُعاد هذه العناصر تلقائياً، لذلك يمكنك تخطيها:

  • thumbs: صور المعاينة والصور المصغّرة
  • encoded-video: الفيديوهات المحوّلة
  • DB_DATA_LOCATION: يُعاد بناؤه من ملف التفريغ
  • وحدة تخزين Docker ‏model-cache: نماذج تعلّم الآلة، ويُعاد تنزيلها عند الطلب

تخطّي هذه العناصر مقايضة، وليس مكسباً مجانياً. تستغرق إعادة بناء الصور المصغّرة وعمليات تحويل الفيديو لمكتبة كبيرة ساعات من وقت CPU على VPS صغير، ويعرض الخط الزمني عناصر نائبة رمادية طوال ذلك الوقت. تعيد تشغيل هذه العمليات من Administration > Jobs، مع ضبط "Generate Thumbnails" و"Transcode Videos" للعمل على الأصول المفقودة. إذا كانت مساحة وجهة النسخة الاحتياطية تكفي، فأدرج هذه العناصر وتجنّب الانتظار. وإذا كنت قريباً من حد التخزين، فاستبعدها وخطّط لإعادة بنائها. يوضّح تقدير حجم مكتبة Immich مدى نمو هذه المجلدات مقارنةً بالأصول الأصلية.

هناك مجلد آخر يجدر بك معرفته. يحتوي UPLOAD_LOCATION/backups على ملفات تفريغ قاعدة البيانات التلقائية الخاصة بـ Immich، وتُكتب يومياً عند 02:00 مع الاحتفاظ بآخر 14 ملفاً. ويمكن ضبط ذلك ضمن Administration > Settings > Database Dump Settings. لا تستهلك هذه الملفات مساحة إضافية عليك، وهي مفيدة فعلاً. لكنها توجد أيضاً على القرص نفسه الذي توجد عليه المكتبة التي تحميها، لذلك تساعد في حالات فشل الترحيل، ولا تساعد عند تعطل الخادم. أنشئ ملف تفريغ خاصاً بك على أي حال، لأن ملف التفريغ الذي تبدأه بنفسك يُنشأ في اللحظة نفسها التي تُلتقط فيها لقطة الملفات المرتبطة به.

استخراج نسخة قاعدة البيانات

docker exec -t immich_postgres pg_dump --clean --if-exists \
  --dbname=immich --username=postgres \
  | gzip > /srv/immich/backup/immich.sql.gz

استبدل immich وpostgres بالقيمتين DB_DATABASE_NAME وDB_USERNAME إذا كنت قد غيّرتهما. يضع --clean --if-exists قيمة DROP ... IF EXISTS قبل كل CREATE، لذلك تُعاد النسخة إلى قاعدة بيانات تحتوي مسبقاً على الكائنات بدلاً من التوقف عند أول كائن.

والآن، إليك التفصيل الذي يفسد نصوص النسخ الاحتياطي بصمت. هذا الأمر عبارة عن pipeline، وتعرض shell حالة الخروج للأمر الأخير في pipeline. إذا فشل pg_dump بسبب كلمة مرور خاطئة أو لأن الحاوية لا تعمل، يتلقى gzip دفقاً فارغاً، ويكتب ملف gzip صالحاً تماماً، ثم ينتهي بالحالة 0. يسجل النص نجاح العملية، وتصبح لديك نسخة احتياطية حجمها 20 بايت. ضع pipefail في بداية كل نص نسخ احتياطي:

#!/usr/bin/env bash
set -euo pipefail

ثم تحقّق من النتيجة بدلاً من الوثوق بحالة الخروج:

ls -lh /srv/immich/backup/immich.sql.gz
gunzip -c /srv/immich/backup/immich.sql.gz | head -n 3

يكون السطر الأول من النسخة السليمة هو -- PostgreSQL database dump. أما الملف الذي لا يتجاوز حجمه بضع مئات من البايتات فهو نسخة فاشلة، مهما قال النص.

سجّل الإصدار الذي أنشأ النسخة بجانبها، وذلك بالاستعلام من الخادم قيد التشغيل:

curl -fsS http://localhost:2283/api/server/version > /srv/immich/backup/immich-version.txt

لا تتطلب نقطة النهاية هذه تسجيل الدخول، وتعيد الإصدار قيد التشغيل بأرقام الإصدار الرئيسي والثانوي وإصدار التصحيح. لذلك تعني 3 و1 و0 الإصدار v3.1.0. لا تعتمد على .env لهذا الغرض، ولا على docker inspect --format '{{.Config.Image}}' immich_server، لأنه يعرض فقط مرجع الصورة الذي ضبطته. يضبط الملف الافتراضي IMMICH_VERSION=v3، وهي وسم متغير يتبع كل إصدارات 3.x، لذلك لا يوضح سجل ينتهي بـ :v3 أي إصدار مبنى أنشأ النسخة فعلياً. ثبّت الوسم الدقيق في .env أيضاً.

أوقف الخادم، ثم أنشئ لقطة باستخدام restic

الملفات الموجودة تحت UPLOAD_LOCATION ليست ثابتة أثناء تشغيل Immich. يكتب الخادم تحميلات جديدة، وتنقل مهمة قالب التخزين الملفات بين المجلدات. إذا قرأت أداة النسخ الاحتياطي ملفاً أثناء الكتابة، فستحفظ وحدات البايت التي قرأتها كما لو كانت الملف كاملاً، ولن يُبلّغ أي مكوّن عن خطأ. أوقف حاوية الخادم طوال مدة العملية:

docker stop immich_server

اترك immich_postgres قيد التشغيل، لأن التفريغ يحتاج إليه. ستظل واجهة الويب والتطبيق المحمول غير متاحين إلى أن تبدأ الخادم مجدداً. وهذا يكون مقبولاً عادةً في مثيل منزلي عند الساعة 03:00.

يناسب restic هذه الحالة لأنه يزيل التكرار ويشفّر البيانات قبل مغادرتها الخادم. وجّهه إلى مستودع لا يوجد على هذا الخادم:

export RESTIC_REPOSITORY=sftp:backup@backup.example.com:/srv/restic/immich
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init

ويعمل تخزين الكائنات بالطريقة نفسها. وهو الخيار الأفضل إذا أردت أن تكون النسخة خارج أجهزتك بالكامل:

export RESTIC_REPOSITORY=s3:https://s3.example.com/immich-backup
export AWS_ACCESS_KEY_ID=your-access-key
export AWS_SECRET_ACCESS_KEY=your-secret-key
restic init

يمكن أن تكون نقطة النهاية هذه حاوية MinIO تديرها بنفسك على جهاز ثانٍ، أو أي موفّر متوافق مع S3. يحميك وجود المستودع على القرص نفسه الذي توجد عليه المكتبة من الحذف غير المقصود، ولا يحميك من أي شيء آخر.

ثم أنشئ اللقطة، مع إدراج ما يهم فقط:

restic backup \
  /srv/immich/backup/immich.sql.gz \
  /srv/immich/backup/immich-version.txt \
  /srv/immich/data/library \
  /srv/immich/data/upload \
  /srv/immich/data/profile \
  /srv/immich/.env \
  /srv/immich/docker-compose.yml
docker start immich_server

يقرأ restic الشجرة بأكملها في كل تشغيل، لكنه لا يرفع إلا الكتل التي لم يرها من قبل. لذلك تنقل اللقطة الأولى مكتبتك بالكامل، بينما تنقل كل لقطة لاحقة الصور الجديدة لذلك اليوم.

الاحتفاظ، والمفاتيح التي يجب حفظها في مكان آخر

restic forget --prune --keep-daily 7 --keep-weekly 5 --keep-monthly 12

يحذف forget اللقطات من الفهرس. أما --prune فهو الجزء الذي يحذف البيانات التي كانت تلك اللقطات آخر مرجع إليها. شغّل forget من دون --prune، ولن تنخفض فاتورة التخزين أبداً.

فحوصات البنية منخفضة التكلفة، لذلك شغّل أحدها أسبوعياً:

restic check

يتحقق ذلك من اتساق البيانات الوصفية للمستودع. لكنه لا يقرأ بياناتك. مرة كل شهر، أعد قراءة عينة وقارنها بالتجزئات المسجلة لها:

restic check --read-data-subset=5%

هذا هو الفحص الوحيد الذي يكتشف التلف الصامت في الواجهة الخلفية للتخزين، لأنه ينزّل كتل بيانات فعلية ويعيد حساب مجاميعها الاختبارية. إن إجراء --read-data كاملاً على مكتبة صور يعني تنزيل المستودع بأكمله. في التخزين الكائني المحسوب وفق حجم الاستخدام، يترتب على ذلك تكلفة فعلية. لذلك فإن استخدام مجموعة فرعية متغيرة هو الأسلوب العملي الذي يطبقه الناس فعلياً.

نصل الآن إلى الجزء الذي يتجاهله الناس. لا يمكن استرداد كلمة مرور مستودع restic. لا توجد آلية لإعادة تعيينها، ولا يمكن حل المشكلة عبر طلب دعم. إذا كانت النسخة الوحيدة منها موجودة في /root/.restic-password على الخادم الذي تحاول الاستعادة منه، فستكون نسخك الاحتياطية مجرد بيانات مشفّرة غير قابلة للاستخدام. وينطبق الأمر نفسه على مفتاح الوصول إلى التخزين الكائني وعلى DB_PASSWORD من .env. احتفظ بها كلها في مكان لا يعتمد على بقاء هذا الجهاز قيد التشغيل: اطبعها وضعها في درج، أو خزّنها في مدير كلمات مرور يعمل على أجهزة مختلفة. إذا كان مدير كلمات المرور مستضافاً ذاتياً أيضاً، فيجب التعامل معه بالطريقة نفسها، كما أن نسخ Vaultwarden احتياطياً مهمة مستقلة.

استعادة Immich بالترتيب الصحيح

ترتيب الاستعادة هو ما يحوّل النسخ الاحتياطية السليمة إلى جداول زمنية فارغة. اتبع هذا التسلسل على المضيف الجديد.

استعد الإعدادات أولاً. فهي تحدد الإصدار الذي يجب تشغيله، والمسارات التي تشير إليها.

restic restore latest --target /restore \
  --include /srv/immich/.env \
  --include /srv/immich/docker-compose.yml \
  --include /srv/immich/backup

ثبّت الإصدار قبل بدء أي شيء. اقرأ immich-version.txt، واضبط IMMICH_VERSION في .env على ذلك الإصدار بالضبط (v3.1.0 للإصدار الرئيسي 3، والثانوي 1، والتصحيحي 0)، واترك أحدث إصدار جانباً في الوقت الحالي. لا يدعم Immich الرجوع إلى إصدار أقدم، حتى بين الإصدارات التصحيحية، لذلك إذا بدأ خادم أحدث باستخدام dump أقدم ونفّذ عمليات الترحيل، فلن توجد طريقة للعودة.

استعد ملفات الوسائط.

restic restore latest --target /restore --include /srv/immich/data

بعد ذلك انقل library وupload وprofile بحيث تكون مباشرة داخل المسار الذي يشير إليه UPLOAD_LOCATION على هذا المضيف. يمكن أن يتغير مسار المضيف نفسه، لأن ملف compose يربط ذلك الدليل بمسار ثابت داخل الحاوية. أما البنية الداخلية للدليل فلا يجوز أن تتغير.

ابدأ قاعدة البيانات وحدها. اترك DB_DATA_LOCATION فارغاً لكي يهيّئ Postgres عنقوداً جديداً.

cd /srv/immich
docker compose pull
docker compose create
docker start immich_postgres
docker exec immich_postgres pg_isready --username=postgres

يطبع pg_isready القيمة accepting connections بعد اكتمال الإعداد الأولي، ويستغرق ذلك بضع ثوانٍ. ينشئ docker compose create جميع الحاويات من دون تشغيلها، وهذا هو الهدف من هذه الخطوة تحديداً: يجب ألا يعمل خادم Immich بعد. فعندما يبدأ الخادم باستخدام قاعدة بيانات فارغة، ينفّذ عمليات الترحيل، وينشئ مخططاً جديداً، ويطلب منك إنشاء حساب مسؤول جديد. عندها ستعيد استيراد dump أسفل تطبيق قيد التشغيل.

أعد تطبيق dump.

gunzip --stdout /restore/srv/immich/backup/immich.sql.gz \
  | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
  | docker exec -i immich_postgres psql --dbname=immich --username=postgres \
      --single-transaction --set ON_ERROR_STOP=on

هناك جزآن ينفذان العمل الفعلي هنا. يوجد sed لأن pg_dump يكتب search_path فارغاً في مخرجاته كتدبير أمان، حتى لا تُحلّ الأسماء غير المؤهلة في dump إلى مخطط غير متوقع. توجد أنواع البحث المتجهي في Immich داخل public، لذلك عند فراغ مسار البحث يصل الاسترداد إلى أول عمود مُعرّف بنوع متجهي، ثم يتوقف psql عند ERROR: type "vector" does not exist. تؤدي إعادة public إلى مسار البحث إلى حل المشكلة.

يضع --single-transaction --set ON_ERROR_STOP=on عملية الاستعادة كاملة داخل معاملة واحدة تتوقف عند أول خطأ. وستحصل إما على قاعدة بيانات مكتملة أو على قاعدة بيانات لم تتغير. ومن دونه، يؤدي الفشل في منتصف العملية إلى قاعدة بيانات تبدأ وتقبل تسجيل دخولك، لكنها تفتقد عدداً غير معروف من الألبومات، ولن تكتشف ذلك إلا بعد أسابيع.

ابدأ كل شيء الآن.

docker compose up -d
docker compose ps
docker logs -f immich_server

انتظر سطر بدء تشغيل مثل Immich Server is listening on، ثم افتح المنفذ 2283 وسجّل الدخول باستخدام بيانات اعتمادك القديمة، لأن حسابات المستخدمين عادت مع dump. إذا عرضت صفحة تسجيل الدخول بدلاً من ذلك خيار إنشاء حساب المسؤول الأول، فهذا يعني أن قاعدة البيانات لم تُستعد. أوقف العملية واقرأ مخرجات psql مرة أخرى.

تحذير واحد بشأن تعليمات الاستعادة الرسمية من سطر الأوامر، التي تبدأ بـ docker compose down -v. يحذف -v وحدات التخزين المُسمّاة. في ملف compose القياسي، يكون UPLOAD_LOCATION وDB_DATA_LOCATION عبارة عن عمليات ربط mount، ولذلك تبقى بعد تنفيذ الأمر. إذا غيّرت أياً منهما إلى وحدة تخزين مُسمّاة، فسيحذف ذلك الأمر صورك. اقرأ ملف compose قبل كتابة الأمر.

لماذا يكون الخط الزمني فارغاً بعد الاستعادة

يُنشأ الخط الزمني من صفوف قاعدة البيانات. لا يفحص Immich المسار upload/ عند الإقلاع للعثور على الصور من جديد، لأن الملف الذي لا يملك صفاً لا يملك مالكاً أو تاريخاً أو ألبوماً. لذلك تكون الاستعادة غير الصحيحة الأكثر شيوعاً هي استعادة الملفات مع فقدان قاعدة البيانات. يبدأ Immich، وينشئ مخططاً فارغاً، ويمنحك نسخة تعمل ولا تحتوي على أي بيانات، بينما القرص ممتلئ بصورك. لا تكون البيانات مفقودة. لكنها لا تكون مرئية أيضاً. الحل هو إعادة تشغيل ملف التفريغ مع إيقاف الخادم، تماماً كما سبق.

الإصدار الثاني من المشكلة أقل وضوحاً. تُستعاد قاعدة البيانات، ويمتلئ الخط الزمني بالإدخالات، لكن يفشل فتح كل عنصر. هذا يعني أن الصفوف تشير إلى ملفات لا يستطيع الحاوي رؤيتها، ويحدث ذلك عادةً لأن library وupload وprofile أصبحت أعمق بمستوى واحد بعد تنفيذ restic restore --target /restore دون نقلها إلى المكان الصحيح. تحقّق من داخل الحاوي بدلاً من التخمين:

docker exec immich_server ls /data

يُثبّت ملف compose القياسي UPLOAD_LOCATION في المسار /data، لذلك يجب أن يعرض ذلك الأمر library وupload وprofile. إذا عرض دليلاً فارغاً أو مجلداً زائداً باسم srv، فإن bind mount يشير إلى المستوى الخطأ، بينما تكون الصفوف سليمة.

تطابق الإصدار بين النسخ الاحتياطي والاستعادة

تصدر إصدارات Immich كثيراً، ويتغير المخطط معها، لذلك يحمل التفريغ مخطط الخادم الذي أنشأه.

تعمل عادةً استعادة تفريغ أقدم إلى خادم أحدث، لأن الخادم يطبّق عمليات الترحيل المعلّقة عند البدء ويدفع المخطط إلى الأمام. ما زالت وثائق النسخ الاحتياطي الخاصة بـImmich توصي بالاستعادة إلى إصدار متوافق متى أمكن، لأن عدم تطابق الإصدارات يعني تشغيل عمليات الترحيل أثناء الاستعادة. يحصر المشروع التغييرات غير المتوافقة في الإصدارات الرئيسية ويوثّقها في ملاحظات الإصدار، لذلك يجب أولاً قراءة التغييرات عند استعادة نسخة تعبر إصداراً رئيسياً.

أما استعادة تفريغ أحدث إلى خادم أقدم فلا تعمل مطلقاً. يحتوي التفريغ على جداول وأعمدة لا يعرفها الرمز الأقدم، ويذكر Immich أن الرجوع إلى إصدار أقدم غير مدعوم حتى بين الإصدارات التصحيحية. ولا يوجد أمر تراجع يمكنك استخدامه.

لذلك تكون الاستعادة الآمنة روتينية. شغّل الإصدار المطابق تماماً للإصدار الذي أنشأ التفريغ، وأعد تشغيله، وسجّل الدخول، وتحقق من اكتمال المخطط الزمني، ثم نفّذ الترقية فقط بعد ذلك. نفّذ الترقية بالطريقة الموضّحة في صفحة الترقية الرسمية: اقرأ ملاحظات الإصدار بحثاً عن التغييرات غير المتوافقة، واضبط IMMICH_VERSION في .env على الإصدار المطلوب، ثم شغّل docker compose pull && docker compose up -d. ويساعد الاحتفاظ بتفريغات لمدة أسبوع هنا أيضاً: إذا اتضح أن أحدث تفريغ أُنشئ أثناء ترقية فاشلة، فسيظل تفريغ الأمس موجوداً في المستودع.

تحقّق من النسخة الاحتياطية كل شهر

النسخة الاحتياطية التي لم تستعدها قط ليست سوى تخمين. مرة واحدة كل شهر، استعدها في مثيل مؤقت وتحقّق من صورة. تستغرق العملية نحو عشرين دقيقة، وهي الشيء الوحيد الذي يحوّل بقية هذه الصفحة إلى خطة استرداد.

restic snapshots
restic stats latest

يجب أن يعرض snapshots عملية التشغيل التي نُفِّذت الليلة الماضية. ويجب أن يعرض stats latest حجماً قريباً من حجم مكتبتك، لا بضعة ميغابايت.

استعد النسخة في دليل مؤقت، ويفضّل أن يكون ذلك على مضيف احتياطي:

restic restore latest --target /tmp/immich-drill

انسخ docker-compose.yml و.env من المجموعة المستعادة، ثم غيّر ثلاثة أشياء في النسخة. وجّه UPLOAD_LOCATION وDB_DATA_LOCATION إلى دليلين ضمن /tmp/immich-drill. انشر منفذ الويب في مكان آخر، باستخدام 12283:2283 بدلاً من 2283:2283. احذف أسطر container_name:، لأن ملف compose الأساسي يثبّت أسماء مثل immich_server، ما يسبب تعارضاً بين مكدسين على المضيف نفسه، ويرفض Docker إنشاء المكدس الثاني.

نفّذ تسلسل الاستعادة المذكور أعلاه: قاعدة البيانات فقط، ثم أعد تشغيل ملف التفريغ، ثم docker compose up -d. بعد ذلك، نفّذ الفحوصات الأربعة التي تثبت نجاح العملية.

  1. سجّل الدخول باستخدام كلمة المرور التي كنت تستخدمها قبل الاختبار. يعني عمل الحسابات أن ملف التفريغ استُعيد بنجاح.
  2. افتح المخطط الزمني وانتقل إلى أقدم شهر. يعني وجود الأصول عبر النطاق الزمني الكامل استعادة جميع الصفوف، لا الصفوف الحديثة فقط.
  3. افتح صورة واحدة بالحجم الكامل ونزّل النسخة الأصلية.
  4. قارنها بالملف نفسه في مكتبتك الفعلية باستخدام sha256sum. تعني مطابقة التجزئات بقاء وحدات البايت بعد مرورها ذهاباً وإياباً عبر restic.

بعد ذلك، أوقف بيئة الاختبار واحذفها باستخدام docker compose down -v في دليل الاختبار، ثم احذف /tmp/immich-drill. دوّن التاريخ في مكان ستراه، لأن قيمة هذه العملية تعتمد بالكامل على تكرارها في الشهر المقبل. إذا كنت لا تزال تختار خادم الصور الذي ستعتمده، فتعرض مقارنة PhotoPrism وImmich أوجه اختلافهما تحديداً في هذه النقطة.

FAQ

هل يجب أن أوقف Immich لإجراء نسخة احتياطية منه؟

أوقف immich_server واترك immich_postgres قيد التشغيل. لا تحتاج قاعدة البيانات إلى الإيقاف المؤقت، لأن pg_dump يقرأ داخل لقطة MVCC واحدة ويرى لحظة متسقة واحدة، بصرف النظر عما تكتبه العمليات الأخرى. الملفات هي سبب الإيقاف: يكتب الخادم التحميلات الجديدة، وتنقل مهمة قالب التخزين الملفات بين الأدلة. لذلك قد تقرأ أداة النسخ الاحتياطي ملفاً أثناء الكتابة إليه وتحفظ نسخة مبتورة منه من دون أي خطأ. تؤدي docker stop immich_server قبل أخذ اللقطة وdocker start immich_server بعدها إلى إزالة هذا التداخل.

هل يمكنني نسخ مجلد بيانات Postgres بدلاً من تشغيل pg_dump؟

ليس أثناء تشغيل Postgres. تقرأ عملية النسخ المتتابع لدليل بيانات نشط ملفات مختلفة في لحظات مختلفة، لذلك لا تمثل النتيجة حالة متسقة واحدة. ويرفضها Postgres عند بدء التشغيل مع PANIC: could not locate a valid checkpoint record، أو يفشل لاحقاً بسبب صفحة تالفة. تُعد النسخة الكاملة التي تُؤخذ بعد إيقاف Postgres نسخة احتياطية صالحة، لكنها لا تُفتح إلا باستخدام الإصدار الرئيسي نفسه من Postgres ومع extensions متوافقة. يثبت Immich صورة Postgres 14 التي تتضمن VectorChord وpgvector وpgvecto.rs، ولن يُفتح الدليل باستخدام إصدار رئيسي مختلف. تفريغ SQL عبارة عن نص عادي ويمكن استعادته على أي خادم متوافق، ولذلك تظل هذه هي النسخة التي يجب الاحتفاظ بها.

لماذا أصبح الخط الزمني في Immich فارغاً بعد الاستعادة؟

لأن الخط الزمني يُبنى من صفوف قاعدة البيانات، بينما استعدت الملفات من دون قاعدة البيانات. لا يفحص Immich upload/ لإعادة اكتشاف الصور، لذلك تظل الملفات التي لا تملك صفوفاً غير مرئية. الصور نفسها لم تتأثر. أوقف الخادم، ثم أعد تشغيل التفريغ في Postgres مهيأ حديثاً، وبعد ذلك ابدأ الحزمة. إذا كان الخط الزمني ممتلئاً بدلاً من ذلك، لكن كل صورة تفشل عند الفتح، فالمشكلة معكوسة: library وupload وprofile ليست موجودة مباشرة داخل الدليل المرتبط بالحاوية. تحقق من ذلك باستخدام docker exec immich_server ls /data.

ما مجلدات Immich التي يمكنني تخطيها في النسخة الاحتياطية؟

تُعاد thumbs وencoded-video من الملفات الأصلية، ويُعاد إنشاء DB_DATA_LOCATION من التفريغ، لذلك لا يلزم تضمين أي منها في مجموعة النسخ الاحتياطية. يؤدي تخطيها إلى استهلاك وقت بعد الاستعادة بدلاً من استهلاك مساحة قبلها، لأن إعادة إنشاء المعاينات والتحويلات لمكتبة كبيرة تستغرق ساعات من وقت CPU، ويمكن تشغيلها من Administration > Jobs لمعالجة الأصول المفقودة. لا يمكنك تخطي library وupload وprofile، لأنها تحتوي على النسخة الوحيدة من كل ملف أصلي.

هل يمكنني استعادة تفريغ Immich إلى إصدار أحدث؟

نعم عادةً، لأن الخادم يطبّق عمليات الترحيل المعلقة عند بدء التشغيل وينقل المخطط إلى الأمام. أما العكس فيفشل: لا يدعم Immich الرجوع إلى إصدار أقدم، حتى بين إصدارات التصحيح، لذلك لا يمكن تحميل تفريغ من إصدار أحدث إلى خادم أقدم. أجرِ الاستعادة مع تثبيت IMMICH_VERSION على الإصدار الذي أنشأ التفريغ، وتأكد من اكتمال الخط الزمني، ثم أجرِ الترقية بعد ذلك. سجّل الإصدار بجانب كل تفريغ باستخدام curl -fsS http://localhost:2283/api/server/version، لأن docker inspect لا يعرض إلا مرجع الصورة المُعد، ولأن IMMICH_VERSION=v3 الافتراضي عبارة عن وسم متحرك لا يقدم أي معلومات مفيدة.

هل كان هذا المقال مفيدًا؟