Self-hosted service
norish-recipes/norish avatar
norish-recipes/norish

Norish: a self-hosted recipe app that assumes more than one cook

Norish - A realtime, self-hosted recipe app for families & friends

1,177 stars105 forksTypeScriptAGPL-3.0

At a glance

What is it?
Norish is a TypeScript, AGPL-3.0 recipe app built around a household rather than a single account, shipped as a Docker Compose stack with Postgres, Redis and a separate page-rendering service. The interesting part is not the recipe box. It is that realtime sharing and grocery coordination are treated as the primary data model, and that brings a heavier deployment than most self-hosted recipe tools.
Who is it for?
Norish fits a household or a small group of friends who already run Postgres and Redis and want recipe import, meal planning and shared groceries in one instance they control. It does not fit a single user who wants a lightweight recipe box, or anyone unwilling to keep a MASTER_KEY stable for the life of the data.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Norish targets is coordination, not storage

Most self-hosted recipe software answers one question: where do I keep the recipes I find? Norish answers a different one. The README describes it as a realtime, household-first recipe app for planning meals, sharing groceries, and cooking together. Those three activities share a property that a personal recipe box does not have. They involve more than one person changing the same state during the same week. A grocery list that two people edit, a meal plan that shifts when someone is away, a recipe that gets imported by one person and cooked by another. The topics on the repository (food, meal-planner, recipes, self-hosted) match that framing, and the recent release names do too: v0.23.0-beta is labelled Groceries, v0.22.0-beta is labelled Cookbooks and user management. The audience is a household or a close group of friends who want to run the instance themselves, which usually means someone in the group is comfortable with Docker Compose and a database volume. If you are one person keeping a private collection, the household model is overhead you will pay for in services and configuration without using the feature it exists to support.

What the Compose file reveals about the architecture

The quick start gives four services, and their split is the clearest statement of how Norish is put together. The norish container is the application, listening on port 3000, with a healthcheck that hits /api/v1/health and expects a 200. Postgres 17 is the store, reached through DATABASE_URL. Redis is present and reached through REDIS_URL, which is consistent with the realtime claim: a shared instance needs a way to push changes to connected clients, and a Redis-backed channel is the usual mechanism for that in a Node or TypeScript stack. The fourth service is the one worth pausing on. It is called obscura, image norishapp/obscura:0.2.0-norish.1, and the Compose comment says it renders recipe pages for URL imports. The app points at it with OBSCURA_ENDPOINT set to ws://obscura:9222, a WebSocket endpoint. So importing a recipe from a URL is not a plain HTTP fetch inside the app process. The page is rendered by a separate service over a socket. That design choice is what lets Norish handle pages whose recipe content only appears after client-side JavaScript runs, and it is also the reason your deployment has a fourth container that can fail independently of the other three. The README does not document what Obscura runs internally, so treat the renderer as a black box you host rather than something you can tune from the supplied material.

Getting it running: two required variables and a key you must not lose

The README states that at a minimum you need DATABASE_URL and MASTER_KEY. Everything else in the sample Compose file has a value you can copy. The MASTER_KEY line carries the strongest warning in the document: generate it with openssl rand -base64 32, and the tip says to keep it secret and stable because it derives the encryption keys, so changing it later invalidates previously encrypted data. Read that as a data-durability constraint, not a security footnote. If you lose the key, the encrypted data is gone. If you rotate it casually, the same. The sample environment block also sets AUTH_URL to http://localhost:3000, UPLOADS_DIR to /app/uploads, and mounts a named volume norish_data at /app/uploads, with the container running as user 1000:1000. That uid matters on a bind mount, where a mismatch between the host directory owner and uid 1000 produces write failures on uploads. The healthcheck uses node to issue a GET against http://localhost:3000/api/v1/health, with a start period of one minute and three retries, which is a reasonable signal that the maintainers expect startup to take a while. Bring the stack up with docker compose up -d from the directory holding the file, then confirm the app answers by requesting /api/v1/health on port 3000. The README does not describe any first-run setup wizard or initial admin account, so account creation is something to check in the documentation at docs.norish.dev rather than infer from the Compose file.

Beta versioning is the real adoption risk here

Every release visible in the material is tagged beta: v0.21.0-beta, v0.22.0-beta, v0.23.0-beta. The cadence is roughly weekly, and the scope of each release is broad. v0.21.0-beta is labelled UI refresh, image generation. v0.22.0-beta is labelled Cookbooks and user management. v0.23.0-beta is labelled Groceries. Those are not patch releases. They are feature areas arriving one at a time, and the user management release in particular touches how accounts and permissions work inside a shared instance. For a household running this for themselves, weekly feature releases are tolerable if you pin an image tag instead of tracking latest, which the sample file does not do: it uses norishapp/norish:latest. That is the default in the README, and it means every docker compose pull can move you across a schema change. The material does not describe a migration process or a rollback path, so the safe reading is that you should pin a specific tag, take a database dump before upgrading, and treat each beta as a deliberate step rather than an automatic one. The Obscura image is pinned in the sample (0.2.0-norish.1) while the app is not, which is an inconsistency worth noticing when you write your own Compose file.

Where Norish is the wrong tool

The deployment footprint is the first disqualifier. Four containers, a Postgres instance and a Redis instance are a lot of moving parts for a recipe collection, and each one is something you own at two in the morning. If your goal is a searchable archive of recipes you have already saved, a static site generator or a single-container app with SQLite will do the job with a fraction of the operational surface. The second disqualifier is the encryption model. Because MASTER_KEY derives the encryption keys, the instance is only as recoverable as your handling of that string. A user who wants to copy a database file around, restore it on a different machine, or hand it to a family member without ceremony will find that constraint grating. The third is the URL import path. Because imports depend on a separate rendering service reached over a WebSocket, an import failure can come from the app, the renderer, or the network between them, and the README gives no troubleshooting guidance for that split. If you cook mostly from a handful of sites that block automated rendering, the feature you adopted Norish for may be the feature that misbehaves, and the material offers nothing about how it handles those cases.

How this differs from a plain self-hosted recipe manager

The obvious comparison is a conventional self-hosted recipe manager, the kind that stores recipes in a database and serves them to whoever logs in. The difference is not features, it is the assumed unit of use. A conventional manager is single-tenant in practice: one account, one collection, sharing done by exporting or by giving someone a login. Norish is built the other way around. The Compose stack includes Redis, which a read-mostly recipe app has little reason to need, and the README leads with realtime and household-first. The v0.22.0-beta release label names user management as a first-class area, and v0.23.0-beta names groceries, which is a shared list rather than a personal one. So the meaningful difference to weigh is whether your problem is retrieval or coordination. If two or more people in your house argue about what to buy and what to cook this week, the shared state model is the point and the extra services are the cost of it. If nobody else touches your recipes, you are paying that cost for nothing, and a single-container alternative with a local database will be easier to back up, easier to upgrade, and easier to explain to whoever inherits it.

Licence and maintenance cost under AGPL-3.0

Norish is AGPL-3.0. For a household running its own instance, that is unremarkable: you are not distributing the software, and the licence does not restrict your private use. It matters if you plan to offer a modified Norish to other people over a network, because the AGPL's network clause reaches users who interact with the software remotely. That is a question for your own legal advice, not something to settle from a README. On maintenance, the material supports a few concrete observations. Releases arrive roughly weekly and are all beta, so an instance you keep current requires attention at that rhythm. The stack has three stateful pieces to back up or reconstruct: the Postgres volume db_data, the uploads volume norish_data, and the MASTER_KEY that makes the encrypted parts of the first two readable. Losing any one of the three degrades the restore. The Obscura service is a second image to track, pinned to 0.2.0-norish.1 in the sample, and its release schedule is not described in the material. The project accepts contributions and points to CONTRIBUTING.md and a development setup page at docs.norish.dev, which suggests an active single-maintainer or small-team effort rather than a large one. Budget for reading release notes before each pull, and for keeping your own pinned tags rather than following latest.

Editorial conclusion

Norish fits a household or a small group of friends who already run Postgres and Redis and want recipe import, meal planning and shared groceries in one instance they control. It does not fit a single user who wants a lightweight recipe box, or anyone unwilling to keep a MASTER_KEY stable for the life of the data. Before committing, verify two things on your own hardware: that an import from a site you actually cook from survives the Obscura renderer, and that a backup and restore of the Postgres volume plus the uploads volume brings the instance back with the same MASTER_KEY.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. norish-recipes/norish on GitHub
  4. README
  5. Releases
Community notes

Community notes