Adding a service
Adding a service to the stack requires no lemonfiber change and no lemonfiber release. It is two data edits, and then the checks.
- A
services/<id>.tomlfile holding its[[service]]entry, named in theincludelist ofstack.toml. - Its profile added to whichever forms should carry it.
Then just ci in the stack directory. If you are working on your own stack
rather than the bundled one, point lemonfiber at it with
--stack-dir.
This page is about changing the stack itself. To run one more service beside the bundled stack without keeping a stack of your own, install it as a plugin instead: lemonfiber writes its container and wiring from a manifest, and takes it out again as one operation.
Where its Compose entry comes from
Section titled “Where its Compose entry comes from”Nobody writes one. lemonfiber generates the stack’s whole Compose project from the declarations of every service, the stack’s and every installed plugin’s: there is no fragment, no shared template and no overlay to edit. The image, the profile, the published port and its bind, the health check and the mounts all come from the service file below.
The one rule
Section titled “The one rule”Every service that touches the library has exactly one data mount, at /data.
Downloads and media live as subdirectories beneath it, on one filesystem, so an
import hardlinks instead of copying. A service says it takes data with
takes_data = true, and lemonfiber writes that one mount from the operator’s
DATA_ROOT setting, the same for every service; there is no second mount to
declare. What that looks like from the operator’s side is
hardlinks and one mount point.
The manifest entry
Section titled “The manifest entry”The [[service]] entry is where lemonfiber learns what the service is. Each one
lives in a file of its own under services/, and the root’s include list
decides which of them the stack holds, in that order. Every required field is
listed in the stack manifest contract;
the ones people forget are describes and without_it, which are what let a
failure be judged rather than merely reported, and last_release, which is the
latest release upstream has published rather than the one you pinned.
id is the name the generated Compose entry takes, and profile must be exactly
one declared profile. image carries neither a tag nor a digest: tag holds an
explicit tag, because a floating one fails validation and nothing in this stack
should change because time passed, and digest holds the multi-architecture
index the tag named, which is what Compose runs.
This is Sonarr’s file:
The release this page is built for does not carry what this part is read from. The next version's page has it.
What the checks enforce
Section titled “What the checks enforce”just ci runs every check that needs no network: the manifest against the
contract, every form resolving to services from its own profiles only, and the
rules a change to the manifest must carry. CI adds the networked ones — each pin read from its
registry, the upstream licence asked of its forge where a pin moves, and every
profile that needs no VPN subscription started for real. Every check has a specification
requirement behind it, and each is itself proven to fail when broken.
The stack’s maintaining guide
lists them all.
Whether it belongs in the bundled stack
Section titled “Whether it belongs in the bundled stack”Your own stack is yours. For the bundled one, a service enters only if it is open
source under an OSI-approved licence, publishes native linux/arm64 and
linux/amd64 images, is actively maintained, does something no included service
already does, and works without a paid tier. An image lemonfiber builds from its
own code carries lemonfiber’s own licence, Hippocratic-3.0, instead.
Maintenance status is established from a project’s commit and release history,
never from its own description of itself. just candidate <url> reads that
history and answers admit, watch or reject. A fork advertising itself as
an actively maintained successor, with no releases and no published image, is a
dead fork — and a dead fork is worse than a slow-moving project that works.
Stating the criteria is what makes “why is X not included?” answerable, and makes an addition a judgement against a standard rather than a matter of taste.
What lemonfiber does with a service it does not know
Section titled “What lemonfiber does with a service it does not know”It keeps working, generically. Lifecycle and status operations run on a service lemonfiber has never heard of, and it is shown with an unknown description rather than hidden. Features that need specific knowledge — wiring it to the other services, reading its queue health — report as unsupported for that service rather than failing the whole command.
The same applies in reverse. Remove a service lemonfiber depends on and it reports which features become unavailable; it does not refuse to run.
Where to go next
Section titled “Where to go next”The stack manifest contract is the field-by-field reference, and the stack manifest is what to do when lemonfiber refuses one. Running without lemonfiber is the compose project on its own, and the media-stack repository is where these files live. The specification for this is F3 stack manifests.