آموزش نصب Mealie روی VPS با Docker Compose
با استفاده از Docker Compose و این راهنما، Mealie را روی سرور شخصی خود نصب کنید. دستورپختها را ذخیره کرده و لیست خرید بسازید. شامل تنظیمات Nginx و TLS برای امنیت کامل.
مدیریت دستورپختهای شخصی با میزبانی محلی
یک مدیریتکننده دستورپخت (recipe manager) که بهصورت محلی میزبانی میشود، دستورپختهای شما را در پایگاهدادهای روی سروری که مالک آن هستید نگهداری میکند و Mealie گزینهای است که اکثر خانوادهها آن را انتخاب میکنند. شما آدرس صفحه یک دستورپخت را وارد میکنید و Mealie مواد لازم، مراحل، مقدار خروجی و زمان پخت را از آن استخراج کرده و حواشی و تبلیغات را حذف میکند. آنچه در مجموعه شما ذخیره میشود، فقط اصل دستور تهیه غذا است.
بخشهای دیگر این برنامه ساده هستند. یک برنامهریز غذای هفتگی وجود دارد که میتوانید دستورپختها را به داخل آن بکشید (drag) و یک لیست خرید که بر اساس آن برنامه ساخته میشود. هر فردی که آشپزی میکند، حساب کاربری اختصاصی خود را دارد. کل این برنامه در یک container اجرا میشود و در فواصل بین درخواستها غیرفعال است؛ بنابراین یک VPS معمولی بدون هیچ فشار اضافهای آن را اجرا میکند.
این راهنما از Docker Compose استفاده میکند. اگر عبارتهای services: و volumes: برای شما جدید هستند، ابتدا نحوه ساختار فایلهای Docker Compose را مطالعه کنید، زیرا تمام مراحل زیر شامل یک فایل compose و چهار دستور است.
نصب Mealie با Docker Compose
Mealie تصاویر خود را در GitHub container registry منتشر میکند. تا ژوئیه 2026، تگ پایدار فعلی v3.22.0 است. به جای استفاده از latest، یک نسخه خاص را پین کنید: با latest، یک docker compose pull در روزی نامرتبط میتواند شما را به سمت یک migration دیتابیس ببرد که برای آن آماده نبودهاید.
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 قوانین خود را در packet filter مینویسد، بنابراین یک 9925:9000 ساده حتی زمانی که فایروال شما میگوید پورت بسته است، از بیرون قابل دسترسی خواهد بود. درک این غافلگیری یک بار ارزشش را دارد: ببینید چرا پورتهای منتشر شده Docker، تنظیمات ufw را نادیده میگیرند.
BASE_URL باید دقیقاً همان آدرس عمومی باشد که استفاده خواهید کرد، همراه با طرح (scheme) و بدون اسلش در انتها. Mealie لینکهای بازنشانی رمز عبور و لینکهای دعوت را بر اساس آن میسازد. اگر آن را روی http://localhost:9925 تنظیم کنید، دعوتی که برای شریک خود میفرستید حاوی لینکی خواهد بود که فقط روی خود سرور کار میکند.
آن را شروع کنید و اولین بوت را زیر نظر بگیرید.
sudo docker compose up -d
sudo docker compose logs -f mealieاولین اجرا، دیتابیس SQLite را ایجاد کرده و migrationها را انجام میدهد که چند ثانیه طول میکشد. هنگامی که لاگها آرام شدند و چاپ خطوط migration متوقف شد، برنامه را به صورت محلی بررسی کنید.
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 کاربران را در یک خانوار دستهبندی میکند. همه افراد در یک خانوار، مجموعه دستور پختها، برنامه غذایی و لیست خرید را به اشتراک میگذارند که همان چیزی است که یک خانواده به آن نیاز دارد. خانوارهای جداگانه روی یک سرور، مجموعههای جداگانهای را حفظ میکنند که این همان چیزی است که در یک خانه اشتراکی (flatshare) مورد نیاز است، زمانی که هیچکس در مورد استفاده از ماهی آنچوی توافق ندارد.
واردکننده، دلیلی برای اجرای این برنامه
مجموعه دستورپختها را باز کنید، گزینه ایجاد دستورپخت از طریق URL را انتخاب کرده و لینک مورد نظر را جایگذاری کنید. Mealie صفحه را فراخوانی کرده و به دنبال دادههای ساختاریافته دستورپخت میگردد؛ این همان بلوک قابلخواندن توسط ماشین است که اکثر سایتهای آشپزی برای موتورهای جستجو در صفحات خود تعبیه میکنند. هنگامی که این بلوک موجود باشد، عملیات وارد کردن تمیز و فوری انجام میشود.
شما همچنین میتوانید از طریق تصویر یا متن سادهای که جایگذاری میکنید، دستورپختها را وارد کنید؛ این روش برای عکس گرفتن از صفحات کتاب آشپزی کاربرد دارد. این موارد از مسیر کندتری عبور میکنند و نیاز به بررسی پس از وارد کردن دارند، زیرا تشخیص کسرها در دستخط ممکن است با خطا همراه باشد.
وارد کردن دستهجمعی نیز از همان صفحه انجام میشود: فهرستی از آدرسها را که هر کدام در یک خط قرار دارند جایگذاری کنید تا Mealie در پسزمینه آنها را پردازش کند. مجموعهای از 200 بوکمارک را میتوان در یک نوبت به برنامه منتقل کرد.
برنامههای غذایی و لیست خرید
برنامهریز وعدههای غذایی یک تقویم است. با کشیدن و رها کردن (Drag and drop) یک دستور پخت روی هر روز، آن وعده برنامهریزی میشود. سپس لیست خرید، مواد اولیه دستورهای برنامهریزیشده را در یک لیست واحد جمعآوری کرده و موارد تکراری را ادغام میکند؛ بنابراین اگر دو دستور پخت به پیاز نیاز داشته باشند، بهجای دو سطر، تنها یک سطر در لیست ایجاد میشود.
این لیست یک صفحه زنده روی تلفن همراه شما در هنگام خرید است. از آنجا که سرور شخصی شما میزبان آن است، همه اعضای خانواده لیست یکسانی را بهطور همزمان مشاهده میکنند و اگر یک نفر گزینه شیر را تیک بزند، آن مورد از صفحه سایر افراد نیز حذف میشود.
قرار دادن Nginx و TLS در مقابل سرویس
برنامه Mealie فقط از پروتکل HTTP پشتیبانی میکند و قابلیت مدیریت گواهینامه ندارد. برای حل این مشکل، TLS را در Nginx که در مقابل آن قرار دارد، خاتمه (Terminate) دهید. ابتدا یک رکورد 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، دروازهٔ اطمینان شماست. تنها پس از موفقیتآمیز بودن این بررسی، تنظیمات را Reload کنید؛ زیرا Reload کردن یک پیکربندی معیوب باعث میشود نسخهٔ قبلی همچنان اجرا شود و خطا تا زمان راهاندازی مجدد بعدی پنهان بماند.
خط 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 بلاک server را بازنویسی میکند تا روی پورت 443 گوش دهد و یک تغییر مسیر (Redirect) از پورت 80 اضافه میکند. سایت را از طریق https:// باز کنید و مطمئن شوید که مرورگر گواهینامه را میپذیرد. اگر Mealie بارگذاری میشود اما لینکهای داخلی آن شما را به http:// هدایت میکنند، به این معنی است که BASE_URL همچنان مقدار http را دارد و نیاز به اصلاح دارد؛ پس از آن باید با دستور sudo docker compose up -d کانتینر را با مقدار جدید بازسازی کنید.
اجرای Mealie در یک زیرمسیر (Subpath) مانند example.com/recipes کار نمیکند، زیرا فرانتاند برنامه قابلیت اجرا از زیرمسیر را ندارد. حتماً از یک زیردامنه (Subdomain) استفاده کنید.
پشتیبانگیری و عملکرد واقعی بازیابی
تمام دادههای Mealie در /app/data/ داخل کانتینر قرار دارد که همان volume با نام mealie-data است. با کپی کردن این volume، شما دستورپختها، تصاویر و پایگاه داده را بهصورت یکجا کپی کردهاید.
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نام volume شامل پیشوندی از نام پروژه است و نام پروژه همان دایرکتوری حاوی فایل compose میباشد. از مسیر /srv/mealie، نام volume برابر با mealie_mealie-data است؛ به همین دلیل اولین دستور docker volume ls است: از نامی که دستور چاپ میکند استفاده کنید، نه نامی که در این راهنما آمده است. متوقف کردن کانتینر پیش از انجام عملیات ضروری است، زیرا SQLite اغلب در حال نوشتن است و کپی گرفتن از آن در حین اجرا ممکن است منجر به بازیابی یک فایل غیرقابلخواندن شود.
Mealie همچنین یک صفحه پشتیبانگیری اختصاصی در بخش مدیریت دارد که یک آرشیو قابلحمل شامل پایگاه داده به فرمت JSON و تصاویر شما ایجاد میکند. از این روش برای انتقال بین سرورها استفاده کنید، زیرا این روش در برابر تغییر نسخهها که ممکن است کپی مستقیم فایلها در آن دچار مشکل شود، مقاوم است. بازیابی این آرشیو ذاتاً مخرب است: پیش از بارگذاری آرشیو، پایگاه داده فعلی را حذف میکند و این عملیات قابل بازگشت نیست. پس از پایان عملیات، شما از سیستم خارج (logout) میشوید.
هیچکدام از این کپیها تا زمانی که روی همان سرور باقی بمانند، پشتیبان محسوب نمیشوند. آرشیو را طبق یک زمانبندی به جای دیگری منتقل کنید؛ این همان کاری است که پشتیبانگیری رمزنگاریشده خارج از سرور با 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نسخهٔ pinned را در فایل افزایش دهید، سپس image جدید را pull کرده و container را دوباره ایجاد کنید. عملیات migration در اولین اجرای image جدید بهصورت خودکار انجام میشود. پیش از انجام تغییرات نسخهٔ اصلی (major version)، حتماً از volume نسخهٔ فعلی یک کپی تهیه کنید؛ زیرا اگر فرآیند migration در میانهٔ راه با خطا مواجه شود، دیتابیس به حالتی درمیآید که نسخهٔ قبلی دیگر قادر به باز کردن آن نخواهد بود. پیش از بهروزرسانی، یادداشتهای انتشار (release notes) را برای تمامی نسخههای بین نسخهٔ فعلی خود و نسخهٔ جدید مطالعه کنید.
هنگامی که واردکننده (importer) با شکست مواجه میشود
برخی از سایتها هیچگونه دادهٔ ساختاریافتهای برای دستور پخت منتشر نمیکنند؛ در این حالت Mealie فقط عنوان را وارد کرده و لیست مواد اولیه را خالی میگذارد. این موضوعی نیست که بتوانید با تغییر پیکربندی آن را رفع کنید. در عوض، متن دستور پخت را بهصورت دستی کپی و جایگذاری کنید.
سایر خطاها ناشی از سیستمهای محافظت از ربات (bot protection) در مقابل سایتهای دستور پخت است که بهجای محتوای دستور، یک صفحهٔ چالش (challenge page) به Mealie پاسخ میدهند. Mealie برای کاهش این مشکل، از قبل رفتار یک مرورگر را شبیهسازی کرده و User Agent خود را تغییر میدهد. هنگامی که یک سایت همچنان دسترسی را مسدود میکند، گزینههای مستندشده عبارتند از: عبور دادن scraper از طریق یک پروکسی با اعتبار آدرس (IP reputation) بهتر، یا اجرای یک نمونه FlareSolverr که چالش را در یک مرورگر واقعی حل میکند. هر دو گزینه اختیاری هستند و از طریق متغیرهای محیطی (environment variables) در container تنظیم میشوند.
شکست در وارد کردن دادهها به دلیل عدم دسترسی سرور شما به سایت، مسئلهٔ متفاوتی است. پیش از آنکه scraper را مقصر بدانید، دسترسی را از داخل سرور با دستور curl -I https://the-site.example/recipe تست کنید و خط وضعیت (status line) را بخوانید.
جایگاه آن
برنامه Mealie یک گزینه مناسب برای شروع میزبانی شخصی در محیط خانه است، زیرا اعضای خانواده بدون نیاز به درخواست شما از آن استفاده خواهند کرد. ماهیت این کار مشابه راهاندازی کتابخانه عکس شخصی با Immich است، هرچند بسیار سبکتر بوده و در فهرست گستردهتری از سرویسهای ارزشمند برای میزبانی شخصی در سال جاری قرار میگیرد. یک سرور کوچک میتواند هر دو را میزبانی کند. Immich تنها گزینه برای آن کار دوم نیست و اگر هنوز در حال تصمیمگیری هستید، حداقل حافظه مورد نیاز و دستورات پشتیبانگیری در PhotoPrism و Immich تفاوتهای کافی دارند که پیش از اختصاص دادن باقی فضای دیسک، ارزش مطالعه داشته باشند.
FAQ
چرا وارد کردن URL دستور پخت با خطا مواجه میشود؟
دو دلیل رایج برای این مشکل وجود دارد. یا صفحه مورد نظر هیچ داده ساختاریافتهای از دستور پخت منتشر نمیکند و در نتیجه scraper چیزی پیدا نمیکند و شما عنوانی بدون مواد اولیه دریافت میکنید، یا یک لایه محافظت از ربات در مقابل سایت، بهجای دستور پخت، یک صفحه چالش (challenge) برمیگرداند. در مورد دوم، میتوان Mealie را به یک پروکسی با اعتبار آدرس بهتر یا یک نمونه FlareSolverr که بهصورت self-hosted اجرا شده و چالش را در یک مرورگر واقعی حل میکند، متصل کرد. پیش از تغییر هر چیزی، با استفاده از curl -I تأیید کنید که سرور شما اصلاً به آن صفحه دسترسی دارد.
آیا به PostgreSQL نیاز دارم یا SQLite کافی است؟
برای مصارف خانگی، SQLite کافی است و گزینه پیشفرض نیز همین است. زمانی به PostgreSQL مهاجرت کنید که دایرکتوری دادهها روی حافظه متصل به شبکه (NAS) قرار دارد؛ زیرا SQLite روی فایلسیستمهای شبکهای باعث خطای locked-database شده و میتواند فایل را خراب کند. عملیات بازیابی (restore) در PostgreSQL نیازمند این است که کاربر دیتابیس دارای دسترسی superuser باشد، زیرا فرآیند بازیابی پیش از بارگذاری آرشیو، همه دادههای قبلی را حذف میکند.
آیا میتوانم Mealie را بدون نام دامنه اجرا کنم؟
بله، در شبکه داخلی خودتان. مقدار BASE_URL را روی آدرسی که واقعاً تایپ خواهید کرد (مانند http://192.168.1.20:9925) تنظیم کنید و از nginx صرفنظر کنید. لینکهای دعوت و بازنشانی رمز عبور بر اساس BASE_URL ساخته میشوند، بنابراین مقدار اشتباه باعث ایجاد لینکهایی میشود که هیچکس قادر به باز کردن آنها نخواهد بود. آن را از طریق HTTP ساده در معرض اینترنت قرار ندهید، زیرا در این صورت اطلاعات ورود بهصورت متن آشکار ارسال میشود.
چگونه برای اعضای خانوادهام حساب کاربری جداگانه بسازم؟
مقدار ALLOW_SIGNUP را روی "false" باقی بگذارید و افراد را از بخش مدیریت (admin area) اضافه کنید؛ این کار یک لینک دعوت تولید میکند که میتوانید برای آنها بفرستید. همه کسانی که در یک آشپزخانه مشترک هستند را در یک household قرار دهید تا دستور پختها، برنامههای غذایی و لیست خرید را به اشتراک بگذارند. householdهای مجزا روی یک سرور، مجموعههای جداگانهای را حفظ میکنند.
اگر اجرای Mealie را متوقف کنم، چه بلایی سر دستور پختهایم میآید؟
آنها در دسترس باقی میمانند. نسخه پشتیبان مدیریتی، دادههای شما را بهصورت JSON مینویسد و Mealie همچنین میتواند دستور پختها را بهعنوان فایلهای متنی ساده (markdown) خروجی بگیرد که در هر ویرایشگر متنی و بدون نیاز به هیچ نرمافزاری قابل خواندن هستند. پیش از آنکه به خروجی نیاز پیدا کنید، یک بار آن را تهیه کرده و بررسی کنید که میتوانید فایلها را باز کنید.