Mealie на VPS: установка через Docker Compose
Установите Mealie на собственный VPS: импортируйте рецепт по ссылке, настройте планы питания и списки покупок, nginx, TLS и резервное копирование через Docker Compose.
Что делает self-hosted менеджер рецептов
Self-hosted менеджер рецептов хранит рецепты в базе данных на принадлежащем вам сервере. Mealie — наиболее распространённый выбор для домашних пользователей. Вы вставляете адрес страницы с рецептом, а Mealie извлекает из неё ингредиенты, этапы приготовления, количество порций и время приготовления. Текст и рекламу сервис не сохраняет. В коллекцию попадает только сам рецепт.
Остальная часть приложения небольшая. Есть недельный план питания, куда можно добавлять рецепты, и список покупок, который формируется на основе этого плана. Каждый пользователь, который готовит, получает отдельную учётную запись. Приложение работает в одном контейнере и простаивает между запросами, поэтому с ним справится даже недорогой VPS.
В этом руководстве используется Docker Compose. Если термины services: и volumes: вам незнакомы, сначала прочитайте как устроены файлы Docker Compose, потому что всё ниже состоит из одного compose-файла и четырёх команд.
Установка Mealie с помощью Docker Compose
Mealie публикует свои образы в реестре контейнеров GitHub. На июль 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 добавляет собственные правила в packet filter, поэтому обычный 9925:9000 доступен извне, даже если firewall сообщает, что порт закрыт. Это поведение важно понять один раз: см. почему опубликованные порты 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 объединяет пользователей в домохозяйства. Все пользователи одного домохозяйства имеют общий набор рецептов, план питания и список покупок — это подходит для семьи. Разные домохозяйства на одном сервере используют отдельные наборы рецептов. Это удобно для совместного проживания, когда никто не может договориться насчет анчоусов.
Импортёр, ради которого всё это запускается
Откройте коллекцию рецептов, выберите создание рецепта по URL и вставьте ссылку. Mealie загрузит страницу и найдёт структурированные данные рецепта — машиночитаемый блок, который большинство сайтов с рецептами встраивает для поисковых систем. Если этот блок присутствует, импорт выполняется быстро и без ошибок.
Также можно импортировать рецепт из изображения или вставленного обычного текста. Это подходит, например, для фотографии страницы из кулинарной книги. Такие данные обрабатываются медленнее и требуют последующей проверки: рукописную дробь легко распознать неправильно.
Массовый импорт запускается с того же экрана: вставьте список адресов, по одному в каждой строке, и Mealie обработает их в фоновом режиме. Коллекцию из двухсот закладок можно перенести за один сеанс.
Планы питания и список покупок
Планировщик питания представляет собой календарь. Перетащите рецепт на нужный день, и он будет добавлен в план. Затем список покупок собирает ингредиенты из запланированных рецептов в единый список и объединяет дубликаты. Поэтому два рецепта, в которых нужен лук, создают одну строку, а не две.
Список открывается на телефоне как актуальная страница во время покупок. Поскольку он хранится на вашем сервере, все члены семьи одновременно видят один и тот же список. Если один человек отмечает молоко как купленное, оно исчезает с экрана другого человека.
Разместите nginx и TLS перед приложением
Mealie работает по обычному HTTP и не обрабатывает сертификаты самостоятельно. Завершите TLS в nginx, размещённом перед Mealie. Сначала добавьте 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. При загрузке фотографии рецепта или восстановлении резервной копии через браузер отправляется тело запроса большего размера. Без этой строки nginx вернёт 413 Request Entity Too Large, а не Mealie, поэтому в журнале приложения вообще не появится записи.
Затем выпустите сертификат. Этот шаг и таймер его продления описаны в разделе выпуск сертификата Let's Encrypt для nginx с помощью certbot.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d recipes.example.comCertbot изменяет блок сервера: добавляет прослушивание порта 443 и перенаправление с порта 80. Откройте сайт по адресу https:// и убедитесь, что браузер принимает сертификат. Если Mealie загружается, но его ссылки перенаправляют на http://, значит, в BASE_URL по-прежнему указано http. Исправьте это значение, затем выполните sudo docker compose up -d, чтобы пересоздать контейнер с новым значением.
Размещение Mealie в подпути, например example.com/recipes, не работает, поскольку frontend нельзя обслуживать из подпути. Используйте поддомен.
Резервные копии и что на самом деле делает восстановление
Всё, чем управляет 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 вместе с изображениями. Используйте этот способ для переноса между серверами. Такой архив можно восстановить после смены версии, тогда как обычная копия файла может оказаться несовместимой. Восстановление по замыслу является разрушительной операцией: перед загрузкой архива текущая база данных удаляется, и отменить это действие нельзя. После завершения операции сеанс пользователя завершается.
Ни одна копия не является резервной, пока хранится на том же сервере. Регулярно отправляйте архив в другое место. Для этого предназначены зашифрованные резервные копии на удалённом сервере с 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Измените закреплённую версию в файле, затем выполните pull и пересоздайте контейнер. Миграции запускаются при первом старте нового образа. Перед переходом на новую основную версию создайте копию тома, поскольку миграция, завершившаяся ошибкой на середине процесса, может оставить базу данных, которую предыдущий образ больше не откроет. Изучите примечания к выпуску для всех версий между текущей и новой.
Когда импорт не работает
Некоторые сайты вообще не публикуют структурированные данные рецептов. В этом случае Mealie импортирует название, но список ингредиентов остаётся пустым. Настройками это не исправить. Вставьте текст рецепта вручную.
Другие сбои вызваны защитой от ботов перед сайтом с рецептами. Вместо рецепта такая защита возвращает Mealie страницу проверки. Mealie уже имитирует браузер и меняет user agent, чтобы снизить вероятность блокировки. Если сайт всё равно отклоняет запросы, в документации указаны два варианта: направить scraper через proxy с лучшей репутацией IP-адреса или запустить экземпляр FlareSolverr, который проходит проверку в настоящем браузере. Оба варианта необязательны. Оба настраиваются через переменные окружения контейнера.
Если импорт не работает потому, что сервер вообще не может подключиться к сайту, проблема в другом. Проверьте подключение с сервера с помощью curl -I https://the-site.example/recipe и прочитайте строку состояния, прежде чем обвинять scraper.
Где это уместно
Mealie — хорошее первое приложение для самостоятельного размещения в домашней сети, поскольку люди, с которыми вы живёте, будут им пользоваться без дополнительных просьб. По назначению оно похоже на собственную фототеку на базе Immich, но значительно легче и входит в более широкий список сервисов, которые стоит разместить самостоятельно в этом году. Один небольшой сервер может запускать оба приложения. Immich — не единственный кандидат на эту вторую задачу. Если вы ещё выбираете, требования к памяти и команды резервного копирования PhotoPrism и Immich заметно различаются, поэтому стоит ознакомиться с ними до того, как вы займёте оставшееся место на диске. Если домашнее хозяйство хранит планы и заметки не только рецептов и ужинов, самостоятельно размещаемое рабочее пространство AFFiNE снова настраивается через compose-файл, но ему требуются четыре контейнера и значительно больше памяти, чем Mealie. Сначала проверьте, сколько ресурсов осталось на сервере. Если домашнее хозяйство хочет отслеживать тренировки так же, как ужины, openGym хранит данные о тренировках и использует тот же закреплённый тег, compose-файл и сертификат, которые вы только что настроили. Для вечера после ужина есть отдельный вариант: Halcyon превращает существующую библиотеку Jellyfin в видеопрокат 1990-х годов, по которому можно перемещаться. Это ещё один небольшой контейнер перед уже работающим сервисом, а не новая база данных, для которой нужно настраивать резервное копирование. Не все приложения из этого семейства предназначены для домашнего использования. Если тот же подход потребуется применить для рабочих задач, самостоятельно размещаемый сервис поддержки Chatwoot снова использует закреплённый тег и сертификат, но требует Postgres, Redis и настроенной исходящей почты. Это значительно более требовательный сервис, чем хранилище рецептов, поэтому для него стоит выделить отдельный сервер.
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, чтобы они могли пользоваться общими рецептами, планом питания и списком покупок. Разные household на одном сервере сохраняют отдельные коллекции.
Что произойдёт с моими рецептами, если я перестану запускать Mealie?
Они сохранятся у вас. Резервная копия из административного раздела записывает данные в формате JSON. Кроме того, Mealie может экспортировать рецепты в виде обычных markdown-файлов. Такие файлы можно открыть в любом текстовом редакторе, даже без специального ПО. Выполните экспорт заранее и проверьте, что файлы открываются.