Fully verified8,703

Food and cooking

Tandoor

Tandoor organizes household recipes with ingredients, cooking steps and images, plans meals on a calendar, and turns planned dishes into shared shopping lists. This recipe runs Tandoor with PostgreSQL, persistent media and regenerated static files; external AI and storage services are optional and unconfigured.

Overview

Tandoor

Tandoor is a household recipe library with ingredients, preparation steps, photographs, cookbooks, a meal calendar and shared shopping lists. It suits families who want to plan meals and reuse a common collection on their own server. It replaces parts of Paprika Recipe Manager, Recipe Keeper and AnyList; it does not bundle their paid apps or cloud services. Compared with Mealie, it offers another approach to recipe organization, meal planning and shopping, with a different license.

Tandoor is source-available, not OSI-approved open source. Its license is GNU AGPL v3 with the Commons Clause selling restriction. Personal and internal use are allowed, but the license does not grant the right to sell the software or services whose value derives substantially from it, including hosting and related consulting/support. Review the actual license before offering a service. The recipe files use MIT; the logo’s upstream notice and license are preserved in assets/NOTICE and assets/LICENSE.

This recipe uses the official Tandoor 2.7.2 image, PostgreSQL 18.6 and an Alpine 3.22.2 initialization/archive helper. The application includes Nginx and Gunicorn and runs as UID/GID 65534; initialization prepares only the media/static volume roots. The database and media are persistent, while static assets are regenerated from the pinned image at startup. External AI providers, storage integrations, social identity providers and SMTP are not configured.

The web interface supports English and Russian. The official related-project directory lists the third-party kitshn client for Android and iOS; it is not installed or tested here. There is no desktop client in this recipe. Untare is listed upstream as discontinued and is not the recommended mobile option.

Local username/password login uses the browser authentication API. Bundled django-allauth providers support OIDC and SAML, and a reverse-proxy user-header mode exists; the recipe leaves all three disabled. No embedded second factor is enabled in this build. Use a properly configured identity provider if you need MFA, and never enable remote-user authentication behind a proxy that accepts an identity header from a visitor.

The security assessment records warnings. The first visitor to /setup/ can create the owner, so bootstrap through loopback or an SSH tunnel before opening the proxy or LAN access. Ordinary signup is explicitly disabled. Permit trusted accounts, restrict server-side import access to sensitive internal services, protect the volumes and encrypt off-server backups.

Uploaded images are served by Nginx without per-file authorization. Marking a recipe private protects the recipe API, but does not make a known image URL private. Do not store sensitive photographs. Recipes, household membership, meal plans and integration credentials live in PostgreSQL and backups; the media archive and .env are equally sensitive.

Upstream 2.7.2 has a known setup redirect bug (#4907): revisiting /setup/ after the owner exists returns HTTP 500. Use /account/login/ for ordinary access. The recipe does not patch upstream authentication or treat that error as a successful protection check; the smoke test checks the actual closed-signup configuration instead.

The amd64 recovery scenario creates an owner through the native CSRF form, signs in, stores a private recipe with an ingredient, instructions, keyword and uploaded image, deletes all three volumes and verifies restored data and the stored image hash. arm64, meal-plan timing, shopping synchronization, URL imports, identity providers, mail, AI and mobile clients remain untested. Five CRITICAL package findings across the pinned images remain visible in the security snapshot.

Allow at least 1 CPU, 1 GiB RAM and 5 GiB disk; 2 GiB RAM gives more room for PostgreSQL and two Gunicorn workers. These are deployment estimates, not a load-test guarantee. Home and VPS deployment are supported after safe bootstrap; the reliable level remains partial because external monitoring, resource limits and backup scheduling are operator tasks.

Sources: Docker installation, configuration, authentication, SAML provider, and 2.7.2 release notes.

Update tracking

These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.

New releases

Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.

Current version 2.7.2Oct 29, 2026, 8:43 PM UTC

Breaking updates

A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.

Not checkedNot checked

compose.yaml

Upstream Compose changes relative to the verified recipe.

Upstream Compose is not trackedNot checked

Outdated Docker images

Pinned image tags checked for newer versions.

All images are currentOct 29, 2026, 8:43 PM UTC

From observation to action

Update plan

The recipe already pins the latest known release. The steps below show the evidence retained for that version.

2.7.22.7.2
  1. Open the release evidence

    Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.

  2. Review the version boundary

    The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.

  3. Prepare image-tag changes

    No image-tag change is currently planned.

  4. Follow the documented migration shape

    Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.

  5. Protect the rollback point

    No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.

  6. Verify the updated recipe

    An exact Fully verified check for this target was recorded on 2026-10-29.

Read the update procedure in the installation guide ↓

Specifications

Security and exposure

Installation levels

The status shows how fully the published recipe meets each level's requirements.

Ready

For home

A minimal setup for a local network or access through a VPN.

  • Single server
  • Local access
  • Minimum components

The recipe covers every requirement at this level.

Ready

For a VPS

A public deployment with protected access and backups.

  • Domain
  • HTTPS via reverse proxy
  • Off-server backup

The recipe covers every requirement at this level.

Partial

Reliable

A setup designed for predictable operations and observability.

  • Separate database
  • Healthcheck
  • External monitoring
  • CPU and memory limits

You still need to add: external monitoring, cpu and memory limits.

Tailored to your server

Configuration generator

Answer five questions and download a ready-to-run Tandoor bundle.

Runs locally
01 Do you have a domain?
02 Which reverse proxy?
03 Server architecture
04 Where should data be stored?

Use an absolute path for a separate disk, such as /srv/tandoor.

05 Is VPN access required?

Your bundle is ready

The archive is created in your browser. Your answers are never sent anywhere.

The same files as one shell script, written in plain heredocs so you can read the whole thing before it runs. It checks Docker, the architecture, memory, disk and the ports it needs, and writes nothing until you add --apply.

Screenshots

Quick start

Read the full guide first: it explains external access, backups, and data locations.

Prepare settingscp .env.example .env
Launchdocker compose up -d
Check statusdocker compose ps

Step-by-step guide

Install and maintain Tandoor

Ubuntu and Debian

Use Ubuntu 24.04/26.04 LTS or Debian 12/13 with Docker Engine and Compose v2.24 or newer, installed from Docker’s instructions. Run commands in this recipe directory. Install openssl; Python 3 and curl are needed for the smoke test. Docker access grants administrative control of the host.

cp .env.example .env
chmod 600 .env
openssl rand -hex 32  # TANDOOR_SECRET_KEY
openssl rand -hex 24  # TANDOOR_DB_PASSWORD

Paste different generated values into .env. The web entrypoint rejects template secrets and a secret key shorter than 50 characters before starting HTTP. The owner password is created through the native setup form. Keep the port on loopback until that owner exists.

docker compose --env-file .env config --quiet
docker compose --env-file .env pull
docker compose --env-file .env up -d --wait --wait-timeout 600
docker compose ps --all

VPS and first owner

Leave the public proxy closed. Open a tunnel from your computer:

ssh -L 8089:127.0.0.1:8089 user@server

Visit http://localhost:8089/setup/ and create the owner with a unique password, then use http://localhost:8089/account/login/. Setup creates the initial space and a placeholder email address; configure SMTP before verifying a real address or relying on email password recovery. Public signup stays disabled. Do not revisit /setup/: upstream 2.7.2 has a known redirect error there after a user exists. Enable a firewall, operating-system updates and encrypted off-server backups before opening HTTPS.

Trusted LAN or VPN

Bootstrap over the tunnel first. Then replace 127.0.0.1 in the web port mapping with the server’s private address. Add that address to TANDOOR_ALLOWED_HOSTS, and set TANDOOR_CSRF_TRUSTED_ORIGINS to its exact origin, for example http://192.168.1.10:8089. Recreate the services and restrict access to trusted clients or a VPN. Plain HTTP still carries credentials and household data unencrypted.

Domain and HTTPS

Point recipes.example.com at the server. Keep the app on loopback, set TANDOOR_ALLOWED_HOSTS=localhost,127.0.0.1,recipes.example.com and TANDOOR_CSRF_TRUSTED_ORIGINS=https://recipes.example.com, then run docker compose up -d --wait --wait-timeout 600. Forward the original Host and X-Forwarded-Proto. Tandoor trusts the forwarded protocol header, so prevent untrusted direct access to that backend. Keep remote-user authentication disabled unless a trusted authenticating proxy strips all client-supplied identity headers.

Caddy, Nginx and Traefik

proxy/Caddyfile obtains HTTPS certificates and routes the domain to loopback. proxy/nginx.conf needs your certificate paths; run nginx -t before reload. proxy/traefik.yaml is dynamic file-provider configuration for an existing Traefik installation with its certificate resolver. The examples assume a proxy on the Docker host. A containerized proxy needs a controlled shared network or reachable host address; its loopback refers to itself. No additional static/media proxy is needed: the official app image includes Nginx.

Data and every setting

Three project-scoped volumes are created: database at /var/lib/postgresql, media at /opt/recipes/mediafiles, and static at /opt/recipes/staticfiles. PostgreSQL holds recipes, users, spaces, meal plans, shopping lists and integration settings. Images and user uploads live in media. Static assets are generated at each startup and are not user data. The initializer assigns the media/static roots to UID/GID 65534; the application runs without root. Do not change the UID or replace volume ownership blindly.

Variable Meaning
TANDOOR_VERSION Pinned app version, 2.7.2.
TANDOOR_PORT Loopback web port, 8089.
TANDOOR_SECRET_KEY Random Django secret of at least 50 characters; preserve it with backups.
TANDOOR_DB_PASSWORD Unique PostgreSQL password set before the first start; changing only .env does not alter the existing database role password.
TANDOOR_ALLOWED_HOSTS Comma-separated hostnames/IPs without schemes or ports; include loopback for healthchecks.
TANDOOR_CSRF_TRUSTED_ORIGINS Comma-separated exact origins including scheme and any nonstandard port; no wildcard.
TZ IANA application timezone, UTC by default.

Scripts additionally accept FOSSARY_ENV_FILE, COMPOSE_PROJECT_NAME and TANDOOR_BACKUP_DIR, defaulting to .env, project tandoor and backups/. If you launched with a custom docker compose -p, export that same COMPOSE_PROJECT_NAME for backup and restore. SMTP, AI, external storage and identity providers remain unconfigured; the recipe does not require their keys.

Backup

bash backup.sh

Wait for initialization first. The script stops the application, dumps PostgreSQL and archives the full media volume, .env and Compose. It restarts the app only if it was running. Archives use private permissions and unique filenames; encrypt an off-server copy and rehearse recovery. Recipe exports alone do not preserve accounts, meal plans, shopping lists or all settings. Static files are regenerated and excluded from the archive.

Restore — replaces current data

Use trusted archives and the matching app/database versions and secrets. On a fresh server, prepare the matching configuration without starting the application:

docker compose up -d --wait database
docker compose create --no-deps tandoor initialize
bash restore.sh /absolute/path/tandoor-<timestamp>.tar

The restore validates archive members, creates a safety backup, stops writers, recreates the application database, replaces media and waits for health. .env from the archive is not activated automatically. Verify login, recipe instructions, images, meal plans and shopping lists before reopening access. If restoration fails, keep writers stopped and inspect logs before retrying. A different server also needs corrected host/origin settings.

Update

Read the release notes and configuration guide. Save the old .env, Compose and a verified backup, stop the app, select a reviewed pinned TANDOOR_VERSION, then restart:

bash backup.sh
docker compose stop tandoor
# Edit TANDOOR_VERSION in .env; keep the PostgreSQL major version unchanged.
docker compose pull tandoor
docker compose up -d --wait --wait-timeout 600
docker compose logs --tail=100 tandoor

Startup migrates PostgreSQL and rebuilds static assets. Check recipes, media, permissions, meal plans and shopping lists. PostgreSQL major upgrades require a separate export/import plan; never reuse a data directory with a different major image.

Rollback

  1. Close external access and stop tandoor.
  2. Restore the previous .env and Compose, preserving the matching secret and the database major version; set the old TANDOOR_VERSION.
  3. Pull that image, start only the database, and run docker compose create --no-deps tandoor initialize. Keep the old app stopped over the newer schema.
  4. Run bash restore.sh /path/to/pre-upgrade.tar. The safety copy preserves the current state before the old database and media replace it; static assets are regenerated.
  5. Verify login, recipes, images, meal plans and shopping lists before reopening the proxy. Changing only the app image does not reverse migrations.

Stop or erase

docker compose down preserves the volumes. After verifying an external backup, docker compose down --volumes --remove-orphans destroys the database, all uploads and static assets. Remove .env and local backups separately when they are no longer needed.

Report a recipe problem

Tell us how it went on your server

Opens a prefilled GitHub issue. A GitHub account is required; submitting the issue makes it public.

Troubleshooting

Troubleshooting Tandoor

Start with docker compose ps --all, docker compose logs --tail=200 tandoor database initialize, docker compose --env-file .env config --quiet, and curl -i http://127.0.0.1:8089/accounts/login/. Keep secrets, session cookies and household data out of public reports.

  • The app restarts before HTTP starts: replace both template secrets. TANDOOR_SECRET_KEY must be at least 50 characters. Preserve an existing key when recovering an established installation.
  • PostgreSQL rejects the password: an initialized role keeps its password. Restore the original .env value or deliberately change the role password; deleting volumes destroys user data.
  • Permission denied in media/static: inspect the initializer and volume mounts. The recipe runs as UID/GID 65534 and prepares the volume roots before startup. Restore matching ownership instead of making directories world-writable. Static assets are regenerated and do not need to be copied from an older image.
  • HTTP 400 or CSRF failure: TANDOOR_ALLOWED_HOSTS takes hostnames/IPs without scheme or port; TANDOOR_CSRF_TRUSTED_ORIGINS takes complete origins. Include the actual hostname, scheme and any nonstandard port, forward Host/protocol correctly, then recreate the app. Keep the backend reachable only through trusted paths.
  • HTTP 500 at /setup/ after creating the owner: this is upstream #4907, still present in 2.7.2. Use /account/login/; do not delete the database or repeat owner creation. The normal headless login and recipe API work independently of that old redirect.
  • Forgotten owner password without SMTP: use the upstream Django command docker compose exec tandoor /opt/recipes/venv/bin/python manage.py changepassword <username>. It prompts interactively; keep the new password out of command history. Configure SMTP before relying on email verification/reset.
  • Images are missing after recovery: restore PostgreSQL and the complete media archive together, with the corresponding secret and version. Inspect app/Nginx logs and file ownership. Do not confuse regenerated static files with user media.
  • A private image opens without login: media are served directly by Nginx. The recipe does not promise private image URLs; remove sensitive uploads rather than relying on the recipe’s private flag for file authorization.
  • No AI import, external storage or identity provider: these are intentionally unconfigured. They are not required for ordinary recipe storage, meal planning or shopping lists. Read the official integration instructions before enabling them.
  • An upgrade fails: close access, keep the previous configuration and restore the pre-upgrade archive with its matching image. Do not force an older app over the newer schema or replace PostgreSQL with a different major version over its existing directory.

References: Docker guide, configuration, authentication and upstream issues.

Official sources

Latest release: 2026-10-27 · GitHub Stars: 8,703 · metadata checked Oct 29, 2026, 8:43 PM UTC

Disclaimer

Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.

A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.