Journiv: a self-hosted journal with mood tracking, shipped as a single container
Journiv - Self hosted private journaling app
At a glance
- What is it?
- Journiv is a beta, self-hosted journaling app that bundles a FastAPI backend, a React frontend and a legacy Flutter interface into one Docker image. It is aimed at homelab users who want mood tracking and prompt-based entries on their own hardware, and the beta warning in the README is the first thing to take seriously.
- Who is it for?
- Adopt Journiv if you already run Docker on a home server and want journal entries, mood tracking and media stored on your own disk rather than someone else's. Do not adopt it if you need a stable schema, a guaranteed upgrade path, or a licence you can read without opening the repository.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- 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 September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Journiv targets: journaling without handing over the entries
Cloud journaling apps hold the most personal text a person writes, and the terms under which they hold it change without notice. Journiv takes the opposite position. The README describes it as a self-hosted private journal with mood tracking, prompt-based journaling, media uploads, analytics and advanced search, and the repository is tagged for homelab and privacy-tools audiences. That tag list is a fair summary of the intended user: someone who already runs containers at home and treats data ownership as a requirement rather than a preference.
The feature set matters less than the deployment shape. A journal is not a service you can migrate in an afternoon if the vendor changes direction, because the value is in years of accumulated entries and the mood data attached to them. Journiv's answer is to put the database and the uploaded media on a volume you control, which the quick start expresses as a single bind point. Whether that is enough depends on how the export path works, and the README is explicit that the minimal docker run command lacks components needed for import and export. That is a real caveat, not a footnote: the simplest way to start the app is also the way that leaves you without the tools to get your data back out.
One FastAPI origin serving two frontends
The architecture visible in the README is a single production container in which FastAPI serves both compiled frontends from the same origin and public port. The routing table is spelled out: the React frontend owns the root path, the Flutter frontend sits at /legacy/, the API lives under /api/v1/, public publishing routes under /pub/, and the FastAPI docs and OpenAPI schema are available at /docs and /openapi.json when enabled.
That layout explains several design decisions. Because everything shares one origin, there is no CORS configuration to get wrong and no separate web server to reverse-proxy. The migration from Flutter to React is handled at the same layer. The README describes a narrowly scoped retirement worker at /flutter_service_worker.js that replaces only the old root Flutter worker, removes the standard flutter-app-* caches, unregisters itself and reloads a controlled window. React also performs a one-time check for that exact root registration. Other service worker registrations are left alone, which the README frames as preserving a path for future React PWA support.
This is a more careful upgrade story than most frontend rewrites get. The constraint is that the /legacy/ interface is described as temporary and may be removed in a future release, so anyone who prefers the Flutter UI is on borrowed time. The OpenAPI schema at /openapi.json is the most useful part of the layout for anyone evaluating the project, because it documents the API surface without requiring an install.
Getting it running with docker run, and what that command leaves out
The README gives a single command for a quick trial:
docker run -d --name journiv -p 8000:8000 -e SECRET_KEY=your-secret-key-here -e DOMAIN_NAME=192.168.1.1 -v journiv_data:/data --restart unless-stopped swalabtech/journiv-app:latest
Four things are configurable here. SECRET_KEY is a placeholder that you replace. DOMAIN_NAME takes the host or IP the instance will be reached at, and the README's example uses 192.168.1.1, which is why the access URL is given as http://192.168.1.1:8000. The named volume journiv_data mounts at /data, which is where the persistent state lives. The image tag is swalabtech/journiv-app:latest, published on Docker Hub.
The README attaches a note to this command: it starts a minimal version of Journiv that lacks components needed for features like import and export, and points to a docker-compose.yml on the latest tag for the complete setup. That is the single most important operational detail in the whole document. A trial run and a production run are not the same deployment, and the difference is precisely the data-portability features. If you evaluate Journiv with the quick command and later decide to migrate, you may find the export tooling was never running.
There is also a hosted path. The README links a PikaPods run button and a demo instance at demo.almostadatacenter.com with the credentials demo@test.com and Demo1234, hosted by a contributor. The demo is the cheapest way to see the interface before committing a volume to it.
Beta status, backward compatibility and the backup instruction
The README opens with a beta warning and does not soften it. Journiv is in beta and under active development. The developers aim to keep data backward-compatible, but breaking changes may still occur, and the README asks users to keep regular backups to avoid loss during updates. One of the repository badges reads backups-recommended-critical.
The release cadence supports the beta label rather than contradicting it. The three most recent releases listed are v0.1.0-beta.24 from 2026-08-23, v0.1.0-beta.23 from 2026-05-25, and v0.1.0-beta.22 from 2026-04-13. All three are pre-1.0 and all carry the beta suffix. The gaps between them are roughly three months and then roughly three months again, with a longer stretch before the most recent one. Nothing here suggests a frozen schema.
For a journal this matters more than for most self-hosted software. A notes app that breaks on upgrade costs you an evening. A journal that breaks on upgrade can cost you years of entries, because the mood records, prompts and media are all keyed to the same store. The README's own instruction, to keep regular backups, is the honest reading: treat every version bump as a migration with a rollback plan, and verify that the backup you took actually restores before you pull a new tag. Note that the beta warning does not name a specific backup mechanism, so the procedure is whatever you build around the /data volume.
The licence is not stated in the README
The repository metadata reports the licence as NOASSERTION, and the README says only that the project is licensed under the terms specified in the LICENSE file. That is not a licence name. It means the terms exist somewhere in the repository but were not recognised by the tooling that classifies licences, and the README does not repeat them.
For most self-hosted apps this is a minor detail. For a journal it is not, because the two questions a licence answers here are whether you may modify the app for your own use and whether you may redistribute a modified image to other people. A permissive licence and a copyleft licence give different answers, and an unclassified licence gives none until you read it. The README also notes that the repository contains portions of code, documentation or text generated with the assistance of AI and LLM tools, reviewed and adapted by the author. That disclosure is worth knowing when you assess the provenance of any particular file.
The practical step is to open the LICENSE file in the repository before you build anything on top of Journiv. This is not legal advice and it is not a judgement about the project's intentions. It is simply that the terms are not summarised anywhere in the material available, so the file is the only source.
Where Journiv is the wrong tool
Journiv assumes you want a server. If you want a journal that syncs between a phone and a laptop without you maintaining anything, this is the wrong shape of software, because the whole value proposition is that you run the container and hold the volume. The README's own quick start is a docker run command with a volume flag. There is no hosted first-party service described.
The second case is stricter. If you need a stable data format with a documented upgrade path, a pre-1.0 project that warns about breaking changes is the wrong choice regardless of how good the feature list looks. The README's compatibility language is an aim, not a guarantee, and the release history shows a project still iterating on its own versioning.
The third case is narrower and easy to miss. The minimal docker run command omits import and export components. Someone who wants to try Journiv, write a few months of entries, and then move to something else has picked exactly the deployment that makes leaving hardest. Use the compose file from the start if you care about portability at all, even for a trial. The README links it on the latest tag rather than inlining it, so the extra step is deliberate.
Finally, the /legacy/ Flutter interface is explicitly temporary. Anyone choosing Journiv because they prefer that UI is building on a path the README says may be removed.
How Journiv differs from a plain Markdown-and-git journal
The obvious alternative for a privacy-minded writer is not another app. It is a folder of Markdown files in a git repository, optionally with a static site generator on top. The difference in approach is structural rather than cosmetic.
Markdown files give you a format that outlives any tool. Journiv gives you a database behind an API, with mood records, prompts, media uploads and search indexes that a text editor cannot represent. That is the trade: you gain queryable mood data and full-text search across entries and attachments, and you give up the guarantee that any future program can read your archive without Journiv's own export path. The README's note about import and export components being absent from the minimal container is exactly the seam where that trade becomes visible.
A second alternative is a general note-taking server with a mobile client. Those tend to be more mature and better documented, but they generally do not model mood tracking or prompt-based journaling, which is the specific combination Journiv is built around. If mood data is the reason you are looking, a general notes app will not substitute. If mood data is incidental, it probably will, and it will come with a longer track record.
The honest framing is that Journiv competes on features inside a self-hosted deployment, not on data longevity. Choose it for the mood and prompt model, and treat the export path as something you verify early rather than something you assume.
Maintenance cost and what to check before you commit
Running Journiv means running one container, one volume and two environment variables. That is a low operational load by self-hosted standards. The recurring cost is not the container, it is the upgrade discipline the beta status demands: watch the release notes, back up /data before pulling a new tag, and confirm the app starts and your entries are readable afterward. The README's own phrasing, that breaking changes may still occur, is the reason that routine exists.
There is a second, quieter maintenance item. The React frontend replaced Flutter at the root path, and the README describes a retirement worker that cleans up the old Flutter service worker and its caches. That logic exists to smooth one transition. It also means the frontend layer is in motion, and the /legacy/ route is on a removal path. Anyone running a reverse proxy in front of Journiv should be aware that the root path changed meaning between versions.
Before committing, three checks are worth doing. Open the LICENSE file and read the terms, since the metadata reports NOASSERTION and the README does not name them. Open the docker-compose.yml on the latest tag and compare it against the minimal docker run command, so you know which components you are missing. And test a restore: copy the /data volume, start a second container against the copy, and confirm your entries and media are there. If that restore works, the beta warning is a manageable risk. If it does not, you have found the problem before it cost you anything.
Editorial conclusion
Adopt Journiv if you already run Docker on a home server and want journal entries, mood tracking and media stored on your own disk rather than someone else's. Do not adopt it if you need a stable schema, a guaranteed upgrade path, or a licence you can read without opening the repository. Before committing, check the LICENSE file for the actual terms, read the docker-compose.yml on the latest tag to see which components the minimal docker run command omits, and confirm that a backup of the /data volume restores cleanly on a second container.
Community notes