Tandoor: When Your Recipe Collection Outgrows Browser Bookmarks

A serious self-hosted kitchen brain with meal plans and shopping lists

My recipe “system” used to be a browser folder with 240-odd bookmarks, a Notes app full of half-typed ingredient lists, and a recurring Sunday-evening ritual of squinting at my phone in the supermarket trying to remember whether I needed one tin of chickpeas or three. It worked, in the sense that a shopping trolley with a wonky wheel works. It got me there, mostly, with some swearing.

Tandoor is what I replaced all of that with, and a year in I’m not going back.

Advertisement

Tandoor Recipes is a self-hosted recipe manager, and the word “manager” is doing real work in that sentence. It is not a place to dump screenshots of recipes. It is a structured database of food: every recipe is broken into steps, every step into ingredients, and every ingredient into an amount, a unit and a food. Once your data lives in that shape, the software can do clever things with it.

The headline features, roughly in the order I came to love them:

  • Recipe import / scraping. Paste a URL and Tandoor pulls the recipe straight off the page, parsing the ingredients into its structured format. Most decent recipe sites publish machine-readable markup, and Tandoor reads it.
  • Ingredient parsing with units and scaling. Because amounts are real numbers with real units, you can ask for the same dish for two people or for eleven, and every quantity rescales.
  • Meal plans. A calendar you drag recipes onto.
  • Auto-generated shopping lists with supermarket aisle ordering, so the list is sorted the way you actually walk the shop.
  • Keywords and books for organising the pile once it gets large.

If you’ve used a lighter recipe app — something like Mealie, or one of the dozens of “save a link, see a card” tools — Tandoor will feel like the heavy-duty cousin. It does more. It also asks more of you, both in setup and in the discipline of structuring your recipes properly. That trade is the whole story here, and I’ll come back to it.

Tandoor is a Django application, which in practice means a gunicorn app server, a Postgres database, and an nginx container serving the static and media files. Here’s a trimmed compose file close to what I run:

services:
  db_recipes:
    image: postgres:15-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=djangodb
      - POSTGRES_USER=tandoor
      - POSTGRES_PASSWORD=changeme_pg
    volumes:
      - ./postgresql:/var/lib/postgresql/data

  web_recipes:
    image: vabene1111/recipes
    restart: unless-stopped
    environment:
      - SECRET_KEY=please_generate_a_long_random_string
      - DB_ENGINE=django.db.backends.postgresql
      - POSTGRES_HOST=db_recipes
      - POSTGRES_DB=djangodb
      - POSTGRES_USER=tandoor
      - POSTGRES_PASSWORD=changeme_pg
    volumes:
      - ./staticfiles:/opt/recipes/staticfiles
      - ./mediafiles:/opt/recipes/mediafiles
    depends_on:
      - db_recipes

  nginx_recipes:
    image: nginx:mainline-alpine
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./staticfiles:/static:ro
      - ./mediafiles:/media:ro
    depends_on:
      - web_recipes

Two things bite people. First, generate a real SECRET_KEYopenssl rand -hex 32 does it — because the default-or-blank route ends in tears. Second, the static and media volumes must be shared between the app and nginx; the app writes them, nginx serves them, and if they don’t point at the same directory your images and stylesheets quietly vanish. Put the whole thing behind your usual reverse proxy for TLS and you’re done.

Here’s a normal week for me.

Import or enter. I find a recipe online, paste the URL into Tandoor, and it scrapes it. The parse is good but not psychic, so I spend two minutes fixing the bits it got wrong — usually splitting a compound ingredient line, or correcting a unit. Recipes from a cookbook I type by hand once; thereafter they’re permanent.

Structure the ingredients. This is the bit lighter apps skip, and it’s where the payoff lives. Each ingredient gets an amount, a unit, and a food. “2 cloves garlic” becomes amount 2, unit clove, food garlic. Do this properly and everything downstream just works.

Scale. Cooking for guests, I bump the servings from 4 to 8 and watch every quantity double. No mental arithmetic, no wrong-by-a-factor disasters.

Plan the week. I open the meal plan and drag recipes onto days. Five minutes on a Saturday and the week is decided.

Push to a shopping list. One click turns the meal plan into a consolidated shopping list. Three recipes that each want onions become one line with the totals added up, and the whole list is sorted by supermarket aisle so I’m not backtracking past the tinned tomatoes for the fourth time.

Tandoor is genuinely excellent, and it is also more software than a lot of people need.

It’s for you if you cook regularly from a growing collection, if meal-planning-to-shopping-list is a chore you’d happily automate, and if the structured-data discipline appeals rather than annoys. The aisle-ordered shopping list alone repaid the setup for me within a fortnight.

It’s overkill if you cook the same dozen things on rotation, or if you just want somewhere tidy to stash links. For that, a lighter manager — or honestly, a better-organised bookmark folder — will serve you fine, with none of the Postgres-and-nginx ceremony.

I sit firmly in the first camp. My 240 bookmarks are gone, the supermarket swearing has stopped, and the wonky trolley wheel is, sadly, the only part of the old system I couldn’t fix.

Advertisement

Related Content

Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.