Mealie Recipe Manager: Deploy a Self-Hosted Meal Planner using Docker
Learn how to self-host Mealie using Docker Compose. Store recipes, plan meals, and auto-import data from the web.
Mealie Recipe Manager: Deploy a Self-Hosted Meal Planner using Docker
Core Architecture and Backend Database Selection
To deploy Mealie for production or active multi-user environments, choosing the correct backend database is critical. Mealie supports two storage backends: SQLite and PostgreSQL.
SQLite vs. PostgreSQL Backend
- SQLite (Default): Best for single-user instances or low-resource virtual private servers (VPS). It operates serverless with minimal overhead, storing all data in a single file inside the
/app/datavolume. However, it lacks robust concurrent write handling and is prone to database locks under high request volume. - PostgreSQL: Highly recommended for multi-user setups, automation integrations, or scenarios requiring database backups independent of container state. PostgreSQL handles simultaneous transactions efficiently, avoiding file-lock conditions.
We will focus on the PostgreSQL-backed deployment configuration to guarantee data integrity and scale.
Mealie Volume Bindings and Scraper Integration
Mealie relies on volume mounts to persist its database, uploaded media assets (recipe images), and search indices. In addition, its integrated recipe scraper parses standard schema structures (e.g., Schema.org Microdata or JSON-LD) using a python backend component.
Essential Bindings
/app/data: Stores SQLite databases (if selected), search indices, customized themes, backups, and user settings./app/data/media: Isolates image uploads and generated assets for easy caching and proxy configurations.
Recipe Scraper Core Details
Mealie's automatic importer uses python libraries to parse incoming URLs, fetch content, and extract structured recipe details (ingredients, instructions, preparation times, and calorie details). It respects robots.txt permissions but performs requests directly from the container, requiring outbound internet access (port 80/443).
Complete Docker Compose Configuration
The following docker-compose.yml provides a production-ready blueprint. It configures a Mealie frontend/backend container coupled with a PostgreSQL database, secured within an isolated Docker network.
version: '3.8'
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v1.12.0
container_name: mealie
restart: always
environment:
# Database Settings
- DB_ENGINE=postgres
- POSTGRES_USER=mealie_user
- POSTGRES_PASSWORD=mealie_secure_pass_99
- POSTGRES_DB=mealie_db
- POSTGRES_SERVER=mealie-db
- POSTGRES_PORT=5432
# Application Settings
- TZ=UTC
- BASE_URL=https://mealie.yourdomain.com
- ALLOW_SIGNUP=false
# Security & Initialization
- PUID=1000
- PGID=1000
volumes:
- mealie-data:/app/data
ports:
- "9000:9000"
depends_on:
mealie-db:
condition: service_healthy
networks:
- mealie-network
mealie-db:
image: postgres:15-alpine
container_name: mealie-db
restart: always
environment:
- POSTGRES_USER=mealie_user
- POSTGRES_PASSWORD=mealie_secure_pass_99
- POSTGRES_DB=mealie_db
volumes:
- mealie-db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mealie_user -d mealie_db"]
interval: 10s
timeout: 5s
retries: 5
networks:
- mealie-network
volumes:
mealie-data:
mealie-db-data:
networks:
mealie-network:
driver: bridge
Step-by-Step Server Setup and Deployment
Deploying the stack requires preparing your directory structure and setting up the environment.
Step 1: Directory Preparation
Create a dedicated project folder and verify the directory permissions:
mkdir -p /opt/mealie && cd /opt/mealie
Step 2: Environment Variables
Create a .env file to store sensitive credentials instead of hardcoding them within docker-compose.yml:
POSTGRES_PASSWORD=your_highly_secure_random_db_password
BASE_URL=https://mealie.yourdomain.com
TZ=America/New_York
Update your docker-compose.yml environment block to reference these variable placeholders:
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- BASE_URL=${BASE_URL}
- TZ=${TZ}
Step 3: Run the Stack
Run the following command to boot the services in detached mode:
docker compose up -d
Verify that both containers are running and healthy:
docker compose ps
Reverse Proxy Configuration with Nginx
To expose Mealie to the internet securely over HTTPS, configure Nginx as a reverse proxy.
Nginx Virtual Host Configuration
Create a configuration file at /etc/nginx/sites-available/mealie:
server {
listen 80;
server_name mealie.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name mealie.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/mealie.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mealie.yourdomain.com/privkey.pem;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'";
# Proxy Configuration
location / {
proxy_pass http://127.0.0.1:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
# Max upload size for recipe images
client_max_body_size 20M;
}
}
Enable the configuration and reload Nginx:
ln -s /etc/nginx/sites-available/mealie /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
Recipe Import and Scraper Mechanics
Once Mealie is online, navigate to the web interface to configure the scraper.
Using the Recipe Parser
Mealie relies on the standard JSON-LD schema pattern embedded within websites. To import a recipe: 1. Navigate to Recipes -> Import from URL. 2. Paste the target URL (e.g., a recipe from a cooking blog). 3. Click Import.
Troubleshooting Scrapers
- Forbidden / Cloudflare blocks: Some recipes sit behind Cloudflare protection or geo-blocking. If a scrape fails, verify container internet access or set up a custom scraper user-agent header if supported.
- Manual Parsing fallback: When schema parser fails to locate metadata, Mealie allows you to manually copy and paste raw text, using its built-in parser to match ingredients to amounts.
Automated Backups and Maintenance
Protect your database and media files from VPS storage failures by scheduling automated backups.
Creating a Backup Script
Create /opt/mealie/backup.sh:
#!/bin/bash
BACKUP_DIR="/opt/mealie/backups"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
mkdir -p "$BACKUP_DIR"
# Dump database
docker exec mealie-db pg_dump -U mealie_user mealie_db > "$BACKUP_DIR/mealie_db_$TIMESTAMP.sql"
# Archive media data
tar -czf "$BACKUP_DIR/mealie_media_$TIMESTAMP.tar.gz" -C /var/lib/docker/volumes/mealie_mealie-data/_data .
# Delete backups older than 7 days
find "$BACKUP_DIR" -type f -mtime +7 -delete
Make the script executable:
chmod +x /opt/mealie/backup.sh
Scheduling with Cron
Open crontab:
crontab -e
Add the following line to run backups daily at 2:00 AM:
0 2 * * * /opt/mealie/backup.sh