تشغيل Mealie على VPS باستخدام Docker Compose
شغّل Mealie على VPS مع Docker Compose، وثبّت الوسمة المستقرة بدل latest لتفادي ترحيل قاعدة بيانات مفاجئ، مع الوصفات والخطط وقوائم التسوق وTLS والنسخ الاحتياطية.
وظيفة مدير الوصفات المستضاف ذاتياً
يحتفظ مدير الوصفات المستضاف ذاتياً بوصفاتك في قاعدة بيانات على خادم تملكه، ويُعد Mealie الخيار الذي تستقر عليه معظم الأسر. تلصق عنوان صفحة الوصفة، فيقرأ Mealie المكونات والخطوات والكمية الناتجة ومدة الطهي منها، ثم يتجاهل القصة والإعلانات. وما يُضاف إلى مجموعتك هو وصفة الطعام.
أما بقية التطبيق فبسيطة. توجد خطة وجبات أسبوعية تسحب الوصفات إليها، وقائمة مشتريات تُنشأ من تلك الخطة. يحصل كل شخص يطبخ على حساب دخول خاص به. يعمل التطبيق كله في حاوية واحدة، ويبقى خاملاً بين الطلبات، لذلك يستطيع VPS متواضع تشغيله دون أن يلاحظه أحد.
يستخدم هذا الدليل Docker Compose. إذا كان المصطلحان services: وvolumes: جديدين عليك، فاقرأ أولاً كيفية إعداد ملفات Docker Compose، لأن كل ما يلي يتكون من ملف compose واحد وأربعة أوامر.
تثبيت Mealie باستخدام Docker Compose
ينشر Mealie صوره إلى سجل الحاويات في GitHub. اعتباراً من July 2026، تكون الوسمة المستقرة الحالية هي v3.22.0. ثبّت إصداراً محدداً بدلاً من استخدام latest: عند استخدام latest، قد تنقلك docker compose pull في يوم غير متوقع إلى مرحلة ترحيل لقاعدة البيانات لم تكن مستعداً لها.
sudo mkdir -p /srv/mealie
cd /srv/mealie
sudo nano docker-compose.ymlservices:
mealie:
image: ghcr.io/mealie-recipes/mealie:v3.22.0
container_name: mealie
restart: always
ports:
- "127.0.0.1:9925:9000"
deploy:
resources:
limits:
memory: 1000M
volumes:
- mealie-data:/app/data/
environment:
ALLOW_SIGNUP: "false"
PUID: 1000
PGID: 1000
TZ: Europe/Amsterdam
BASE_URL: https://recipes.example.com
volumes:
mealie-data:يجب مراجعة سطرين قبل تشغيله.
يُكتب المنفذ بصيغة 127.0.0.1:9925:9000 وليس بصيغة 9925:9000. يستمع الحاوي على المنفذ 9000 داخلياً، ويربط المضيف المنفذ 9925 به. يعني ربط هذا التعيين بعنوان loopback أن nginx يستطيع الوصول إلى Mealie، بينما لا يستطيع الإنترنت الوصول إليه. يكتب Docker قواعده الخاصة في مرشّح الحزم، لذلك يمكن الوصول إلى 9925:9000 من الخارج حتى عندما يشير جدارك الناري إلى أن المنفذ مغلق. من المهم فهم هذا السلوك مرة واحدة: راجع سبب تجاهل منافذ Docker المنشورة لـ ufw.
يجب أن يكون BASE_URL هو العنوان العام الدقيق الذي ستستخدمه، مع تضمين المخطط ومن دون شرطة مائلة في النهاية. ينشئ Mealie روابط إعادة تعيين كلمات المرور وروابط الدعوات انطلاقاً منه. اضبطه على http://localhost:9925، وإلا فستتضمن الدعوة التي ترسلها إلى شريكك رابطاً لا يعمل إلا على الخادم نفسه.
شغّله وراقب الإقلاع الأول.
sudo docker compose up -d
sudo docker compose logs -f mealieينشئ التشغيل الأول قاعدة بيانات SQLite وينفذ عمليات الترحيل، ويستغرق ذلك بضع ثوانٍ. عندما تستقر السجلات وتتوقف عن عرض أسطر الترحيل، افحص التطبيق محلياً.
curl -I http://127.0.0.1:9925تعني 200 OK أن التطبيق يعمل. وتعني Connection refused أن الحاوي لا يعمل: نفّذ sudo docker compose ps واقرأ رمز الخروج. إذا توقف الحاوي بالرمز 137، فهذا يعني أنه أُوقف لتجاوزه حد الذاكرة البالغ 1000M، ويحدث ذلك في أصغر الخطط.
تسجيل الدخول الأول وتعطيل التسجيل المفتوح
الحساب الافتراضي هو changeme@example.com، وكلمة المرور هي MyPassword. سجّل الدخول باستخدامهما، ثم غيّرهما فوراً، لأن هذا الزوج منشور في الوثائق، ولذلك يكون معروفاً لكل أدوات الفحص.
وجود ALLOW_SIGNUP: "false" في ملف Compose مقصود. عند فتح التسجيل، يمكن لأي شخص يعثر على العنوان إنشاء حساب في صندوق وصفاتك. عند إغلاقه، تضيف المستخدمين من منطقة الإدارة، التي تنشئ رابط دعوة ترسله إليهم بنفسك. يُنشأ هذا الرابط من BASE_URL، ولذلك فقيمة هذا المتغير مهمة. إذا شغّلت عدة تطبيقات على الخادم نفسه وأردت استخدام كلمة مرور واحدة لها جميعاً، يمكن لـMealie تمرير تسجيل الدخول إلى موفّر هوية خارجي، مثل مثيل Authentik مستضاف ذاتياً.
يضم Mealie المستخدمين في household. يشترك جميع أفراد household الواحد في مجموعة الوصفات وخطة الوجبات وقائمة التسوق، وهذا يناسب العائلة. أما households المنفصلة على الخادم نفسه، فتحافظ على مجموعات منفصلة، وهذا يناسب السكن المشترك عندما لا يتفق أحد على استخدام الأنشوجة.
المستورد، وهو سبب تشغيل هذا التطبيق
افتح مجموعة الوصفات، واختر إنشاء وصفة من عنوان URL، ثم الصق رابطاً. يجلب Mealie الصفحة ويبحث عن بيانات وصفة منظَّمة، وهي كتلة قابلة للقراءة آلياً تضمّنها معظم مواقع الوصفات لمحركات البحث. عند وجود هذه الكتلة، تكون عملية الاستيراد نظيفة وفورية.
يمكنك أيضاً الاستيراد من صورة أو من نص عادي تلصقه، بما في ذلك صورة لصفحة من كتاب طبخ. تمر هذه العمليات بمسار أبطأ، وتحتاج إلى مراجعة لاحقة، لأن قراءة كسر مكتوب بخط اليد قد تكون غير صحيحة بسهولة.
تُجرى عمليات الاستيراد المجمّعة من الشاشة نفسها: الصق قائمة بالعناوين، عنواناً واحداً في كل سطر، وسيعالجها Mealie في الخلفية. يمكنك نقل مجموعة تضم مئتي إشارة مرجعية في جلسة واحدة.
خطط الوجبات وقائمة التسوق
مخطط الوجبات عبارة عن تقويم. اسحب وصفة إلى يوم محدد لتخطيطها. تجمع قائمة التسوق بعد ذلك مكونات الوصفات المخططة في قائمة واحدة، وتدمج العناصر المكررة. فإذا احتاجت وصفتان إلى البصل، تظهر خانة واحدة بدلاً من خانتين.
القائمة صفحة مباشرة على هاتفك أثناء التسوق. وبما أنّ خادمك الخاص يحتفظ بها، يرى جميع أفراد المنزل القائمة نفسها في الوقت نفسه. وعندما يضع أحدهم علامة على الحليب لإتمامه، يُزال من شاشة الشخص الآخر.
ضع nginx وTLS أمام التطبيق
يستخدم Mealie بروتوكول HTTP العادي ولا يتولى إدارة الشهادات بنفسه. أنهِ TLS في nginx أمامه. أضف أولاً سجل DNS من النوع A يشير إلى خادمك، لأن خطوة إصدار الشهادة تتحقق من هذا الاسم.
sudo apt update && sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/mealieserver {
listen 80;
server_name recipes.example.com;
client_max_body_size 64M;
location / {
proxy_pass http://127.0.0.1:9925;
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;
}
}sudo ln -s /etc/nginx/sites-available/mealie /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxيمثل طبع nginx -t وsyntax is ok وtest is successful نقطة التحقق الأساسية. أعد التحميل فقط بعد نجاح هذا الفحص، لأن إعادة تحميل إعداد معطوب تُبقي الإعداد القديم قيد التشغيل وتخفي الخطأ حتى إعادة التشغيل التالية.
وجود client_max_body_size 64M ضروري لأن القيمة الافتراضية في nginx هي 1 MB. يؤدي رفع صورة وصفة أو استعادة نسخة احتياطية عبر المتصفح إلى إرسال جسم طلب أكبر من ذلك. من دون هذا السطر، تحصل على 413 Request Entity Too Large من nginx، لا من Mealie، ولذلك لا يظهر أي شيء في سجل التطبيق.
بعد ذلك، أصدر الشهادة. تتناول إصدار شهادة Let's Encrypt لـ nginx باستخدام certbot هذه الخطوة ومؤقت التجديد الخاص بها.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d recipes.example.comيعيد Certbot كتابة كتلة الخادم للاستماع على 443، ويضيف إعادة توجيه من المنفذ 80. افتح الموقع عبر https:// وتأكد من أن المتصفح يقبل الشهادة. إذا تم تحميل Mealie لكن روابطه الداخلية توجهك إلى http://، فهذا يعني أن BASE_URL لا يزال مضبوطاً على http. صححه، ثم استخدم sudo docker compose up -d لإعادة إنشاء الحاوية بالقيمة الجديدة.
لا يعمل تشغيل Mealie ضمن مسار فرعي مثل example.com/recipes، لأن الواجهة الأمامية لا يمكن تقديمها من مسار فرعي. استخدم نطاقاً فرعياً.
النسخ الاحتياطية، وما الذي تنفذه عملية الاستعادة فعلياً
يوجد كل ما يديره Mealie داخل /app/data/ في الحاوية، وهو المجلد mealie-data. انسخ هذا المجلد، وستنسخ الوصفات والصور وقاعدة البيانات معاً.
sudo docker volume ls
sudo docker compose stop mealie
sudo docker run --rm -v mealie_mealie-data:/data -v "$PWD":/backup \
alpine tar czf /backup/mealie-data.tgz -C /data .
sudo docker compose start mealieيحمل اسم المجلد اسم المشروع كبادئة، واسم المشروع هو اسم الدليل الذي يحتوي على ملف compose. من /srv/mealie يكون المجلد هو mealie_mealie-data، ولذلك يكون الأمر الأول هو docker volume ls: استخدم الاسم الذي يطبعه الأمر، وليس الاسم الوارد في هذا الدليل. من المهم إيقاف الحاوية أولاً، لأن SQLite تكون في منتصف عملية كتابة في كثير من الأحيان، وقد تؤدي النسخة الحية إلى استعادة غير قابلة للقراءة.
تحتوي Mealie أيضاً على صفحة نسخ احتياطي خاصة بها في منطقة الإدارة. تكتب هذه الصفحة أرشيفاً قابلاً للنقل يحتوي على قاعدة البيانات بصيغة JSON إلى جانب الصور. استخدمه عند نقل Mealie بين الخوادم، لأنه يظل صالحاً بعد تغيير الإصدار، بخلاف النسخ المباشر للملفات الذي قد لا يظل صالحاً. استعادة أرشيف تحذف قاعدة البيانات الحالية قبل تحميل الأرشيف، وهذا مقصود ولا يمكن التراجع عنه. سيُسجَّل خروجك عند انتهاء العملية.
لا تُعد أي من النسختين نسخة احتياطية ما دامت موجودة على الخادم نفسه. ادفع الأرشيف وفق جدول زمني إلى مكان آخر، فهذا هو الغرض من النسخ الاحتياطية المشفّرة خارج الخادم باستخدام restic.
تحديث Mealie
cd /srv/mealie
sudo nano docker-compose.yml
sudo docker compose pull
sudo docker compose up -d
sudo docker compose logs -f mealieارفع الإصدار المثبّت في الملف، ثم نفّذ السحب وأعد إنشاء الحاوية. تُنفَّذ عمليات الترحيل عند التشغيل الأول للصورة الجديدة. خذ نسخة من الـvolume قبل الانتقال بين إصدارات رئيسية، لأن فشل عملية الترحيل في منتصفها قد يترك قاعدة بيانات لا تعود الصورة السابقة قادرة على فتحها. اقرأ ملاحظات الإصدار لكل الإصدارات الواقعة بين إصدارك والإصدار الجديد.
عند فشل الاستيراد
لا تنشر بعض المواقع أي بيانات منظّمة للوصفات، ولذلك يستورد Mealie العنوان مع قائمة مكونات فارغة. لا يمكن معالجة ذلك من خلال الإعدادات. الصق نص الوصفة يدوياً بدلاً من ذلك.
تنتج حالات فشل أخرى عن حماية الروبوتات أمام موقع الوصفة، إذ يرسل الموقع إلى Mealie صفحة تحقق بدلاً من الوصفة. يحاكي Mealie المتصفح مسبقاً، ويبدّل user agent لتقليل هذه المشكلة. إذا واصل الموقع الرفض، فالخيارات الموثقة هي تمرير أداة الكشط عبر proxy ذي سمعة أفضل لعنوان IP، أو تشغيل نسخة من FlareSolverr تحل التحدي في متصفح فعلي. كلا الخيارين اختياري، ويُضبطان من خلال متغيرات البيئة الخاصة بالحاوية.
يختلف فشل الاستيراد الناتج عن عدم قدرة خادمك على الوصول إلى الموقع تماماً. اختبر الوصول من الخادم باستخدام curl -I https://the-site.example/recipe، واقرأ سطر الحالة قبل إلقاء اللوم على أداة الكشط.
الموضع المناسب
يُعد Mealie تطبيقاً جيداً كبداية للاستضافة الذاتية داخل المنزل، لأن الأشخاص الذين تعيش معهم سيستخدمونه من دون أن تطلب منهم ذلك. وهو يؤدي نوعاً مشابهاً من المهام لتشغيل مكتبة صورك الخاصة باستخدام Immich، لكنه أخف بكثير، ويندرج ضمن القائمة الأوسع لـالأشياء التي تستحق الاستضافة الذاتية هذا العام. ويمكن لخادم صغير واحد تشغيل التطبيقين معاً. ولا يُعد Immich الخيار الوحيد للمهمة الثانية. وإذا كنت لا تزال تقرر، فإن الحد الأدنى للذاكرة وأوامر النسخ الاحتياطي في PhotoPrism وImmich يختلفان بما يكفي ليستحقا القراءة قبل أن تستهلك بقية مساحة القرص.
FAQ
لماذا يفشل استيراد عنوان URL لوصفة؟
هناك سببان شائعان. إما أن الصفحة لا تنشر بيانات منظمة للوصفة، لذلك لا يجد scraper أي شيء، وتحصل على عنوان بلا مكونات، أو أن طبقة حماية من الروبوتات أمام الموقع تعيد صفحة تحدٍّ بدلاً من الوصفة. في الحالة الثانية، يمكن توجيه Mealie إلى proxy ذي سمعة أفضل للعنوان، أو إلى مثيل FlareSolverr مستضاف ذاتياً يحل التحدي في متصفح فعلي. تأكد من أن خادمك يستطيع الوصول إلى الصفحة أصلاً باستخدام curl -I قبل تغيير أي شيء.
هل أحتاج إلى PostgreSQL، أم تكفي SQLite؟
تكفي SQLite للاستخدام المنزلي، وهي الإعداد الافتراضي. انتقل إلى PostgreSQL عندما يكون مجلد البيانات موجوداً على وحدة تخزين متصلة بالشبكة، لأن SQLite عبر نظام ملفات شبكي تنتج أخطاء قاعدة بيانات مقفلة وقد تتسبب في تلف الملف. تتطلب عمليات الاستعادة في PostgreSQL أن يكون مستخدم قاعدة البيانات superuser، لأن عملية الاستعادة تحذف كل شيء قبل تحميل الأرشيف.
هل يمكنني تشغيل Mealie من دون اسم نطاق؟
نعم، على شبكتك الخاصة. اضبط BASE_URL على العنوان الذي ستكتبه فعلياً، مثل http://192.168.1.20:9925، وتجاوز nginx. تُبنى روابط الدعوات وإعادة تعيين كلمة المرور من BASE_URL، لذلك تؤدي القيمة الخاطئة إلى إنشاء روابط لا يستطيع أي شخص آخر فتحها. لا تعرّضه للإنترنت عبر HTTP العادي، لأن بيانات تسجيل الدخول تُرسل عندئذٍ من دون تشفير.
كيف أمنح أفراد عائلتي حسابات تسجيل دخول خاصة بهم؟
اترك ALLOW_SIGNUP مضبوطاً على "false"، وأضف الأشخاص من منطقة الإدارة، التي تنشئ رابط دعوة ترسله إليهم. ضع كل من يتشارك المطبخ في household واحد، حتى يتشاركوا الوصفات وخطة الوجبات وقائمة التسوق. تحافظ households المنفصلة على خوادم واحدة على مجموعات منفصلة.
ماذا يحدث لو توقفت عن تشغيل Mealie؟
يمكنك الاحتفاظ ببياناتك. تنشئ النسخة الاحتياطية من منطقة الإدارة بياناتك بتنسيق JSON، ويمكن لـMealie أيضاً تصدير الوصفات كملفات markdown نصية عادية، وتبقى هذه الملفات قابلة للقراءة في أي محرر نصوص من دون الحاجة إلى أي برنامج. أنشئ تصديراً واحداً قبل أن تحتاج إليه، وتحقق من قدرتك على فتحه.