Mealie on Kubernetes: Self-Hosting Recipes with OIDC Lessons

The most important deployment in my homelab is the recipe manager, because downtime at dinnertime has consequences that Kubernetes cannot remediate.

Mealie on Kubernetes: Self-Hosting Recipes with OIDC Lessons

Of all the services in my homelab, Mealie is the one my wife actually uses. That makes it the most important deployment in the cluster, because downtime on Mealie gets noticed at dinnertime, and dinnertime outages have consequences that Kubernetes cannot remediate.

What Mealie Does

Mealie is a self-hosted recipe manager. You paste a URL from a cooking website, it scrapes the recipe (stripping the life story, ads, and SEO padding), and stores a clean version with ingredients, steps, and nutritional info. You can organize recipes into categories and tags, plan meals on a calendar, and generate shopping lists.

The killer feature is the recipe scraper. Cooking websites are famously hostile to readers: a 300-word recipe wrapped in 3000 words of narrative, surrounded by auto-playing video ads. Mealie extracts the recipe and presents it cleanly. Once imported, the recipe loads instantly from local storage with no ads, no cookie banners, and no "jump to recipe" button because there is nothing to jump past.

The Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mealie
  namespace: mealie
spec:
  replicas: 1
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
      - name: mealie
        image: ghcr.io/mealie-recipes/mealie:v2.6.0
        ports:
        - containerPort: 9000
        env:
        - name: ALLOW_SIGNUP
          value: "false"
        - name: BASE_URL
          value: https://mealie.naviauxlab.com
        - name: DB_ENGINE
          value: sqlite
        volumeMounts:
        - name: data
          mountPath: /app/data
        resources:
          requests:
            cpu: 50m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 256Mi
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: mealie-data

Mealie uses SQLite by default (PostgreSQL is optional but unnecessary at household scale). The /app/data directory contains the database, uploaded images, and backup files. A single Longhorn PVC handles everything.

Resource usage is moderate: heavier than Miniflux or Homepage because Mealie runs a Python backend with recipe parsing and image processing. The 256Mi memory limit gives enough headroom for recipe imports that involve downloading and processing images.

The OIDC Story

Mealie supports OIDC authentication. In theory, you configure it with your Authentik provider, and users log in via SSO. In practice, the implementation had issues when I set it up.

The problems I hit:

  • Token refresh failures. Mealie's OIDC session handling would occasionally fail to refresh the token, logging users out mid-session. This is particularly annoying when you're cooking: hands covered in flour, phone propped up showing a recipe, and suddenly you're looking at a login page.
  • User attribute mapping. Mealie expects specific claims from the OIDC token (email, name, groups) and the mapping wasn't straightforward with Authentik's default scope configuration. It took several rounds of adjusting Authentik's property mappings to get it right.
  • Fallback behavior. When OIDC auth fails, Mealie doesn't gracefully fall back to local auth. It shows an error page. For a recipe app that needs to work reliably at dinnertime, this isn't acceptable.

My solution: ForwardAuth via Traefik instead of native OIDC. Authentik handles authentication at the reverse proxy layer. Mealie sees pre-authenticated requests and doesn't need to manage OIDC tokens at all. The SSO experience is the same for the user (they log in once via Authentik and access all services), but the authentication flow is handled by infrastructure, not by the application.

This is a pattern I've applied to several apps where the OIDC implementation is immature: let Traefik and Authentik handle auth, and treat the app as a trusted backend behind the proxy.

Recipe Management Workflow

The daily workflow is simple:

  1. Find a recipe online (from a cooking blog, Reddit, YouTube description)
  2. Paste the URL into Mealie's import dialog
  3. Mealie scrapes the recipe, extracts ingredients and steps
  4. Review and adjust (serving sizes, ingredient substitutions, personal notes)
  5. Categorize (dinner, lunch, baking, etc.) and tag (quick, meal-prep, vegetarian)

For meal planning, Mealie has a calendar view where you drag recipes onto days. This generates a consolidated shopping list across all planned meals, which you can check off while shopping. The shopping list groups items by category (produce, dairy, pantry) for efficient grocery store navigation.

Backup and Data Safety

Mealie has built-in backup functionality that exports recipes, settings, and user data to a zip file. The Longhorn PVC provides volume-level snapshots and cross-node replication. Combined with the NFS backup CronJob that backs up the full data directory, recipes survive any single-node failure.

I also export recipes periodically to a JSON file stored in Git. This is a manual process (Mealie's API supports bulk export), but it means the recipes themselves, the actual content, are version-controlled independently of the database. Even if Mealie, Longhorn, and the NFS backups all fail simultaneously, the recipes are in a Git repository.

Why Not Just Use Paprika / Notion / Google Keep?

Paprika is a good app. I used it for years. But it's a native app with its own sync service, and the data is locked in its format. Exporting is possible but lossy. Mealie stores recipes in a standard format that's queryable via API.

Notion / Google Keep work for storing recipes but have no recipe-specific features: no ingredient parsing, no serving adjustment, no nutritional calculation, no shopping list generation. They're general-purpose note-taking tools being used for a specific purpose they weren't designed for.

The self-hosted advantage isn't about features. Paprika probably has a better mobile experience than Mealie. It's about integration and control. Mealie's API lets me query recipes programmatically. The data is in SQLite, which I can inspect directly. And there's no subscription fee, no data sharing with a third party, and no risk of the service shutting down.

Lessons

Optimize for the non-technical user. My wife doesn't care about Kubernetes. She cares that the recipe loads when she's cooking. ForwardAuth over OIDC was the right call because it's more reliable, even though OIDC is more "correct" architecturally. Reliability beats elegance for user-facing services.

Recipe apps need to work offline-ish. When you're cooking and your phone screen turns off, the recipe should still be there when you tap it. Mealie's PWA handles this reasonably well: once loaded, the recipe stays in the browser cache. But a true offline mode (service worker with local storage) would be better.

The scraper is the feature. Everything else Mealie does (meal planning, shopping lists, categories) is nice but not essential. The recipe scraper that strips a cooking blog down to ingredients and steps is what makes it worth running. If the scraper breaks (and it does, occasionally, when cooking sites change their markup), the app loses most of its value.

Family approval is the real SLA. Uptime Kuma can show 99.9% availability. If the 0.1% downtime happens at 6 PM on a Tuesday when dinner is being prepared, the perceived reliability is zero. For family-facing services, the bar is higher than any monitoring dashboard suggests.