SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor

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 container registry. По состоянию на октябрь 2026 года текущим стабильным тегом является v3.28.0. Фиксируйте версию вместо использования latest: при latest случайное обновление docker compose pull в любой день может привести к миграции базы данных, к которой вы не готовы.

sudo mkdir -p /srv/mealie
cd /srv/mealie
sudo nano docker-compose.yml
services:
  mealie:
    image: ghcr.io/mealie-recipes/mealie:v3.28.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 распределяет пользователей по домохозяйствам, а домохозяйства объединяются в группы. Все участники одного домохозяйства совместно используют план питания и список покупок, что удобно для семьи. Коллекция рецептов имеет более широкую область действия: домохозяйства делятся своими рецептами, тегами и категориями со всеми остальными домохозяйствами в той же группе. Таким образом, отдельные домохозяйства на одном сервере ведут раздельные планы и списки, но готовят по одним и тем же рецептам, что подходит для совместного проживания, где вкусы участников не совпадают. Если пользователям нужны полностью изолированные коллекции, поместите их в разные группы, так как между группами данные не передаются.

Импортер — основная причина использования системы

Откройте коллекцию рецептов, выберите создание рецепта по URL и вставьте ссылку. Mealie загружает страницу и ищет структурированные данные рецепта — машиночитаемый блок, который большинство кулинарных сайтов встраивают для поисковых систем. При наличии этого блока импорт выполняется корректно и мгновенно.

Если вы настроите провайдера ИИ, вы также сможете выполнять импорт из изображения или обычного текста, который вы вставляете вручную; это позволяет импортировать фотографии страниц из кулинарных книг. Для импорта изображений требуется провайдер, способный распознавать графические данные. Без провайдера эти функции импорта недоступны. Они проходят по более медленному пути и требуют последующей проверки, так как рукописные дроби легко распознать неверно.

Массовый импорт запускается с того же экрана: вставьте список адресов (по одному на строку), и Mealie обработает их в фоновом режиме. Коллекция из 200 закладок переносится за один сеанс.

Планы питания и список покупок

Планировщик питания представляет собой календарь. Перетащите рецепт на нужный день, и он будет добавлен в план. Затем список покупок собирает ингредиенты из запланированных рецептов в единый список и объединяет дубликаты. Поэтому два рецепта, в которых нужен лук, создают одну строку, а не две.

Список открывается на телефоне как актуальная страница во время покупок. Поскольку он хранится на вашем сервере, все члены семьи одновременно видят один и тот же список. Если один человек отмечает молоко как купленное, оно исчезает с экрана другого человека.

Разместите nginx и TLS перед приложением

Mealie работает по обычному HTTP и не обрабатывает сертификаты самостоятельно. Завершите TLS в nginx, размещённом перед Mealie. Сначала добавьте DNS-запись типа A для сервера. На этапе выпуска сертификата проверяется это имя.

sudo apt update && sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/mealie
server {
    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.com

Certbot изменяет блок сервера: добавляет прослушивание порта 443 и перенаправление с порта 80. Откройте сайт по адресу https:// и убедитесь, что браузер принимает сертификат. Если Mealie загружается, но его ссылки перенаправляют на http://, значит, в BASE_URL по-прежнему указано http. Исправьте это значение, затем выполните sudo docker compose up -d, чтобы пересоздать контейнер с новым значением.

Размещение Mealie в подпути, например example.com/recipes, не работает, поскольку frontend нельзя обслуживать из подпути. Используйте поддомен.

Резервное копирование и процесс восстановления

Все данные Mealie находятся в /app/data/ внутри контейнера, что соответствует тому, как смонтирован том mealie-data. Скопируйте этот том, и вы получите полную копию рецептов, изображений и базы данных. В официальной документации Mealie остановка контейнера и копирование этой папки названы лучшим способом создания резервной копии при использовании SQLite.

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 также есть собственная страница резервного копирования, которая создает zip-архив с базой данных в виде файла database.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

Измените закреплённую версию в файле, затем выполните 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 завершается ошибкой?

Существует две основные причины. Либо на странице отсутствуют структурированные данные рецепта, поэтому парсер ничего не находит и вы получаете заголовок без списка ингредиентов, либо система защиты от ботов на сайте возвращает страницу с проверкой вместо самого рецепта. Во втором случае для Mealie можно настроить прокси с более высокой репутацией IP-адреса или использовать self-hosted экземпляр FlareSolverr, который проходит проверку в полноценном браузере. Прежде чем что-либо менять, убедитесь, что ваш сервер вообще может получить доступ к странице с помощью curl -I.

Нужен ли мне PostgreSQL или достаточно SQLite?

Для домашнего использования достаточно SQLite, он установлен по умолчанию. Переходите на PostgreSQL, если каталог с данными находится на сетевом хранилище (NAS), так как работа SQLite через сетевую файловую систему приводит к ошибкам блокировки базы данных и может повредить файл. При восстановлении из резервной копии в PostgreSQL пользователю базы данных требуются права суперпользователя, так как процесс восстановления удаляет всё содержимое перед загрузкой архива.

Можно ли запустить Mealie без доменного имени?

Да, в пределах вашей локальной сети. Установите BASE_URL в значение адреса, который вы будете вводить в браузере, например http://192.168.1.20:9925, и пропустите настройку nginx. Ссылки для приглашений и сброса пароля формируются на основе BASE_URL, поэтому неверное значение приведет к тому, что ссылки станут нерабочими для других пользователей. Не открывайте доступ к сервису из Интернета по обычному HTTP, так как в этом случае данные для входа передаются в открытом виде.

Как предоставить членам семьи отдельные учетные записи?

Оставьте ALLOW_SIGNUP установленным в "false" и добавляйте пользователей через панель администратора — это создаст ссылку-приглашение, которую вы сможете им отправить. Добавьте всех, кто пользуется одной кухней, в одно домохозяйство, чтобы они могли совместно использовать план питания и список покупок. Рецепты в любом случае доступны всем домохозяйствам в группе, поэтому пользователям, которым нужны отдельные коллекции, потребуются отдельные группы.

Что произойдет с моими рецептами, если я перестану использовать Mealie?

Они останутся у вас. Функция резервного копирования администратора сохраняет данные в формате JSON, кроме того, Mealie позволяет экспортировать рецепты в виде обычных файлов markdown. Они останутся читаемыми в любом текстовом редакторе без какого-либо дополнительного ПО. Сделайте один экспорт заранее и убедитесь, что можете открыть полученные файлы.