Play
A full-screen player built for meditation, not for music — settling lead-in, interval bells, a gentle fade-out, and a mini-player that survives navigation. Or breathe with nothing playing.
Self-hosted meditation manager · Open source · MIT
A practice manager over the meditation audio already in your folders — read-only, nothing renamed. Play it, plan against it, and keep an honest record of what you actually sat for.
The loop
A practice has a shape, and it is a circle. ZenPort is built around that shape rather than around a file browser.
Today opens on one suggestion and what you were part-way through — never a wall of files.
Play something, or breathe with nothing playing at all.
A short sheet afterwards — how settled it felt, a tag, a voice note. Or skip it.
A practice or a learning plan, drafted by hand or by AI, on a timeline that forgives a missed day.
Statistics assembled only from sits that actually happened.
Nothing here guesses what you practised. It only knows what you did.
A full-screen player built for meditation, not for music — settling lead-in, interval bells, a gentle fade-out, and a mini-player that survives navigation. Or breathe with nothing playing.
Practice plans and learning plans, with an intention, a cadence and a target. A missed day is recorded as information, never as failure.
Every sit records what you actually listened to. Those sessions are the only source for your statistics, which is why a quiet week is allowed to look quiet.
The scanner
The worst thing a media app can do to a library somebody spent years assembling is have an opinion about it. So the scanner works only from the structure you already made — and it will show you its reasoning for every single thing it decided.
/library/meditationsread-only
What it concluded, and why
An invented library, not anyone’s real one. ZenPort has no idea what is in yours, and neither does this page.
Cover art
Most of a self-hosted library has no artwork at all, and a letter on a flat tint makes thirty recordings look like a spreadsheet. So anything without a cover is painted one, picked by a hash of its creator and title — and if an image ever fails, it falls back to arcs drawn from the logo’s own sweep, below.
Either way it is deterministic. Cover art that reshuffles on reload is worse than none, because you can no longer find things by their look.
Not a mockup: this page imports the app’s own fallback function and runs it at build time. The eight titles are invented — any eight strings make the point, and real ones would only publish somebody’s library.
Evening Current · Settling In
The Long Exhale
Ten Slow Minutes
Rain on the Roof
Before Sleep, Briefly
Walking Without Arriving
A Steady Place
Coming Back
In the app
Every item is a meditation, course, talk or soundscape — guessed from your folders, corrected by you, and never un-corrected by a rescan. Courses count lessons, and pick up where you stopped.
Add your own OpenAI key and a three-step sheet drafts a plan from your library: titles, creators and lengths only, named to the model by short handles. It proposes; nothing is saved until you accept.
Full-screen stage, picture-in-picture, a scrubber that previews while you drag. Audio keeps its own element, so a meditation still plays with the screen locked.
The breath guide can be felt as well as seen: taps that gather and lengthen through the in-breath, and spread and soften through the out-breath.
A reflection sheet after each sit — how settled it felt, a tag, a voice note. Markdown export, and no admin backdoor into anybody’s journal.
⌘K opens a palette over every meditation, page and preference. Four accents, and a folder switch for anything you would rather it did not index.
Self-hosting
SQLite in a named volume, your library mounted read-only, a non-root runtime, and first-run admin setup with no default credentials. Until you point it somewhere real it serves a bundled sample library, so you can feel the app first.
# get it git clone https://github.com/ilanKushnir/zenport.git cd zenport cp .env.example .env # in .env ZP_SESSION_SECRET=$(openssl rand -hex 32) ZP_MEDITATION_PATH=/srv/media/meditations # run it docker compose up -d --build
Then open http://<host>:8484 and create your admin account.
The two mounts
Audio: .mp3 .m4a .m4b .flac .ogg .opus .wav .aac · Covers: .jpg .png .webp .avif · Documents: .pdf .txt .md .html
Before you expose it
It holds private journals and listening history. Put HTTPS and a reverse proxy in front of it, and read docs/security.md for the honest version of what it does and does not protect you from.
Questions
read-only at the container level, so it is not a promise in the code — it is a promise the kernel keeps. ZenPort writes only to its own /data volume.yt-dlp for YouTube metadata, and YouTube’s privacy-enhanced embed, which only loads after you tap a video.
This is the calm place to keep them, and the honest record of what you did with them. Free, MIT, and running on a machine in your own house.
Latest release on GitHub · MIT licence