lemonfiber-media-stack — the stack itself
lemonfiber-media-stack
The media stack that lemonfiber runs: indexers, download clients, the *arr
apps, Jellyfin and Seerr. The Compose project lemonfiber writes from it runs
on its own, with plain docker compose.
This repository defines a self-hosted media stack of 23 services: twenty open-source projects, such as Prowlarr, SABnzbd, qBittorrent, Sonarr, Radarr, Jellyfin and Seerr, with Caddy run twice, as the proxy and as Jellyfin’s door, plus two small services lemonfiber builds for it. Every image is pinned to an exact version.
The lemonfiber tool writes this
stack’s Compose project, wires the services together and checks them. You do not
need it to keep running: the project it writes is a normal Compose project, so
you can run it yourself and stop using lemonfiber at any time.
Status: CI starts every profile on every change and checks that each service answers. The exception is the torrent profile (Gluetun and qBittorrent): it needs a real VPN subscription, so CI does not start it, and nobody has yet started it by hand.
Run it without lemonfiber
Section titled “Run it without lemonfiber”This repository holds the stack’s manifest, stack.toml and a file per service
in services/, and no Compose file. lemonfiber writes the project from them:
compose.generated.yml in its stack directory, with every value written in, a
.env beside it naming the profiles of the last start and the files Compose
reads, and each service’s secrets in a file of its own that no container
mounts. From then on the stack is operable with plain docker compose in that
directory, with no lemonfiber binary running. You need Docker with Compose v2.
docker compose --profile media up -dThat starts the library services, with no third-party accounts needed: Jellyfin on port 8096, plus Seerr, Navidrome, Audiobookshelf and Calibre-Web-Automated.
Jellyfin is reached through its door, which publishes 8096 and 8920 and asks
Jellyfin, before any item’s stream or picture goes on, whether the session
presented may see that item. A picture Jellyfin finds by a name rather than an
id, and its splash screen, it refuses. Its TLS port reads a certificate and key from
the project’s config/door/, which lemonfiber writes before it starts the door.
Where lemonfiber never started it, make your own pair there before up:
openssl ecparam -name prime256v1 -genkey -noout -out config/door/key.pemopenssl req -new -x509 -key config/door/key.pem -out config/door/certificate.pem \ -days 825 -subj /CN=doorJellyfin trusts the door’s fixed address, 10.80.96.18, to say which client a request came from. Set it under Dashboard → Networking → Known proxies, and restart Jellyfin, so its local-network rules see the household device rather than the door.
A form is a named set of profiles: the part of the stack you want for one
job. lemonfiber up <form> starts one, and just
reads them from stack.toml:
$ just forms-listsearch Find things. Nothing else runs.dl You have a link — fetch it.hunt Search and grab, manually.tv Search, download and automate televisionmovies Search, download and automate filmmusic Search, download and automate musicbooks Search, download and automate books and audiobooksauto Everything automated, nothing servedlibrary Serve what exists. Requires no third-party accounts.full The lot.proxy Friendly hostnames. Layers onto any other form.$ just form tv--profile search --profile usenet --profile torrent --profile tv --profile subslemonfiber up tv starts those profiles, and so does the same command typed in
the project’s directory:
docker compose --profile search --profile usenet --profile torrent \ --profile tv --profile subs up -dThe one rule
Section titled “The one rule”Every service that touches your files gets exactly one mount of the data
root, at /data. Downloads and media live under it on one filesystem, so an
import is a hardlink: instant, and no extra disk. Split them across two mounts
and every import silently becomes a copy. A service declares only whether it
takes data, and lemonfiber writes the one mount.
What is in this repository
Section titled “What is in this repository”| Path | What it is |
|---|---|
stack.toml |
The manifest’s root: profiles, forms, wiring, services that were removed, and include, which names each service’s file |
services/ |
One file per service, <id>.toml, holding its [[service]] |
.env.example |
Every variable the operator sets, documented |
config/ |
Starting configuration for Recyclarr, Homepage, Caddy, the door and SABnzbd |
recordings/ |
Recorded answers from each service, which prove what it can do |
scripts/ |
The checks CI runs, each runnable locally through just; the ones that start the stack run the project lemonfiber writes |
Documentation
Section titled “Documentation”- The services: what each one does
- Running without lemonfiber: the variables, and how to take over from lemonfiber
- Hardlinks and one mount point
Contributing
Section titled “Contributing”docs/maintaining.md covers adding, updating and removing a
service, and every check CI runs. just ci runs those checks locally and turns on
the git hooks.
Every change cites a requirement in the specification; read the contributing guide and AGENTS.md first. Report a vulnerability privately, as SECURITY.md describes.
Licence
Section titled “Licence”Hippocratic License 3.0. The services lemonfiber does not build keep their own open-source licences (GPL, MIT, Apache and others); this repository holds their configuration, not their code. The request gate and the decline service, which lemonfiber builds, are under the Hippocratic License too.
lemonfiber is made by NightWorksIO.
This page lives in another repository Rendered from lemonfiber/lemonfiber-media-stack at 1699ac3, 2026-10-26. Read the source of this page