Stack updates
Rendered from spec at76d46f3 (2026-10-25) · its source · Edit this page
Status: Accepted · Audience: Operator · Area: E — Maintenance
Purpose
Move the stack forward without breaking it, and make the one genuinely irreversible step visible before it’s taken.
The *arrs migrate their SQLite schema on first start of a new version, and there is no downgrade path. An operator who pulls a newer image and finds it unusable cannot simply revert — the database has already been rewritten in a format the previous binary refuses to open.
This is the single most consequential maintenance operation in the stack, and by default it is a one-word command with no warning.
Behaviour
Versions are pinned, so updating is deliberate
Images carry explicit version tags (roadmap M1). Nothing changes because time passed. An update happens when the operator decides it should.
The alternative — :latest everywhere — means an unrelated pull can jump six
months across a dozen services at once, with no way back.
What’s available is shown before anything is applied
Current version, available version, and the size of the jump. Where release notes are reachable, they’re linked. Where a version crosses a major boundary, that’s called out rather than left to be inferred from numbers.
Irreversibility is stated in advance
Any update that will migrate a database says so, plainly, before proceeding:
Sonarr 4.0.15 → 4.1.0 migrates its database on first start. Returning to 4.0.15 afterwards is not possible. A backup will be taken first.
Backup is automatic and unconditional
Before any update that touches service state, a backup is taken (E3). Not offered — taken. The operator can decline the update, not the safety net.
If the backup fails, the update does not proceed.
Updates are staged, not simultaneous
Services update one at a time with health verified between each. A failed update stops the run rather than continuing into a half-migrated stack.
This costs time and is worth it: a single failure is diagnosable, whereas twelve simultaneous ones are not.
Rollback is offered where it’s actually possible
For services that haven’t migrated state, reverting is straightforward and offered directly (E4). Where migration has occurred, rollback requires restoring the backup, and lemonfiber says which situation applies rather than offering an action that cannot work.
The stack definition updates too
New lemonfiber versions carry a new pinned bundle, each part’s image pinned in its own plugin manifest (ADR-0005, ADR-0045). Locally modified stack files are never silently overwritten — the operator is shown a diff (C9).
On the companion
The app reads versions; it does not become a second place to apply them.
N14-R8 holds that boundary (N14):
updating the services is N2’s offer and carries different consequences, and a
version screen that grew an Update button would be making that offer without
the account N2 requires of it.
Where the offer is made, N14-R5 requires it to state what version of the stack
it carries and what happens after it is applied, before it is agreed to —
which is irreversibility is stated in advance, on the surface where an operator
is least able to go and check anything first. And N14-R3 requires a withdrawn
release neither offered nor counted as an available update: counted, it produces
a badge for something nobody may install.
Rollback is offered where it’s actually possible carries across intact.
N6-R10 requires that where the stack offers neither a rollback nor a restore,
the app offers neither, and never presents one as the other
(N6).
States
| State | Meaning |
|---|---|
current |
Everything at its pinned version |
updates-available |
Newer versions exist |
checking |
Querying registries |
backing-up |
Pre-update backup in progress |
updating |
Applying, service by service |
partial |
Some updated, one failed; run halted |
failed |
Update failed; prior state described |
Edge cases
| Situation | Behaviour |
|---|---|
| Update available for one service only | Update just that one. Don’t force a wholesale bump. |
| Major version jump | Highlight distinctly; major versions carry breaking changes far more often. |
| Update fails mid-run | Halt. Report exactly which services updated and which didn’t. Never continue into unknown territory. |
| Backup fails | Do not update. The safety net is a precondition. |
| Registry unreachable | Report that update checking failed; leave the stack untouched. |
| Image pulled but service won’t start | Report with logs; offer rollback if no migration occurred. |
| Downloads active during update | Report and offer to wait. Restarting a download client mid-transfer is usually recoverable but not always. |
| Update changes a default the operator relied on | Where detectable from release notes, call it out. Otherwise surface via drift afterwards. |
| Two services must move together | Where a dependency exists, update as a unit and say so. |
| Disk too full to pull | Detect before pulling; a failed pull from a full disk is confusing. |
| Update available for a service not in the active form | Show it; updating it is fine even when stopped. |
| Operator wants to stay on an old version indefinitely | Entirely supported. Pinning is the default posture, and lemonfiber MUST NOT nag. |
Acceptance criteria
| ID | Requirement |
|---|---|
| E1-R1 | All service images MUST be pinned to an immutable digest, of the multi-architecture index rather than of one platform’s image, in the manifest that declares the service, a bundled part’s in its own plugin manifest, with the version tag recorded alongside it and shown wherever a version is shown. No service MAY resolve an image by tag when it runs. |
| E1-R2 | Available updates MUST be shown with current version, target version, and the significance of the jump before anything is applied. |
| E1-R3 | An update that will migrate service state MUST state that the change is irreversible before proceeding. |
| E1-R4 | A backup MUST be taken automatically before any update touching service state. |
| E1-R5 | If the pre-update backup fails, the update MUST NOT proceed. |
| E1-R6 | Services MUST be updated one at a time with health verified between each. |
| E1-R7 | A failed update MUST halt the run and MUST report which services were and were not updated. |
| E1-R8 | Rollback MUST be offered only where it can actually succeed; where migration has occurred, restore MUST be indicated instead. |
| E1-R9 | Locally modified stack files MUST NOT be overwritten without a diff and confirmation. |
| E1-R10 | Major version transitions MUST be highlighted distinctly from minor and patch updates. |
| E1-R11 | Insufficient disk space MUST be detected before pulling. |
| E1-R12 | Active transfers MUST be reported before an update, with an option to wait. |
| E1-R13 | Individual services MUST be updatable without updating others. |
| E1-R14 | lemonfiber MUST NOT repeatedly prompt an operator who has chosen to remain on current versions. |
Related
- E3 Backup & restore — the mandatory safety net
- E4 Rollback — undoing when possible
- E2 Self-update — updating lemonfiber itself
- C9 Drift detection · A5 Migration
- J7 Upgrading