Skip to main content

Tandoor

A self-hosted recipe management and meal planning application.

Tandoor Recipes is an open-source recipe manager with a rich web UI for storing, organizing, and planning recipes. It supports importing from URLs, nutritional calculations, shopping list generation, and meal planning. Self-hosting keeps your recipe collection private without subscription fees.

Alternatives considered

Cloud Hosted

ToolOpen SourceFree TierMonthly Cost
PaprikaNoNo$4.99–$29.99 one-time
AnyListNoLimited$1.25/mo (annual)

Self Hosted

ToolOpen SourceFull FeaturesNotes
MealieYesYesSimpler recipe manager with meal planning

Installation

Architecture

  • Deployment: Single tandoor deployment in the tandoor namespace
  • Image: vabene1111/recipes:2.6.0 (digest-pinned)
  • Database: CNPG PostgreSQL cluster with Longhorn-encrypted PVCs
  • Storage: Two Longhorn PVCs — tandoor-media and tandoor-static
  • Networking: ClusterIP service on port 8080, HTTPRoute via internal gateway

Security

  • Runs as runAsUser: 0, runAsNonRoot: false
  • Longhorn PVCs encrypted at rest via SOPS-managed keys

Updates

Managed by Renovate. Image is digest-pinned.

Data Management

  • Database: CNPG PostgreSQL cluster (Longhorn-encrypted PVCs)
  • PVCs: tandoor-media and tandoor-static (both Longhorn-encrypted, k8up.io/backup: "true")
  • Backups: k8up Schedule backs up CNPG Longhorn PVCs and app data to Hetzner S3. CNPG annotated with k8up.io/backupcommand: pg_dump.

User Management

No OIDC env vars in manifests. User accounts managed through the Tandoor admin UI. Multi-user sharing supported natively.

Configuration Management

  • Database credentials injected from CNPG-generated secret
  • App secret key and other credentials from SOPS-encrypted secret

Administration

Usage

Import recipes by pasting URLs from cooking websites — Tandoor automatically extracts ingredients and steps. Organize recipes with tags and categories. Generate weekly meal plans and export shopping lists. Multiple users can share the same recipe collection.

Cluster-specific deviations from the above live in the per-cluster README — see k8s/apps/talos/tandoor/README.md.

Cluster Deployment

Tandoor — Talos cluster

Cluster-specific notes only. General product info, "why we use it", and alternatives live in docusaurus/docs/apps/tandoor.mdx.

Deviations from defaults

Defaults live in docusaurus/docs/apps/tandoor.mdx — document anything this cluster does differently here, with a one-line reason.

Kubernetes Metadata
  • Image: vabene1111/recipes:2.6.9@sha256:969c5b3552ffbf18a6f82b3ad5babbae89bfbd30ab6e3195fd3c158bcf3062ed
Rendered manifests (kustomize build)
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kustomize.toolkit.fluxcd.io/force: enabled
labels:
app.kubernetes.io/instance: tandoor
app.kubernetes.io/name: tandoor
name: tandoor
namespace: tandoor
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: tandoor
app.kubernetes.io/name: tandoor
ingress: public
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/instance: tandoor
app.kubernetes.io/name: tandoor
ingress: public
spec:
containers:
- env:
- name: DB_ENGINE
value: django.db.backends.postgresql
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: cnpg-app
- name: POSTGRES_HOST
valueFrom:
secretKeyRef:
key: host
name: cnpg-app
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
key: dbname
name: cnpg-app
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
key: user
name: cnpg-app
- name: POSTGRES_PORT
valueFrom:
secretKeyRef:
key: port
name: cnpg-app
- name: EMAIL_HOST
valueFrom:
secretKeyRef:
key: host
name: allinkl-smtp-credentials
- name: EMAIL_PORT
valueFrom:
secretKeyRef:
key: port
name: allinkl-smtp-credentials
- name: EMAIL_HOST_USER
valueFrom:
secretKeyRef:
key: username
name: allinkl-smtp-credentials
- name: EMAIL_HOST_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: allinkl-smtp-credentials
- name: DEFAULT_FROM_EMAIL
valueFrom:
secretKeyRef:
key: from
name: allinkl-smtp-credentials
- name: EMAIL_USE_TLS
value: '1'
- name: EMAIL_USE_SSL
value: '1'
- name: SECRET_KEY
value: keepthisasecretinprod
envFrom:
- secretRef:
name: tandoor
image: vabene1111/recipes:2.6.9@sha256:969c5b3552ffbf18a6f82b3ad5babbae89bfbd30ab6e3195fd3c158bcf3062ed
livenessProbe:
failureThreshold: 5
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 2
name: tandoor
ports:
- containerPort: 80
name: web
protocol: TCP
readinessProbe:
failureThreshold: 5
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 2
volumeMounts:
- mountPath: /opt/recipes/staticfiles/
name: tandoor-static
- mountPath: /opt/recipes/mediafiles/
name: tandoor-media
volumes:
- name: tandoor-static
persistentVolumeClaim:
claimName: tandoor-static
- name: tandoor-media
persistentVolumeClaim:
claimName: tandoor-media