Your kitchen, organized. Mise is a self-hosted kitchen companion for households and restaurants: recipes, meal plans, pantry, and a shopping list in one place — with your data stored in an open format you can take anywhere.
- 📖 Recipes — stored as standard Schema.org Recipe JSON-LD, so your collection is portable to and from any compliant tool. Import from the community public library or add your own.
- 🗓️ Meal planning — plan the week, then generate a shopping list from it.
- 🧺 Pantry & shopping list — know what you have and what you need.
- 🔍 Full-text search — instant recipe search powered by Meilisearch.
- 👨👩👧 Households — multiple members share one kitchen; onboarding supports households and restaurants.
- 🤖 Works with Claude & ChatGPT — connect Mise as an MCP connector and talk to your kitchen: "send this recipe to Mise", "what's for dinner Thursday?", "add the missing ingredients to my list". See docs/mcp.md.
- 🔌 REST API — every feature is API-first; interactive OpenAPI docs ship
with the app at
/api/docs.
One variable is all you need — everything else has working defaults:
mkdir mise && cd mise
curl -fsSLO https://raw.githubusercontent.com/sidhantpanda/mise/main/compose.yml
echo "JWT_SECRET=$(openssl rand -hex 32)" > .env
docker compose up -dOpen http://localhost:3000, create your account, and you're cooking.
The stack is three containers: the Mise app (API + web on one port), Postgres, and Meilisearch. Postgres and Meilisearch are not published to the host — they're only reachable inside the compose network, which is why you don't need to configure credentials for them.
Set WEB_ORIGIN in .env to the URL you'll open Mise at:
WEB_ORIGIN=https://mise.example.comThat's the only switch: an https:// origin automatically gets Secure auth
cookies (put Mise behind any TLS-terminating reverse proxy), while an http://
origin (LAN or homelab without TLS) automatically doesn't — no cookie flags to
remember.
Once Mise is on a public HTTPS URL, add it as a custom connector in Claude (Settings → Connectors) or ChatGPT, pointing at:
https://mise.example.com/mcp
You'll be sent to Mise to sign in, pick which household the assistant may act on, and approve — no tokens to copy. Then you can say "send this recipe to Mise", "what's on the meal plan this week?", or "we're out of butter", and the assistant can search recipes, plan meals, and manage your pantry and shopping list. Full tool list and auth details: docs/mcp.md.
This is the one feature that needs
WEB_ORIGINto be right — it's the OAuth issuer, so it must be the public URL you actually open Mise at.
The compose file pulls the latest image on every start:
docker compose up -dYour data lives in named Docker volumes (postgres-data, meili-data) and
survives updates and docker compose down. Only docker compose down -v
deletes it.
Everything is optional except JWT_SECRET. Set values in the .env file next
to compose.yml (see .env.example for the full annotated list).
| Variable | Default | Purpose |
|---|---|---|
JWT_SECRET |
— (required) | Signs login cookies. Generate with openssl rand -hex 32 |
WEB_ORIGIN |
http://localhost:3000 |
The URL you open Mise at. https:// origins get Secure cookies automatically, and it's the OAuth issuer for the MCP connector |
APP_PORT |
3000 |
Host port the app is published on |
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB |
mise |
Database credentials (internal to the compose network) |
MEILI_MASTER_KEY |
a built-in default | Meilisearch key (internal to the compose network) |
COOKIE_SECURE |
inferred from WEB_ORIGIN |
Force the cookie Secure flag on/off, e.g. HTTPS at the proxy with an http:// WEB_ORIGIN |
PUBLIC_LIBRARY_URL |
official Mise library | Point recipe importing at your own list.json catalog |
Running the image outside Compose? It needs DATABASE_URL and JWT_SECRET,
and syncs its own schema on boot (disable with AUTO_MIGRATE=false).
- Node.js 20+ (developed on 24)
- pnpm 9+ (
corepack enableprovides it) - Docker (for the dev database and search)
No .env needed — dev defaults are built in:
pnpm install # installs deps + generates the Prisma client
pnpm dev:db # terminal 1 — Postgres, Adminer, Meilisearch
pnpm dev # terminal 2 — API (:3000) + web with HMROpen http://localhost:3000. The app creates the database and tables on first boot, so there are no migrations to run.
Want demo data for a first look?
pnpm db:seedThen sign in with demo@mise.app / password. (Seeding wipes and
recreates only the demo household — other accounts are untouched. Skip it to
start with a clean slate and the signup screen.)
Only need to override something (say, a different Postgres port)? Copy .env.example to
.envand uncomment the line — every variable is documented there.
pnpm dev starts both apps with hot reload: the API server on :3000 is the
front door — it serves /api and proxies everything else to the Vite dev
server on :4000 (HMR included), so the whole app lives on one port in dev
and prod alike. In production the same API proxies the built SSR frontend
instead.
| Command | What it does |
|---|---|
pnpm dev |
Run API + web together with hot reload |
pnpm dev:db |
Start dev Postgres + Adminer + Meilisearch |
pnpm build |
Production build of both apps |
pnpm start |
Run the built apps locally |
pnpm lint |
Lint all packages |
pnpm format |
Prettier-format the repo |
pnpm db:push |
Push the Prisma schema to the database |
pnpm db:seed |
Seed demo data |
pnpm db:studio |
Browse the database with Prisma Studio |
Package-specific scripts (e.g. pnpm --filter web typecheck,
pnpm --filter server typecheck) are also available.
The schema lives in apps/server/prisma/schema.prisma — after changing it, run
pnpm db:push. Dev data is bind-mounted to ./data/postgres (gitignored); to
reset it completely:
docker compose -f compose.dev.yml down
rm -rf ./data/postgres ./data/meili
pnpm dev:dbpnpm dev:db also serves Adminer at
http://localhost:8080 — log in with System PostgreSQL, server postgres,
and mise / mise / mise as the username / password / database (or your
POSTGRES_* overrides).
- Single port / proxy: the API server is the entry point in both dev and
prod. It serves
/apiand proxies everything else to the web server — the Vite dev server in dev, the SSR server in prod. No proxy config in Vite. - SSR + auth: protected pages render a loading state during SSR and fetch
on the client after hydration, so no session cookie passes through the SSR
server.
/loginand/signupare public and fully server-rendered. - Search is best-effort: if Meilisearch is unreachable, the app still runs — search reports unavailable and the index reconciles from Postgres on the next boot.
- Prisma 7 uses driver adapters; the connection URL lives in
apps/server/prisma.config.ts, not inschema.prisma. - Recipes, pantry items, and meals are exchanged in their Schema.org shapes end-to-end, so API responses map directly onto the frontend types.
Licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later), an OSI-approved open-source license. In short: you're free to use, copy, modify, and self-host this software — including inside a business — provided you keep the copyright/license notices intact. If you modify Mise and offer it to others over a network, you must make your modified source available to those users under the same license.




