Skip to content

chore: bump Nextcloud to 34.0.3; 33.0.8:3 → 34.0.3:0 - #131

Merged
MattDHill merged 3 commits into
masterfrom
next
Aug 24, 2026
Merged

chore: bump Nextcloud to 34.0.3; 33.0.8:3 → 34.0.3:0#131
MattDHill merged 3 commits into
masterfrom
next

Conversation

@helix-nine

@helix-nine helix-nine commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Bumps the bundled Nextcloud image from 33.0.8 to 34.0.3, releasing as StartOS version 34.0.3:0, and hardens the two recovery paths that a major upgrade drives traffic to.

The 33 line has not moved since 33.0.8 (33.0.8 is the newest 33.x.y-apache tag on Docker Hub), so 34.0.3 is the only upstream release available. This is a major bump, 33 → 34.

The 32.x cohort

An install still on 32.0.11:0 is offered this update, and it cannot succeed. Measured, not inferred:

  • 32.0.11:0 is still in otherVersions on both the beta and production registries, and is in other[] at startos/versions/index.ts:8 for exactly that reason. It is where a StartOS 0.3.5.x carry-over lands.
  • Executing the real VersionGraph from the installed SDK against this branch: canMigrateFrom is >=32.0.11:0 && <=34.0.3:0, and 32.0.11:0 satisfies it. StartOS accepts the hop and the Updates page offers it.
  • $OC_VersionCanBeUpgradedFrom in v34.0.3/version.php is {33.0, 34.0}. Nextcloud genuinely refuses 32 → 34.
  • startos/init/bootstrapNextcloud.ts:165 therefore throws: image[0] is 34, installed[0] + 1 is 33.

The user downloads the update, StartOS snapshots the volumes, init fails, and the whole thing rolls back. Data is safe — the guard is doing its job.

The remedy is a two-step update, and it exists in the product today. The Marketplace listing renders a version picker over best + otherVersions (shared-libs/ts-modules/marketplace/src/components/preview.component.ts versions$about.component.tsMarketplaceVersionsComponent), and MarketplaceControlsComponent renders Update for whichever version is selected once it is newer than the installed one. 33.0.8:3 stays indexed — it is the newest :N of its (flavor, upstream) group, so pruning never touches it. So a 32.x user picks 33.0.8:3 from the version list, updates, and then updates again to 34.0.3:0.

Nothing pointed them there, so this branch does:

  • the guard message now names the two-step path instead of only naming the problem;
  • the release notes lead with it, in all five locales.

Why the version graph is left alone

Raising the floor is possible — a 33.0.5:0 anchor with up: IMPOSSIBLE (33.0.5:0 being the oldest 33.x release) yields >=33.0.5:0 && <=34.0.3:0, with every 33.x revision still reachable and v33.0.8_2's migrateFrom035x still running for all of them. Verified by executing the graph. Dropping v_32_0_11_0 on its own does the opposite, widening the range to <=34.0.3:0, because the floor is set by the lowest vertex whose up is IMPOSSIBLE.

It would be the right change if the registry published canMigrateFrom. It does not: PackageVersionInfo::from_s9pk sets source_version: None, // TODO (shared-libs/crates/start-core/src/registry/package/index.rs:155), and the live production response for this package confirms it — "sourceVersion": null. That leaves inert both the registry's own source_version filter (registry/package/get.rs:180) and updates-refinement.service.ts, the UI machinery built to refetch a reachable version when best is not one. So the 32.x user would still be offered 34.0.3:0, and the failure would move from this package's clear domain message to service_map.rs's unsatisfiable target_range(prev_can_migrate_to ∧ next_can_migrate_from)uninit target range … is unsatisfiable — no version can satisfy it (host contract violation).

A worse message for the same outcome. Revisit once the registry publishes sourceVersion — at that point the floor makes StartOS route the 32.x cohort to 33.0.8:3 by itself.

Why 34 is worth taking now

Five earlier 34.x PRs (#110, #111, #112, #113, #114, #123) were closed. Two things named in #123 as reasons to wait have since resolved:

  • 34.0.2 shipped with ImageMagick previews disabled. lib/private/Preview/IMagickSupport.php at v34.0.3 is byte-identical to v33.0.8 — PDF, SVG, TIFF, HEIC and PSD previews are back, via the magic-byte-checking re-enable (server#62620).
  • A 33.x stepping stone had not shipped. 33.0.8:3 is now the registry's best, so the ordinary cohort is one major below 34.

The bump

  • nextcloud.DockerfileNEXTCLOUD_VERSION 33.0.8 → 34.0.3, per UPDATING.md. The upstream nextcloud/docker 33→34 apache Dockerfile diff is only php:8.4-apache-trixiephp:8.5-apache-trixie plus the version and tarball-URL lines; same Debian trixie base, so the ffmpeg layer and the a2enconf startos-proxy-keepalive layer are unaffected, and entrypoint.sh is byte-identical. nextcloud:34.0.3-apache resolves on Docker Hub for amd64 and arm64 (pushed 2026-08-17).
  • startos/actions/maintenance/disableUnstableApps.ts — adds appstore, files_lock and office. That is exactly core/shipped.json's defaultEnabled delta between v33.0.8 and v34.0.3 (nothing removed). appstore is load-bearing: 34 also adds it to alwaysEnabled, and AppManager::disableApp throws on an always-enabled app so Disable.php sets exitCode = 2.
  • startos/versions/current.ts — version and release notes, five locales.
  • UPDATING.md — the Nextcloud section documented only a patch bump. It now records the four checks a major needs.
  • package-lock.json — refreshes the filebrowser-startos#next pin (c35ff45ed12408). One line; package.json unchanged.

@start9labs/start-sdk is already at 2.0.9 (npm latest), so there is no SDK bump.

Every occ command the package invokes was diffed between v33.0.8 and v34.0.3: only #[\Override] attributes, declare(strict_types=1), and an unrelated new --no-password option on user:resetpassword. occ status still emits installed and needsDbUpgrade; core/register_command.php gains two background-job commands and removes none. Every key startos/fileModels/config.php.ts models is still in v34.0.3's config.sample.php — the key-set diff is additions only, plus a removal of mount_file, which the package does not model.

The hardening

Both are pre-existing, and both are worst exactly on the instances this release sends to them.

Disable Non-default Apps no longer aborts on the first failure (closes #127). It fanned every occ app:disable out under Promise.all + execFail, so one non-zero exit rejected the lot and withTemp's finally { destroy() } tore the subcontainer down under the still-running siblings — and the disabledApps report was never returned, leaving a bare ExitError. occ app:disable runs the app's own uninstall repair steps, so a fataling app is simultaneously the likeliest one to fail and the reason the action was run at all. Disables now run one at a time through exec, and the result reports what was disabled and, separately, what was not.

Reading the enabled-app list moved into readEnabledApps in utils.ts, which slices from the first { (a PHP startup or deprecation notice on stdout ahead of occ's JSON otherwise gives SyntaxError: Unexpected token 'P') and tolerates a missing enabled key instead of Object.keys(undefined).

Note the concurrency hazard here is the timeout and the teardown, not lost writes: AppManager::disableApp writes enablement to oc_appconfig in Postgres, not to config.php.

talk-turn now checks that Talk is enabled, not merely present (closes #128). It decided Talk was usable from the presence of custom_apps/spreed. OC\Console\Application::loadCommands iterates getEnabledApps(), and Updater::checkAppsRequirements calls disableApp($app, true) on every app without a release for the new major while leaving its files in place — so after a major upgrade the check passed while talk:turn:add did not exist. allOk stayed false, talkTurnConfigured was never recorded, and the relay silently stopped being applied while every chain build retried. Of the 457 apps with a Nextcloud 33 release, 63 have no 34 release.

hasNextcloudApp keeps its presence semantics for the three App Commands prechecks: those are allowedStatuses: 'any', so they must work with Postgres down, and their failure mode is an explicit failed-task notification rather than a silent retry.

Version file

current.ts is edited in place. 33.0.8:3 carries up: async () => {}, so there is no migration to strand and no reason to spin it into other[]. Confirmed by executing the graph before and after: v33.0.8_2.ts's clearTask + migrateFrom035x still runs for 32.0.11:0, 33.0.5:0, 33.0.7:0, 33.0.8:0 and 33.0.8:1, and is still correctly skipped for :2 and :3. 34.0.3:0 is unpublished (registry best is 33.0.8:3).

Release notes

They describe Nextcloud 34's feature set, not the 34.0.1–34.0.3 point fixes — every fix in those point releases was backported to the 33 line as well, so a user arriving from 33.0.8:3 already has them. Verified pairwise against the upstream release bodies:

fix 34.x also in
encrypted→unencrypted object-storage corruption server#63020 (34.0.3) server#63019 (33.0.8)
admin permission for all systemtag updates server#62924 (34.0.3) server#62923 (33.0.8)
preview file-signature check server#62620 (34.0.3) server#62619 (33.0.8)
ffprobe hangs server#61182 (34.0.1) server#61183 (33.0.6)
rate limiting for federated shares server#62130 (34.0.2) server#62129 (33.0.7)

The recovery-path bullet names the Disable Non-default Apps action, and each locale quotes that action's own dictionary entry verbatim (startos/i18n/dictionaries/translations.ts key 28) rather than a fresh translation.

Issues filed while reviewing, and their outcome

Left as-is

  • The preserved-defaults list is still a hand-maintained mirror of core/shipped.json. Across v32.0.11, v33.0.8 and v34.0.3 the 58-entry literal is exactly shippedApps{calendar, contacts} with no drift, and occ app:list --shipped=true --output=json would collapse it to one exec. Worth doing, but not under a major bump — getting it wrong fails silently on the recovery action itself.
  • files_lock is now preserved, and it is also an app-store app on 33. A user who installed it from the store carries a 33-era copy; after this upgrade it is exactly the kind of app that can break the UI, and the recovery action will now skip it. Excluding it would instead disable an app Nextcloud 34 ships enabled by default.

Not verified

No make, no s9pk pack, no VM install, and specifically no 33 → 34 upgrade exercised end to end. This crosses a major and runs a database schema change; it wants a real upgrade run before it goes out. npm run check and npx prettier --check are green.

Full upstream changelogs: 34.0.0 · 34.0.1 · 34.0.2 · 34.0.3

Merge with a merge commit — do not squash. next is long-lived: a merge commit leaves it a true ancestor of master, so it fast-forwards cleanly afterwards.

Bumps the bundled Nextcloud image from 33.0.8 to 34.0.3, crossing the
major from the 33 series to 34. The 33 line has not moved since 33.0.8,
so 34.0.3 is the only upstream release available.

nextcloud:34.0.3-apache is published for amd64 and arm64. Postgres and
Valkey track floating majors and are unchanged; Nextcloud 34's system
requirements still list PostgreSQL 17 as supported.

Nextcloud 34 adds appstore, files_lock and office to core/shipped.json's
defaultEnabled, and appstore to alwaysEnabled. The preserved-defaults
list in the Disable Non-default Apps action is a hand-maintained mirror
of that file, so all three are added. appstore in particular is
load-bearing: occ app:disable exits non-zero on an alwaysEnabled app and
the action runs under execFail, so without it the action would throw.

UPDATING.md documented only a patch bump. It now records the four extra
checks a major needs, which is what this bump had to do by hand.

Release notes describe the Nextcloud 34 feature set rather than the
34.0.1-34.0.3 point fixes: every one of those was backported to the 33
line too, so a user arriving from 33.0.8 already has them.

Does not merge as-is. An install still on 32.0.11:0 is offered this
update, migrates through the version graph, and then fails init on the
one-major-at-a-time guard. See the PR description.
The 34 release notes point users at Disable Non-default Apps, and a major
upgrade is the event that leaves an app disabled but on disk. Both paths
were built for the quieter case.

Disable Non-default Apps ran every occ app:disable concurrently under
execFail, so the first non-zero exit rejected Promise.all and withTemp
destroyed the subcontainer under the still-running siblings — and the
"these were disabled" report was never returned. occ app:disable runs the
app's own uninstall repair steps, so a fataling app is both the likeliest
one to fail and the reason the action was run. Disables now run one at a
time through exec, and the result reports what was disabled and what was
not. Reading the enabled-app list moved into readEnabledApps, which
tolerates a PHP notice ahead of occ's JSON and a missing `enabled` key
rather than dying with a bare SyntaxError or TypeError.

talk-turn decided Talk was usable from the presence of custom_apps/spreed.
occ registers an app's command namespace only while the app is enabled, and
occ upgrade disables any app without a release for the new major, so after
a major upgrade the check passed while talk:turn:add did not exist: allOk
stayed false, the signature was never recorded, and the relay silently
stopped being applied while every chain build retried. It now reads the
enabled set. hasNextcloudApp keeps its presence semantics for the App
Commands prechecks, which must work with the service stopped.

The one-major-at-a-time guard named the problem but not the remedy. The
Marketplace version list still offers the intermediate releases, so the
message and the release notes now spell out the two-step update.

Closes #127
Closes #128
Nextcloud's default trashbin_retention_obligation is `auto`: keep a
deleted file at least 30 days, then remove it only as disk space is
needed. On a server with room to spare nothing ever triggers that second
condition, so trash accumulates for the life of the instance with no way
to bound it from StartOS.

Adds it to the Configure action as a preset list writing `auto, <days>`
for 7 through 365, plus the Nextcloud default and `disabled`. Only the
maximum is offered — Nextcloud's `D1, D2` form also sets a guaranteed
minimum, which would hold files past the cap the user picked.

Also corrects the Nextcloud 34 release note for the new Office section.
The shipped `office` app is a document overview page, not an editor, and
it opens files in whatever office suite is configured — of which this
package bundles none.

Closes #63
@MattDHill
MattDHill merged commit c24694a into master Aug 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants