Intuitive self-hosted recipe management app designed to be the best recipe management experience on the web.
| Port | 9000 |
| Registry | ghcr.io/daemonless/mealie |
| Source | https://github.com/mealie-recipes/mealie |
| Website | https://mealie.io/ |
| Tag | Description | Best For |
|---|---|---|
latest |
Upstream Binary. Built from official release. | Most users — recommended. |
Before deploying, ensure your host environment is ready. See the Quick Start Guide for host setup instructions.
Database. Where the app keeps its data. The default needs nothing else running.
A file in the app's config folder. Right for one person, nothing extra to run.
1. Save as .env and fill in what is empty:
PUID=1000
PGID=1000
TZ=UTC2. Save as compose.yaml:
name: mealie
services:
mealie:
image: ghcr.io/daemonless/mealie:latest
container_name: mealie
restart: unless-stopped
environment:
- BASE_URL=http://localhost:9000
- PUID=1000
- PGID=1000
- TZ=UTC
# The database, from the Database choice; sqlite needs nothing else.
- DB_ENGINE=${DB_ENGINE:-sqlite}
- POSTGRES_SERVER=${POSTGRES_SERVER:-}
- POSTGRES_PORT=${POSTGRES_PORT:-5432}
- POSTGRES_USER=${POSTGRES_USER:-}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-}
- POSTGRES_DB=${POSTGRES_DB:-}
volumes:
- /path/to/containers/mealie:/config
ports:
- "9000:9000"Then run podman-compose up -d.
One more container, its data in its own folder. For a household, or an app that wants it.
1. Save as .env and fill in what is empty:
PUID=1000
PGID=1000
TZ=UTC
# Database: PostgreSQL
DB_ENGINE=postgres
POSTGRES_SERVER=postgres
POSTGRES_PORT=5432
POSTGRES_USER=mealie
POSTGRES_PASSWORD= # set one
POSTGRES_DB=mealie
DATABASE_LOCATION=/containers/mealie/postgres2. Save as compose.yaml:
name: mealie
services:
mealie:
depends_on: [postgres]
image: ghcr.io/daemonless/mealie:latest
container_name: mealie
restart: unless-stopped
environment:
- BASE_URL=http://localhost:9000
- PUID=1000
- PGID=1000
- TZ=UTC
# The database, from the Database choice; sqlite needs nothing else.
- DB_ENGINE=${DB_ENGINE:-sqlite}
- POSTGRES_SERVER=${POSTGRES_SERVER:-}
- POSTGRES_PORT=${POSTGRES_PORT:-5432}
- POSTGRES_USER=${POSTGRES_USER:-}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-}
- POSTGRES_DB=${POSTGRES_DB:-}
volumes:
- /path/to/containers/mealie:/config
ports:
- "9000:9000"
postgres:
image: ghcr.io/daemonless/postgres:17
restart: always
annotations:
org.freebsd.jail.allow.sysvipc: "true"
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- "${DATABASE_LOCATION}:/var/lib/postgresql/data"Then run podman-compose up -d.
A database you already run, here or on another machine. Nothing extra runs; you give the address and the account.
1. Save as .env and fill in Kind, Host, Port, User, Password, Database:
PUID=1000
PGID=1000
TZ=UTC
# Database: Your own
DB_ENGINE= # Kind: postgres
POSTGRES_SERVER= # Host
POSTGRES_PORT= # Port
POSTGRES_USER= # User
POSTGRES_PASSWORD= # Password
POSTGRES_DB= # Database2. Save as compose.yaml:
name: mealie
services:
mealie:
image: ghcr.io/daemonless/mealie:latest
container_name: mealie
restart: unless-stopped
environment:
- BASE_URL=http://localhost:9000
- PUID=1000
- PGID=1000
- TZ=UTC
# The database, from the Database choice; sqlite needs nothing else.
- DB_ENGINE=${DB_ENGINE:-sqlite}
- POSTGRES_SERVER=${POSTGRES_SERVER:-}
- POSTGRES_PORT=${POSTGRES_PORT:-5432}
- POSTGRES_USER=${POSTGRES_USER:-}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-}
- POSTGRES_DB=${POSTGRES_DB:-}
volumes:
- /path/to/containers/mealie:/config
ports:
- "9000:9000"Then run podman-compose up -d.
.env:
# .env
DIRECTOR_PROJECT=mealie
BASE_URL=http://localhost:9000
PUID=1000
PGID=1000
TZ=UTC
DB_ENGINE=sqlite
POSTGRES_SERVER=
POSTGRES_PORT=5432
POSTGRES_USER=
POSTGRES_PASSWORD=<POSTGRES_PASSWORD>
POSTGRES_DB=
appjail-director.yml:
# appjail-director.yml
options:
- virtualnet: ':<random> default'
- nat:
services:
mealie:
name: mealie
options:
- container: 'args:--pull'
- expose: '9000:9000 proto:tcp'
oci:
user: root
environment:
- BASE_URL: !ENV '${BASE_URL}'
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
- DB_ENGINE: !ENV '${DB_ENGINE}'
- POSTGRES_SERVER: !ENV '${POSTGRES_SERVER}'
- POSTGRES_PORT: !ENV '${POSTGRES_PORT}'
- POSTGRES_USER: !ENV '${POSTGRES_USER}'
- POSTGRES_PASSWORD: !ENV '${POSTGRES_PASSWORD}'
- POSTGRES_DB: !ENV '${POSTGRES_DB}'
volumes:
- mealie: /config
volumes:
mealie:
device: '/containers/mealie'Makejail:
# Makejail
ARG tag=latest
OPTION container=boot
OPTION overwrite=force
OPTION from=ghcr.io/daemonless/mealie:${tag}
Save the files above, then run appjail-director up.
.env:
# .env
DIRECTOR_PROJECT=mealie
BASE_URL=http://localhost:9000
PUID=1000
PGID=1000
TZ=UTC
DB_ENGINE=postgres
POSTGRES_SERVER=mealie_postgres
POSTGRES_PORT=5432
POSTGRES_USER=mealie
POSTGRES_PASSWORD=
POSTGRES_DB=mealie
DATABASE_LOCATION=/containers/mealie/postgres
appjail-director.yml:
# appjail-director.yml
options:
- virtualnet: ':<random> default'
- nat:
services:
mealie:
name: mealie
options:
- container: 'args:--pull'
- expose: '9000:9000 proto:tcp'
oci:
user: root
environment:
- BASE_URL: !ENV '${BASE_URL}'
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
- DB_ENGINE: !ENV '${DB_ENGINE}'
- POSTGRES_SERVER: !ENV '${POSTGRES_SERVER}'
- POSTGRES_PORT: !ENV '${POSTGRES_PORT}'
- POSTGRES_USER: !ENV '${POSTGRES_USER}'
- POSTGRES_PASSWORD: !ENV '${POSTGRES_PASSWORD}'
- POSTGRES_DB: !ENV '${POSTGRES_DB}'
volumes:
- mealie: /config
postgres:
name: mealie_postgres
priority: 10
options:
- from: ghcr.io/daemonless/postgres:17
- template: !ENV '${PWD}/postgres-template.conf'
oci:
environment:
- POSTGRES_USER: !ENV '${POSTGRES_USER}'
- POSTGRES_PASSWORD: !ENV '${POSTGRES_PASSWORD}'
- POSTGRES_DB: !ENV '${POSTGRES_DB}'
volumes:
- database: /var/lib/postgresql/data
volumes:
mealie:
device: '/containers/mealie'
database:
device: !ENV '${DATABASE_LOCATION}'Makejail:
# Makejail
ARG tag=latest
OPTION container=boot
OPTION overwrite=force
OPTION from=ghcr.io/daemonless/mealie:${tag}
postgres-template.conf:
# The jail PostgreSQL runs in: SysV shared memory, which a jail does not
# get by default. ip4/ip6 are set here because the director's ip4_inherit
# option is a no-op in AppJail 5.5.0.
exec.start: "/bin/sh /etc/rc"
exec.stop: "/bin/sh /etc/rc.shutdown jail"
sysvmsg: new
sysvsem: new
sysvshm: new
mount.devfs
persist
ip4: inherit
ip6: inherit
Save the files above, then run appjail-director up.
.env:
# .env
DIRECTOR_PROJECT=mealie
BASE_URL=http://localhost:9000
PUID=1000
PGID=1000
TZ=UTC
DB_ENGINE=
POSTGRES_SERVER=
POSTGRES_PORT=
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_DB=
appjail-director.yml:
# appjail-director.yml
options:
- virtualnet: ':<random> default'
- nat:
services:
mealie:
name: mealie
options:
- container: 'args:--pull'
- expose: '9000:9000 proto:tcp'
oci:
user: root
environment:
- BASE_URL: !ENV '${BASE_URL}'
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
- DB_ENGINE: !ENV '${DB_ENGINE}'
- POSTGRES_SERVER: !ENV '${POSTGRES_SERVER}'
- POSTGRES_PORT: !ENV '${POSTGRES_PORT}'
- POSTGRES_USER: !ENV '${POSTGRES_USER}'
- POSTGRES_PASSWORD: !ENV '${POSTGRES_PASSWORD}'
- POSTGRES_DB: !ENV '${POSTGRES_DB}'
volumes:
- mealie: /config
volumes:
mealie:
device: '/containers/mealie'Makejail:
# Makejail
ARG tag=latest
OPTION container=boot
OPTION overwrite=force
OPTION from=ghcr.io/daemonless/mealie:${tag}
Save the files above, then run appjail-director up.
Access at: http://localhost:9000
| Variable | Default | Description |
|---|---|---|
BASE_URL |
http://localhost:9000 |
The base URL for the application (e.g. https://mealie.example.com) |
PUID |
1000 |
User ID for the application process |
PGID |
1000 |
Group ID for the application process |
TZ |
UTC |
Timezone for the container |
DB_ENGINE |
sqlite |
sqlite or postgres -- set by the Database choice |
POSTGRES_SERVER |
`` | PostgreSQL host (the PostgreSQL choice fills this in) |
POSTGRES_PORT |
5432 |
PostgreSQL port |
POSTGRES_USER |
`` | PostgreSQL user |
POSTGRES_PASSWORD |
<POSTGRES_PASSWORD> |
PostgreSQL password |
POSTGRES_DB |
`` | PostgreSQL database name |
| Path | Description |
|---|---|
/config |
Data directory (database, images) |
| Port | Protocol | Description |
|---|---|---|
9000 |
TCP | Web UI |
SQLite: No issues, just copy your data.
PostgreSQL: You cannot copy the postgres data directory between Linux and FreeBSD due to locale incompatibilities. Use pg_dump/pg_restore instead:
# On Linux
podman exec mealie-postgres pg_dump -U mealie mealie > mealie.sql
# On FreeBSD (start fresh postgres first, then restore)
cat mealie.sql | podman exec -i mealie-postgres psql -U mealie -d mealieSee daemonless/postgres README for details.
Architectures: amd64
User: bsd (UID/GID via PUID/PGID, defaults to 1000:1000)
Base: FreeBSD 15.1
Need help? Join our Discord community.