You can’t (shouldn’t) skip versions. It breaks Nextcloud.
Each version update requires you to update and then run Nextcloud before updating to the next so that its migration scripts can run. We prevent you from “skipping” versions (you can’t update from 30 → 32) but we can’t stop you from arriving in a bad state at 31, then locking in the damage by updating to 32.
There’s no “supported” way to reverse an unsupported upgrade path, but I can turn my notes into a guide for you to adapt. If this fails though, you can just restore your v30 backup and start over.
You don’t need a sideloaded .s9pk. Nextcloud 31.0.13:1 is on the marketplace, and that’s all the bridge you need. A plain downgrade looks blocked only because one file on disk is out of sync — fix that first and the normal downgrade works.
Why it’s stuck
Two version fields drifted apart:
/var/www/html/version.php (on disk) = 32.0.11.1 — got overwritten when the 32 image’s entrypoint ran its file copy.
config.php 'version' + the database schema = 30.0.11.1 — never advanced, because occ upgrade refused the 30 → 32 (two-major) skip.
The container entrypoint checks version.php before anything else. On the 31 image it sees on-disk 32 > image 31 and aborts with “downgrading is not supported.” That’s almost certainly why the package looked un-downgradeable. Edit version.php back down to 30.0.11.1 and the entrypoint treats 31 as an ordinary one-major upgrade, runs the intermediate migration, and you’re unstuck.
Fix
Run from a root shell inside the Nextcloud container.
0. Snapshot the current (broken) state. You already have the v30 pg_dump + config.php — also copy version.php, and take a fresh StartOS backup so you can return to “broken but recoverable”:
cp /var/www/html/version.php /var/www/html/version.php.bak-32
1. Rewrite version.php to report 30.0.11.1:
cat > /var/www/html/version.php <<'EOF'
<?php
$OC_Version = array(30,0,11,1);
$OC_VersionString = '30.0.11.1';
$OC_Edition = '';
$OC_Channel = 'stable';
$OC_VersionCanBeUpgradedFrom = array (
'nextcloud' => array (
'29.0' => true,
'30.0' => true,
),
'owncloud' => array (
'10.13' => true,
'10.14' => true,
'10.15' => true,
),
);
$OC_Build = '';
$vendor = 'nextcloud';
EOF
2. Verify it took (should print 30.0.11.1):
php -r 'require "/var/www/html/version.php"; echo $OC_VersionString, "\n";'
Do not run occ upgrade here — the 32 code is still on disk. Exit and let the package downgrade do the work.
3. Downgrade the package. StartOS UI: Stop the Nextcloud service → Marketplace → Downgrade to 31.0.13:1 → Start.
4. Watch the logs. You want, in order:
Initializing nextcloud 31.0.13.1 ...
Upgrading nextcloud from 30.0.11.1 ... # file copy — entrypoint check passed
... Updating database schema ...
... Update successful
Maintenance mode during this is normal. Let it finish — don’t jump to 32 early (that’s what caused the original skip).
If instead you see “downgrading is not supported” or any unexpected version-mismatch, stop and recheck the three numbers below.
5. Confirm 31 is clean:
php /var/www/html/occ status # expect version: 31.0.13.1, needsDbUpgrade: false
Run these if the upgrade output suggested them:
php /var/www/html/occ db:add-missing-indices
php /var/www/html/occ db:add-missing-columns
6. Now go to 32. Marketplace → Update to 32 → let occ upgrade run the 31 → 32 migration. Verify:
php /var/www/html/occ status # expect version: 32.x, needsDbUpgrade: false
7. Third-party apps. If any were auto-disabled, check each for a 32-compatible release before re-enabling.
If anything refuses to boot
These three numbers govern the whole state machine — read them before trying anything:
cat /var/www/html/version.php # on-disk code version
php /var/www/html/occ config:system:get version # config/DB version
plus the image version in the entrypoint’s Initializing nextcloud X.X.X.X log line. The entrypoint only proceeds when on-disk ≤ image and the gap is ≤ 1 major; occ upgrade only proceeds when code − config ≤ 1 major.