Tandoor Recipes на Ubuntu 24.04: пошаговая установка
Если рецепты у вас разбросаны по закладкам, скриншотам из соцсетей и десятку вкладок в браузере, а посчитать калорийность блюда для плана питания приходится вручную в калькуляторе — Tandoor Recipes закрывает обе проблемы разом. Это self-hosted менеджер рецептов с автоматическим импортом по ссылке, пересчётом КБЖУ на порцию и группировкой рецептов в виртуальные книги. Ниже — установка на чистый VPS с Ubuntu 24.04 через Docker Compose, с доменом, HTTPS и рабочей схемой бэкапов.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Tandoor Recipes и чем он отличается от аналогов
Tandoor — открытый проект на Django (бэкенд) и Vue (фронтенд), который хранит всю базу рецептов на вашем сервере. По набору функций это один из самых полных self-hosted менеджеров рецептов из доступных сегодня:
- импорт рецепта по URL — встроенный парсер вытаскивает ингредиенты, шаги и фото с большинства кулинарных сайтов и блогов;
- расчёт КБЖУ (калории, белки, жиры, углеводы) на порцию, который автоматически пересчитывается при изменении количества порций;
- книги — рецепты можно объединять в виртуальные сборники (например, «Завтраки» или «Рецепты бабушки»), а не держать их одним общим списком;
- планировщик меню на неделю и автогенерация списка покупок с группировкой по категориям;
- отметки «уже есть дома» для ингредиентов — список покупок не включает то, что и так есть в холодильнике;
- мультипользовательский доступ с разделением прав (обычный пользователь / администратор);
- REST API и PWA — можно поставить как приложение на телефон.
Если сравнивать с более лёгкими альтернативами вроде Mealie — Tandoor тяжелее по требованиям к ресурсам (использует PostgreSQL, а не встроенную SQLite) и его интерфейс менее минималистичен, зато функций объективно больше: детальный учёт КБЖУ по каждому ингредиенту и структура «книг» для больших коллекций рецептов есть далеко не у всех аналогов. Если вам достаточно простого списка рецептов с планировщиком — Mealie проще в установке; если важен точный подсчёт нутриентов и организация в сборники — Tandoor стоит потраченного времени на настройку.
Требования к серверу и подготовка
Tandoor работает через связку из Django-приложения и PostgreSQL, поэтому запросы к ресурсам выше, чем у SQLite-аналогов:
- минимум 1 vCPU и 1 ГБ RAM — для личного использования этого хватает, но комфортнее работать с 2 ГБ, особенно если параллельно крутится ещё что-то на том же VPS;
- 10-20 ГБ диска — большую часть места займут фотографии рецептов, сама база данных весит немного;
- Ubuntu 24.04 (команды ниже написаны под неё; на Debian 12 разница минимальна);
- домен или поддомен, направленный A-записью на IP сервера, если планируете доступ по HTTPS (а не только по IP из локальной сети).
Точные цифры по RAM и диску — ориентир: у вас они будут отличаться в зависимости от числа рецептов, фотографий и одновременных пользователей.
Если Docker ещё не установлен, разверните его с нуля по отдельной статье: установка Docker на Ubuntu 24.04. После установки проверьте версии:
docker --version
docker compose version
Компоуз-плагин должен быть версии 2.x — команда docker compose (без дефиса) в актуальных сборках Docker идёт из коробки.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверУстановка Tandoor через Docker Compose
Официально поддерживаемый способ установки Tandoor — Docker Compose с тремя частями: PostgreSQL для данных, само приложение (образ уже включает бэкенд, собранный фронтенд и встроенный gunicorn+nginx) и файл переменных окружения. Создайте рабочую директорию:
sudo mkdir -p /opt/tandoor
cd /opt/tandoor
sudo mkdir -p postgresql staticfiles mediafiles
Создайте docker-compose.yml:
services:
db_recipes:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- ./postgresql:/var/lib/postgresql/data
environment:
POSTGRES_DB: tandoor
POSTGRES_USER: tandoor
POSTGRES_PASSWORD: замените_на_сложный_пароль
web_recipes:
image: ghcr.io/tandoorrecipes/recipes:latest
restart: unless-stopped
depends_on:
- db_recipes
ports:
- "8080:80"
env_file:
- ./.env
volumes:
- ./staticfiles:/opt/recipes/staticfiles
- ./mediafiles:/opt/recipes/mediafiles
И файл .env рядом с ним:
SECRET_KEY=сгенерируйте_длинную_случайную_строку
DB_ENGINE=django.db.backends.postgresql
POSTGRES_HOST=db_recipes
POSTGRES_PORT=5432
POSTGRES_USER=tandoor
POSTGRES_PASSWORD=тот_же_пароль_что_и_в_docker-compose.yml
POSTGRES_DB=tandoor
ALLOWED_HOSTS=recipes.example.com,203.0.113.10
TIMEZONE=Europe/Moscow
GUNICORN_MEDIA=0
Сгенерировать случайный SECRET_KEY можно так:
openssl rand -base64 48
Названия отдельных переменных в .env могут отличаться между версиями образа — перед первым запуском стоит свериться с .env.template в официальном репозитории проекта на GitHub (TandoorRecipes/recipes), если после запуска приложение ругается на конфигурацию. Пароль в POSTGRES_PASSWORD должен совпадать в обоих файлах — это частая причина, по которой контейнер приложения не может подключиться к базе при первом старте.
Запускайте:
sudo docker compose up -d
sudo docker compose ps
Оба контейнера должны перейти в статус running (или healthy, если в образе настроен healthcheck). Если что-то не поднялось — смотрите логи:
sudo docker compose logs -f
Чаще всего на этом этапе всплывают две проблемы: несовпадение пароля между .env и docker-compose.yml, либо порт 8080 уже занят другим сервисом на сервере — в последнем случае просто поменяйте левую часть "8080:80" на свободный порт.
Первый запуск и рабочее пространство
Откройте http://ваш-ip:8080 (или домен, если уже настроили прокси — см. следующий раздел). При первом заходе Tandoor предложит зарегистрировать первого пользователя — он автоматически становится администратором рабочего пространства («space» в терминологии Tandoor — контейнер, в котором живут рецепты, книги и пользователи конкретного домохозяйства или команды).
После входа стоит сразу:
- зайти в Settings → Spaces → Preferences и выставить единицы измерения (метрические/имперские) и язык интерфейса;
- в Books создать первый сборник рецептов — например, «Основное меню» — чтобы сразу раскладывать импортируемые рецепты по папкам, а не копить их одним списком;
- при необходимости завести дополнительных пользователей через административную панель, если планом питания занимается вся семья.
Для импорта рецепта достаточно вставить ссылку в форму Import → From URL — парсер вытащит ингредиенты, шаги приготовления, время готовки и КБЖУ, если исходный сайт указывает их в разметке. Если автоматика не сработала (нестандартная вёрстка сайта или защита от ботов), рецепт можно ввести вручную — все поля, включая КБЖУ по каждому ингредиенту, доступны в форме создания.
Домен, обратный прокси и SSL
Встроенный в образ nginx отдаёт приложение изнутри контейнера, но для доступа по HTTPS с человекочитаемым доменом нужен ещё один слой — обратный прокси на самом сервере (Nginx, Caddy или Traefik), который терминирует TLS-сертификат и проксирует запросы на порт 8080.
Минимальный конфиг для Nginx на хосте:
server {
listen 80;
server_name recipes.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
client_max_body_size 20M;
}
}
Параметр client_max_body_size стоит увеличить заранее — при загрузке фото рецептов или импорте через OCR лимит по умолчанию (1 МБ) режет запросы без внятной ошибки на стороне пользователя.
Дальше выпускайте сертификат Let's Encrypt — подробный разбор с certbot есть в отдельной статье: установка и настройка Let's Encrypt SSL на VPS. После выпуска сертификата не забудьте добавить домен в ALLOWED_HOSTS в .env и перезапустить контейнер приложения:
sudo docker compose restart web_recipes
Без этого шага Django вернёт ошибку 400 Bad Request при заходе по домену — типичная и легко упускаемая деталь при первой настройке.
Резервное копирование и обновление
Данные Tandoor хранятся в двух местах: база PostgreSQL (рецепты, планы меню, пользователи) и папка mediafiles (фотографии рецептов). Бэкапить нужно оба, иначе восстановление будет неполным.
Дамп базы вручную:
sudo docker compose exec db_recipes pg_dump -U tandoor tandoor > /root/backups/tandoor-$(date +%F).sql
Общие подходы к автоматизации бэкапа баз данных на сервере, включая ротацию и хранение вне сервера, разобраны отдельно: резервное копирование БД на VPS. Папку mediafiles из /opt/tandoor достаточно копировать тем же cron-заданием, что и дамп базы — например, архивом в то же хранилище:
tar czf /root/backups/tandoor-media-$(date +%F).tar.gz -C /opt/tandoor mediafiles
Обновление образа — стандартная процедура для Docker Compose:
sudo docker compose pull
sudo docker compose up -d
Перед обновлением на новую мажорную версию стоит свериться с release notes на GitHub-странице проекта — иногда меняется структура миграций базы, и разработчики явно указывают, требуется ли ручное вмешательство. Для рутинных версий делать дамп базы перед каждым pull — не паранойя, а минимум пяти минут, которые экономят вечер восстановления с нуля.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли обойтись без PostgreSQL, использовать SQLite?
Нет, в отличие от некоторых аналогов Tandoor рассчитан на PostgreSQL как основную СУБД — это плата за более сложную структуру данных (КБЖУ, книги, многопользовательские пространства).
Сколько реально нужно RAM для комфортной работы?
Для одного-двух пользователей хватает 1 ГБ, но если на том же сервере крутятся другие сервисы или пространство активно используется несколькими людьми одновременно — комфортнее заложить 2 ГБ, точная цифра зависит от нагрузки и не поддаётся универсальной формуле.
Импорт рецепта по ссылке не сработал — что делать?
Часть сайтов не использует стандартную разметку рецептов (schema.org/Recipe) или блокирует автоматические запросы парсера — в этом случае проще ввести рецепт вручную, скопировав текст, или через форму с вставкой распознаваемого текста.
Нужен ли отдельный Redis для кеша?
Для базовой установки — нет, минимальная связка PostgreSQL + приложение работает без него; Redis имеет смысл добавлять только при заметной нагрузке на большое пространство с множеством пользователей.
Что будет, если забыть добавить домен в ALLOWED_HOSTS?
Django просто откажется отдавать страницы по этому домену с ошибкой 400 — это защитный механизм от подмены Host-заголовка, а не баг, и правится добавлением домена в переменную с перезапуском контейнера.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →