--- name: mealie-setup description: Deploy and configure Mealie (self-hosted meal planner/recipe manager) — Docker deployment, nginx reverse proxy, batch recipe import, mobile app setup, and common pitfalls. version: 1.0.0 author: Hermes Agent license: MIT platforms: [linux] metadata: hermes: tags: [mealie, meal-planner, recipes, docker, self-hosting, nginx] --- # Mealie Setup Mealie is a self-hosted recipe manager and meal planner. Web-based with PWA support for mobile, plus third-party Android/iOS apps (Ghee, Mealient, MealieSwift). ## Quick Deploy ```bash docker run -d \ --name mealie \ --restart unless-stopped \ -p 127.0.0.1:9925:9000 \ -v /path/to/data:/app/data \ ghcr.io/mealie-recipes/mealie:latest ``` **⚠️ PORT PITFALL:** Mealie listens on **port 9000** internally, not 9925. The `-p` mapping must be `host:9000`, not `host:9925`. If you map `9925:9925`, the container starts but the proxy returns empty responses. ## BASE_URL (critical for invite links) Without `BASE_URL`, Mealie generates invite links, password reset URLs, and notification links using `http://localhost:8080` — they'll always fail. Set it to the external URL including the port: ```bash docker run -d \ --name mealie \ ... -e BASE_URL=https://your.domain:3449 \ ghcr.io/mealie-recipes/mealie:latest ``` If invite links show "refused to connect", you forgot `BASE_URL`. Re-create the container with it set — data survives in the volume. ## Nginx Reverse Proxy Mealie needs to be at the root path — subpath proxying (`/mealie/`) is not supported (JS framework limitation). ```nginx server { listen PORT ssl; server_name your.domain; ssl_certificate /etc/letsencrypt/live/your.domain/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your.domain/privkey.pem; client_max_body_size 50M; location / { proxy_pass http://127.0.0.1:9925; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 86400; } } ``` ## First Visit & Setup - First visitor creates the admin account (no default credentials). - Seed **Foods** and **Units** databases: User menu → Manage Data → ensure orange button shows "Foods" → click Seed → do the same for Units. This enables smart ingredient parsing and shopping list generation. ## Mobile Access - **PWA** (recommended, free): Open Mealie in Chrome Android → ⋮ → "Add to Home Screen". Full-screen with offline caching. All features (meal planner, shopping lists, cook mode) are free — unlike third-party apps. - **Ghee** (Play Store): **⚠️ Meal planner is a paid feature.** Recipe browsing and shopping lists work for free, but the meal calendar requires an in-app purchase. If "server unreachable" with SSL URL, try `http://192.168.50.X:9925` first — hairpin NAT or Android cleartext policies often block HTTPS on non-standard ports from apps. - **Mealient** (open source): GitHub release APK. Free, no paywalls. ## Batch Importing Recipes Recipes are imported by URL via the scraper API. See `references/batch-import.md` for a curl/Python script. **Scraper compatibility:** see `references/scraper-sites.md` for which recipe sites work reliably and workarounds for blocked sites. **Recipe management (list, filter, delete):** see `references/recipe-management.md` for bulk CRUD via API — useful when you need to remove recipes by keyword (e.g., all pork/bacon recipes) or inspect what's in the library. ## Managing Recipes Search, filter, and delete recipes in bulk via the API — useful for dietary cleanup, duplicate removal, and post-import curation. See `references/recipe-management.md` for the full API workflow (list, keyword-search, inspect ingredients, delete). ## Data Location - Container data: `/app/data` (bind-mount to persistent storage) - Database: SQLite inside the data directory - Backups: copy the data directory