Skip to main content

How to Self-Host Tandoor Recipes: Meal Planner and Shopping Lists 2026

·OSSAlt Team
tandoorrecipesmeal-planningself-hostingdocker2026

TL;DR

Tandoor Recipes (MIT, ~5K GitHub stars, Python/Vue) is a feature-rich self-hosted recipe manager. It imports recipes from any URL (Allrecipes, NYT Cooking, The Guardian, etc.), tracks nutrition, plans meals on a weekly calendar, and generates shopping lists from your meal plan. Recipe services like Paprika 3 charge $4.99 one-time but sync to cloud; Whisk and Mealime use subscriptions. Tandoor is free, self-hosted, and has a strong REST API for automation.

Key Takeaways

  • Tandoor: MIT, ~5K stars, Python/Vue — recipe import, nutrition, meal planning, shopping lists
  • URL import: Scrapes recipe sites automatically (Allrecipes, NYT Cooking, BBC Food, etc.)
  • Nutrition tracking: Automatic nutrition facts via built-in Open Food Facts integration
  • Meal planner: Weekly view, drag-and-drop recipes to days, generate shopping lists
  • Shopping lists: Auto-combine ingredients from multiple recipes, group by supermarket aisle
  • REST API: Full API for integration with Home Assistant, Grocy, other tools

Part 1: Docker Setup

# docker-compose.yml
services:
  db:
    image: postgres:15-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: djangodb
      POSTGRES_USER: djangouser
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U djangouser"]
      interval: 10s
      start_period: 20s

  web:
    image: vabene1111/recipes:latest
    container_name: tandoor
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - tandoor_media:/opt/recipes/mediafiles
      - tandoor_static:/opt/recipes/staticfiles
    environment:
      DB_ENGINE: django.db.backends.postgresql
      POSTGRES_HOST: db
      POSTGRES_PORT: 5432
      POSTGRES_USER: djangouser
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
      POSTGRES_DB: djangodb
      SECRET_KEY: "${SECRET_KEY}"
      ALLOWED_HOSTS: "recipes.yourdomain.com"
      GUNICORN_MEDIA: 0
      TZ: America/Los_Angeles
    depends_on:
      db:
        condition: service_healthy

  nginx:
    image: nginx:alpine
    container_name: tandoor_nginx
    restart: unless-stopped
    ports:
      - "8090:80"
    volumes:
      - tandoor_media:/media
      - tandoor_static:/static
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - web

volumes:
  db_data:
  tandoor_media:
  tandoor_static:
# nginx.conf
server {
    listen 80;
    
    location /media/ {
        alias /media/;
    }
    
    location /static/ {
        alias /static/;
    }
    
    location / {
        proxy_pass http://tandoor:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
# .env
POSTGRES_PASSWORD=your-db-password
SECRET_KEY=your-random-50-char-secret-key

docker compose up -d

Part 2: HTTPS with Caddy

recipes.yourdomain.com {
    reverse_proxy localhost:8090
}

Visit https://recipes.yourdomain.com → create admin account on first visit.


Part 3: Import Recipes

From URL (automatic scraping)

  1. Recipes → Create → Import from URL
  2. Paste URL: https://www.allrecipes.com/recipe/...
  3. Tandoor scrapes the page and extracts:
    • Title, description, image
    • Ingredients with amounts
    • Instructions (step by step)
    • Nutrition information (if available)
  4. Review and save

Supported sites include:

  • Allrecipes, Food Network, Epicurious
  • NYT Cooking, Serious Eats, Bon Appétit
  • BBC Food, BBC Good Food
  • Minimalist Baker, Cookie and Kate
  • Plus 2,000+ more via recipe schema.org markup

Manual recipe entry

  1. Recipes → Create → New Recipe
  2. Add ingredients with amounts and units
  3. Add steps with rich text editor
  4. Set cooking time, servings, nutrition

Import from other apps

# Import from Paprika (export to .paprikarecipes):
# Recipes → Import → Paprika

# Import from JSON/CSV:
# Recipes → Import → Bulk Import

Part 4: Meal Planning

Weekly planner

  1. Meal Plan → + Add
  2. Select recipe, date, meal type (breakfast/lunch/dinner/snack)
  3. Set number of servings

Drag and drop

The planner shows a weekly grid — drag recipes between days to plan your week.

Generate shopping list from meal plan

  1. Meal Plan → Select meals (checkboxes)
  2. Shopping → Create shopping list from selection
  3. Tandoor combines ingredients, merges duplicates, scales by servings:
    • Recipe A needs 2 cups flour (4 servings)
    • Recipe B needs 1 cup flour (2 servings)
    • Combined: 3 cups flour

Part 5: Shopping Lists

Smart ingredient combining

Tandoor automatically:

  • Converts units (cups → grams using density data)
  • Merges the same ingredient from multiple recipes
  • Deducts what you already have (if using inventory tracking)

Supermarket sorting

  1. Settings → Shopping → Supermarkets
  2. Add a supermarket: Whole Foods
  3. Add categories: Produce, Dairy, Meat, Bakery, Dry Goods
  4. Assign ingredients to categories (one-time setup)
  5. Shopping list automatically sorts items by store layout

Share shopping list

Generate a share link to a read-only list:

  • Shopping list → Share → copy link
  • Family members open link on phone while shopping

Part 6: Nutrition Tracking

# Tandoor uses Open Food Facts for nutrition data.
# Enable in settings:
# Settings → Integrations → Open Food Facts: Enabled

# Per recipe, nutrition shows:
# - Calories per serving
# - Protein, Fat, Carbohydrates
# - Fiber, Sugar, Sodium

Per-meal nutrition in the meal planner shows daily totals.


Part 7: REST API

# Get API token:
# Settings → API → Create Token

TOKEN="your-api-token"
BASE="https://recipes.yourdomain.com"

# List all recipes:
curl "$BASE/api/recipe/" \
  -H "Authorization: Token $TOKEN" | jq '.[].name'

# Get a recipe by ID:
curl "$BASE/api/recipe/42/" \
  -H "Authorization: Token $TOKEN"

# Create a recipe:
curl -X POST "$BASE/api/recipe/" \
  -H "Authorization: Token $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Pasta Carbonara", "servings": 4}'

# Get shopping list:
curl "$BASE/api/shopping-list-entry/" \
  -H "Authorization: Token $TOKEN" | jq '.[].ingredient.name'

# Get today's meal plan:
curl "$BASE/api/meal-plan/?from_date=$(date +%Y-%m-%d)&to_date=$(date +%Y-%m-%d)" \
  -H "Authorization: Token $TOKEN"

Home Assistant integration

# configuration.yaml — show tonight's dinner:
sensor:
  - platform: rest
    name: Tandoor Tonight
    resource: https://recipes.yourdomain.com/api/meal-plan/
    params:
      from_date: "{{ now().strftime('%Y-%m-%d') }}"
      to_date: "{{ now().strftime('%Y-%m-%d') }}"
    headers:
      Authorization: !secret tandoor_token
    value_template: >
      {{ value_json | selectattr('meal_type', 'eq', 3) | 
         map(attribute='recipe.name') | join(', ') }}

Part 8: Grocy Integration

Tandoor + Grocy creates a complete food management stack:

Tandoor meal plan → generate shopping list
Grocy shopping list → check off items while shopping
Grocy inventory → know what you have
Tandoor → filter meal plans to what's in stock
# Enable Grocy sync in Tandoor:
# Settings → Integrations → Grocy
# Grocy URL: https://home.yourdomain.com
# Grocy API Key: your-grocy-api-key

Maintenance

# Update:
docker compose pull
docker compose up -d

# Database backup:
docker exec tandoor-db-1 pg_dump -U djangouser djangodb \
  | gzip > tandoor-db-$(date +%Y%m%d).sql.gz

# Media backup (recipe images):
tar -czf tandoor-media-$(date +%Y%m%d).tar.gz \
  $(docker volume inspect tandoor_tandoor_media --format '{{.Mountpoint}}')

# Logs:
docker compose logs -f web

See also: Grocy — pair with Tandoor for inventory integration

See all open source recipe tools at OSSAlt.com/categories/home.

Comments