Self-hosted service
ulsklyc/yuvomi avatar
ulsklyc/yuvomi

Yuvomi packs twenty household modules into one container and argues with Compose in German

Project brief: Self-hosted family planner - tasks, calendars, shopping, meals, budget. Your data, your server.

2,340 stars228 forksJavaScriptMIT

At a glance

What is it?
A self-hosted family planner in JavaScript on Node, MIT licensed, shipping twenty switchable modules and optional AES-256 database encryption. Its compose file carries a long German comment explaining why it refuses the long environment syntax, and its test script names twelve ways the database breaks.
Who is it for?
Yuvomi is aimed at a household that has outgrown a dozen separate accounts and is willing to run a container, and the parts that make it credible are the unglamorous ones: append only migrations, a restore rollback, an encrypted database with its own rekey test, and a wall tablet that can log in as its own account. The parts to check before you commit are the ones the project itself hedges.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • ulsklyc/yuvomi2,340 ★ · +1,144
Star history
Repository2026-07-312026-11-14
ulsklyc/yuvomi1.2k2.3k

1,144 Stars in the last 106 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One container for the tasks, the calendar, the shopping and the budget

Yuvomi replaces a set of subscriptions with a single server you run. The pitch is a substitution table: a to-do app becomes Tasks with a Kanban board, deadlines, recurring chores and multi assignment; a family calendar becomes Calendar with sync, subscriptions and per event visibility; a meal planner and recipe app become a weekly planner with shopping export; a grocery list becomes aisle organised shared lists; a budgeting and cost splitting app becomes Budget with accounts, savings goals and shared costs with debt simplification; a document manager becomes searchable family files in folders.

The identity line in the readme is 20 modules, 26 languages, 0 trackers and optional AES-256 database encryption, and the module list in the manifest has twenty entries, so the number checks out. Only four are on by default; Inventory, Waste collection and Schedule start switched off and a module you switch off disappears from everyone's menu. Per family role a module is full, read only, or not there at all, which is the mechanism that makes a shared instance workable.

The code is JavaScript on Node, declared as ES modules with the entry point at `server/index.js`, MIT licensed, and the manifest asks for Node 22 or newer while the container image is built on Node 24. Two releases, v2.70.0 and v2.71.0, were published on the same day in late September 2026, with v2.69.1 a week earlier.

The compose file argues in German for the short environment syntax

The most surprising file in the repository is docker-compose.yml, because a block of it is a German comment explaining a decision to use the short form of `env_file` rather than the long `path:` form:

yaml
    # Bewusst die Kurzform, nicht `path:`/`required:` (Issue #765): die Langform
    # gibt es erst ab Compose v2.24, und Synology DSM, QNAP und ältere Distros
    # liefern ältere - dort scheitert schon das Einlesen mit
    # "services.yuvomi.env_file.0 must be a string", also noch vor dem ersten
    # Start.

The reasoning is about the machines this is meant to run on. The long syntax only exists from Compose v2.24, and Synology DSM, QNAP and older distributions ship older versions where the file fails to parse, before the first start, with that exact error. The long form had been needed for Git and GitOps stacks without a .env file, tracked as issue 698, and that case now goes through a separate compose file under docs/ that lists every variable individually.

The last line of the comment is the honest ending: keeping it bought nothing, because without a .env the missing SESSION_SECRET stops the container at startup anyway, so the deployment fails either way, one step later. A repository whose compose comments are in German while its readme offers English and German is not an oversight so much as two authors.

Both ends of the documents mount are read from .env on purpose

The volumes section is where the lessons from running this on a NAS are visible. Three mounts are one sided and read from the environment file, with defaults:

yaml
    ports:
      # Host-Port aus .env (OIKOS_HTTP_PORT); App im Container bleibt auf 3000.
      - "0.0.0.0:${OIKOS_HTTP_PORT:-3000}:3000"
    volumes:
      - ${DATA_DIR:-./data}:/data
      - ${BACKUP_DIR:-./backups}:/backups
      - ${MODULES_DIR:-./modules}:/app/modules

The port mapping publishes on all interfaces unless you change it, and the port inside the container is always 3000. The modules mount points at `/app/modules`, which the comment explains is pinned so the value in .env only moves where the host folder is, not where the application looks.

The fourth mount is the interesting one, because both its sides come from the environment file. The app writes to a configurable local path, so a mount hard coded to `/documents` would send uploads into the container's writable layer the moment anyone changed that setting in the interface, and those files would be gone on the next pull and up. That is a data loss bug written into the compose file as a comment so nobody reintroduces it.

The test script names the twelve ways this database can break

The most informative file in the repository is the scripts block of the manifest, because the test names read as a list of the ways a self-hosted app with a database goes wrong:

code
    "test-parallel": "node scripts/run-tests-parallel.mjs",
    "test:db": "node --experimental-sqlite test/test-db.js",
    "test:migrations-append-only": "node --test test/test-migrations-append-only.js",
    "test:migrate-tolerance": "DB_PATH=:memory: LOG_LEVEL=warn node --test test/test-migrate-tolerance.js",

Beyond those sit schema mirror, schema reconcile, db isolation, rename migration, db encryption, restore rekey, restore swap and restore server. Append only migrations, tolerance for a schema that is not exactly what the code expects, a mirror that keeps a second copy of the schema honest, and a family of restore tests including one for changing the encryption key and one for swapping a database in, are the concerns of an application people are expected to keep running for years.

Several of them run against Node's built in SQLite behind the experimental flag, and two set the database path to `:memory:` so they cannot touch a real installation. The coverage script is not simpler than the rest: it clears a directory, points Node's own coverage variable at it, redirects the test output to a log, and then hands the directory to a report script with a `server/` prefix.

The base image is pinned by digest because a tag is only a claim

The Dockerfile opens with a comment about pinning, in German, and both FROM lines carry the same digest:

code
FROM node:24-slim@sha256:0e0ff40c39bc087845bfb27465a0df4ea419520094bc35842ff83dd8cbe6f9b6 AS build

The argument is that a tag alone is a claim and the digest is the image that was actually built, with the note that Dependabot raises the digest together with the tag and that the two lines stay in step. The comment dates the digest to 4.9.2026.

The second comment explains the build stage's python3, make and g++ as a fallback rather than a requirement. Since v13 the SQLite driver is built on Node's stable native interface and ships its own binaries for linux on x64 and arm64, for glibc and musl, so there is no ABI specific download left for a source build to catch; node-gyp only runs on a platform where no binary was shipped. The encryption lives inside that module, so a system SQLCipher is not installed either. The runtime stage adds one package, gosu, and creates four directories. One more German comment explains that without a container default for the backup target the application falls back to a bare metal path inside the image, where the node user has no write permission and backups quietly miss the mounted volume.

Two timezone settings exist and only one of them is reachable

The example environment file documents variables that change behaviour rather than just filling blanks. `TZ` sets the container timezone, default UTC, and the comment says it affects log timestamps and the automated backup schedule, and is the default for the household zone. Then it says something more useful: since v2.34.0 the household zone is a setting of its own under Settings, Account, Appearance, Region, and that setting wins wherever both are present. The variable lives in the compose file, which you cannot reach on Umbrel, TrueNAS or Unraid. So on those platforms the household zone has exactly one possible source, which is the point.

`BIND_ADDRESS` has a parser written into its documentation. It takes IP addresses only; a host name, `localhost` included, is refused, and so is an IPv6 address carrying a zone identifier such as `fe80::1%eth0`. Set it to 127.0.0.1 when Node runs directly on the host behind a reverse proxy on that host, and leave it unset in a container, because a published port only reaches the app when the app listens on all interfaces.

A third variable is easy to confuse with it. `OIKOS_HTTP_BIND` is where the container engine publishes the port, not where the application listens, and the file notes it applies to the podman-compose file rather than the Docker one, with 0.0.0.0 as the default and 127.0.0.1 suggested for rootless Podman behind a proxy on the same host.

Modules are switched off, and integration is read only or one way

What the modules reach outside the container is narrow and worth knowing before you commit. Recipes can mirror a Mealie or a Tandoor instance read only, which is integration by subscription to somebody else's database rather than a second source of truth. Calendar does two way sync with Google and with CalDAV, Outlook push, subscribed calendars, holidays and per event visibility. Documents can be stored locally, on WebDAV or in Google Drive. Contacts sync with CardDAV and import and export vCard. Waste collection takes a pickup rule such as the last Friday, or a subscribed municipal calendar in ICS form.

Reminders have six outbound paths: in app, push, Gotify, ntfy, webhook and email, covering tasks, events, medications, warranties, best before dates, expiring documents and pickups. Push notifications arrive while the app is closed, and the readme is explicit that your server needs HTTPS for that, which is what the certificate profile and the example nginx configuration at the repository root are for. The installable app is a home screen shortcut from the browser with no app store, and the last shopping list you opened stays readable without signal.

Two integration surfaces are aimed at other software rather than at people. API Tokens are bearer tokens described by an OpenAPI 3.1 specification, and there is a built in MCP endpoint for AI agents. Backup does manual and scheduled runs with optional WebDAV upload, and it keeps a rollback point before restoring, plus the claim that a backup taken from a different installation can be restored straight in the browser.

Twenty six languages in the app, two languages in the repository

The gap between the product and its documentation is the clearest thing about this project. The application ships twenty six languages with in app switching, and the readme itself offers a German translation at README.de.md and nothing else, so the remaining twenty four are maintained by whoever asked for them rather than by the project. Detailed module behaviour lives in a spec under docs/, and the design notes, module list and backlog each have their own file at the root, alongside a security policy, a support file and a code of conduct.

What the modules do when they cooperate is the part a folder of separate apps cannot do, and it is worth reading the four examples rather than the module list. One import turns the next seven days of the meal plan into a shopping list with every ingredient sorted by aisle. Ticking something off while shopping books it back into the pantry with its amount and unit. A chore pays out points to whoever did it, either the assignee or the person chosen at the moment it is ticked, and those points are spent in a reward catalog you control, with an auditable ledger behind it. A receipt uploaded once attaches to the transaction, the shared expense and the inventory item at the same time.

The kitchen wall tablet is the reason some people would run this. It shows today's plan and who is on duty in wall mode, readable across a room, has an account of its own, and can fall back to an Immich screensaver of your own photos when it sits idle. That is a household device assumption, not a personal app.

Editorial conclusion

Yuvomi is aimed at a household that has outgrown a dozen separate accounts and is willing to run a container, and the parts that make it credible are the unglamorous ones: append only migrations, a restore rollback, an encrypted database with its own rekey test, and a wall tablet that can log in as its own account. The parts to check before you commit are the ones the project itself hedges. Modules ship switched off, the hosted tour at yuvomi.cloud is not the same thing as your instance, the app supports twenty six languages while the documentation exists in two, and two releases shipped on the same day in late September. Before the first import, take a backup with the built in scheduler, confirm HTTPS is terminated on your side if you want push notifications, and read the row in MODULES.md for the modules you actually need.

Frequently asked questions

Does Yuvomi send any data off your server?

Out of the box only a version check leaves the installation. Integration points exist but they are opt in: Google and CalDAV calendar sync, WebDAV or Google Drive document storage, CardDAV contacts, and outbound reminders through push, Gotify, ntfy, webhook or email.

Can Yuvomi encrypt its database?

Encryption is optional and uses AES-256. The manifest carries a dedicated test for the encrypted database and another for rekeying a restore, and the SQLite driver used in the container image keeps its cipher layer inside the module rather than relying on a system SQLCipher install.

Which Yuvomi modules are switched off by default?

Inventory, Waste collection and Schedule start switched off, and a module that is switched off disappears from everyone's menu. Per family role, each remaining module is full, read only, or not available at all.

What does Yuvomi need before it will start from docker-compose.yml?

An environment file. The compose file uses the short form of env_file on purpose, because the long form only exists from Compose v2.24, and its own comment states that without a .env file SESSION_SECRET is missing and the container stops at startup.

Does Yuvomi need HTTPS for push notifications?

Yes. The readme states that your server needs HTTPS for reminders to arrive as push notifications while the app is closed. The repository root carries a certificate profile and an example nginx configuration for that purpose.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ulsklyc-yuvomi.svg)](https://hysenlabs.com/projects/ulsklyc-yuvomi)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/ulsklyc-yuvomi.svg)](https://hysenlabs.com/projects/ulsklyc-yuvomi)

Was this useful?