You ran docker-compose pull, restarted your container, and now your Nextcloud instance refuses to load. Instead of your files and calendar, you see a maintenance screen or a cryptic error about unsupported upgrades. If you are staring at the message “Updates between multiple major versions and downgrades are unsupported,” you have landed in one of the most frustrating situations a self-hoster can encounter.
This happens when your Docker image jumps from one major Nextcloud version to another, skipping a release in between. The nextcloud:latest tag is the most common culprit. It silently pulls whatever the newest major version is, even if your instance was running two or three major versions behind.
The good news is your data is almost always recoverable. I have walked through this recovery process multiple times, and the fix comes down to three things: downgrading the Docker image, editing the version.php file, and stepping through each major version one at a time. This guide walks through how to recover a Nextcloud Docker instance after a skipped major version upgrade, with exact commands you can copy and paste.
Whether you skipped from v28 to v30, accidentally pulled nextcloud:latest on an old installation, or are trying to migrate from an EOL release, the procedure below covers every scenario I have encountered.
Table of Contents
Quick Summary: The Fix in One Paragraph
You recover a Nextcloud Docker instance after a skipped major version upgrade by downgrading the Docker image to the version your database expects, editing version.php to match that older version, starting the container, running occ upgrade, and then incrementally upgrading one major version at a time until you reach your target version.
The recovery breaks into three phases. First, roll back the Docker image and align version.php with your database. Second, run occ upgrade on the rolled-back version to ensure the database and filesystem are consistent. Third, step through each intermediate major version sequentially until you arrive at the version you originally wanted.
This mirrors the approach documented in GitHub issue nextcloud/docker #2079, which the community considers the most reliable recovery procedure.
Why Nextcloud Refuses to Start After a Skipped Major Version
Nextcloud enforces sequential major-version upgrades to protect database schema integrity. Each major version includes migration scripts that transform the database from the previous major version’s schema to the next one. When you skip a version, those migration scripts cannot run because they expect the database to be at the immediately preceding version.
For example, if your database is at version 28 and you pull the version 30 Docker image, the version 30 migration scripts expect a version 29 database. They find a version 28 database instead and abort with an error. Nextcloud then enters a protected state where it refuses to start, which prevents corruption.
The same protection blocks downgrades. If you accidentally upgraded and try to pull the old image back, Nextcloud sees that your data version is higher than the image version and refuses to run. This is what creates the “stuck” feeling where neither rolling forward nor rolling backward works.
The Exact Error Message You Will See
The most common error is: Exception: Updates between multiple major versions and downgrades are unsupported. Update failed.
This appears in docker-compose logs -f or in the browser when you try to load Nextcloud. The message is technically accurate but gives you no actionable direction, which is why so many users end up searching for help.
Other symptoms include the container entering a reboot loop, the web interface showing a perpetual maintenance screen, or logs that repeat the upgrade attempt and failure endlessly.
version.php vs the Database Version
Two things track your Nextcloud version, and they can disagree. The version.php file in your Nextcloud installation directory declares what version the code expects. The database has its own version entry in the oc_appconfig or oc_preferences table that records what version the data was last upgraded to.
When these two values disagree in a way that violates the sequential upgrade rule, Nextcloud refuses to start. The recovery procedure works by making version.php agree with the database, then stepping forward correctly.
Prerequisites and Backup Before You Start
Before touching anything, back up your data. This is non-negotiable. The recovery procedure involves editing version files and running migration scripts, and a mistake could corrupt your installation. Users who had backups reported feeling far more confident attempting recovery, and several community members cited backups as the single most important safety measure.
Back up the Docker volumes that hold your Nextcloud data directory and database. If your volumes are named nextcloud_html and nextcloud_db, you can create tarball backups like this:
docker run --rm -v nextcloud_html:/data -v $(pwd):/backup alpine tar czf /backup/nextcloud_html_backup.tar.gz /data
docker exec nextcloud-db mysqldump -u root -pPASSWORD nextcloud > nextcloud_db_backup.sql
Store these backups somewhere safe before proceeding. If anything goes wrong, you can restore from these and try again.
Identify Your Previous Version
You need to know what version your instance was running before the accidental upgrade. The most reliable way is to check the database version entry, which records the last successfully upgraded version.
Run this against your database container: docker exec nextcloud-db mysql -u root -pPASSWORD nextcloud -e "SELECT * FROM oc_appconfig WHERE appid='core' AND configkey='lastupdatedat';"
You can also check config/config.php in your data volume, look at old docker-compose.yml commits, or review docker-compose logs output from before the incident. The version you find here is the one you will roll back to.
Tools You Will Need
You do not need a text editor inside the container. The Nextcloud Docker image does not include vi or nano, and installing one adds unnecessary steps. Instead, you will edit version.php from the host using docker exec with sed, or by mounting the volume and editing the file directly.
You will also need your docker-compose.yml file, access to Docker Hub to verify available image tags, and SSH or terminal access to your Docker host.
Diagnosing Your Recovery Scenario
Not every recovery is the same. The difficulty depends on how many major versions you skipped and whether the problem is in the database, the filesystem, or both. Use this decision table to identify your scenario before you start.
A single-version skip (like v28 to v29) is the easiest to fix because you only need one intermediate step. A two-version skip (like v28 to v30) requires an extra upgrade cycle. Three or more version skips are rare but follow the same pattern, just with more steps.
Recovery Difficulty by Skip Distance
One major version skipped (e.g., v28 to v30 expecting v29): Roll back to v28, confirm version.php matches, run occ upgrade, then upgrade to v29, run occ upgrade again, then upgrade to v30. Two upgrade cycles total.
Two major versions skipped (e.g., v27 to v30): Same process but three upgrade cycles. Each cycle follows the identical pattern.
Downgrade attempt after accidental upgrade: Roll back the Docker image to the version your database reports, edit version.php to match that version, then proceed with forward upgrades. Never attempt a true data downgrade.
Symptom-to-Cause Mapping
If the container reboots in a loop with no clear error in logs, the cause is almost always a version mismatch between the Docker image and the database. Check docker-compose logs -f for the unsupported upgrade message.
If the web interface shows a maintenance screen that never clears, the cause is usually a failed or incomplete occ upgrade. You need to run the upgrade command manually.
If all apps show “files were not correctly replaced” errors, the cause is typically that the app files on disk do not match the version declared in version.php. This happens when you edit version.php but do not properly downgrade the image first.
Step-by-Step Recovery Procedure
This is the core recovery procedure I have used and refined based on community-tested methods. Follow the steps in order. Do not skip any step, and do not try to jump ahead even if things look like they are working.
Step 1: Roll Back the Docker Image
Stop the current container and change the image tag in your docker-compose.yml to the version your database expects. If your database reports version 28, use an image tag like nextcloud:28 or a specific patch like nextcloud:28.0.12-apache.
Open docker-compose.yml and change the image line: image: nextcloud:28
Then stop and remove the current container: docker-compose down
Pull the correct image and start the container: docker-compose pull && docker-compose up -d
Do not navigate to the web interface yet. The container will likely still refuse to start because version.php does not match. That is expected. Proceed to Step 2.
Step 2: Edit version.php Without vi or nano
The Nextcloud Docker image does not ship with a text editor, so you need to edit version.php using sed or by editing the file on the host if the volume is mounted. The goal is to make version.php report the same version your database expects.
First, check the current contents of version.php: docker exec -u www-data nextcloud-app cat version.php
You will see two key lines: $OC_Version = array(30, 0, 4, 1); and $OC_VersionString = '30.0.4'; These need to match your database version, which in our example is 28.
Use sed to replace the version values. For example, to change from 30.0.4 to 28.0.12:
docker exec -u www-data nextcloud-app sed -i 's/30, 0, 4, 1/28, 0, 12, 1/' version.php
docker exec -u www-data nextcloud-app sed -i "s/30.0.4/28.0.12/" version.php
Verify the change worked: docker exec -u www-data nextcloud-app cat version.php
If your Docker volume is bind-mounted to the host, you can also edit version.php directly with any text editor on the host machine. The result is the same.
Step 3: Start the Downgraded Container
With version.php now matching the database version, restart the container: docker-compose up -d
Watch the logs to confirm it starts without the unsupported upgrade error: docker-compose logs -f
If the logs show Nextcloud initializing normally, you have successfully rolled back. If you still see the error, double-check that both $OC_Version and $OC_VersionString in version.php match the database version exactly. A mismatch in either value will trigger the error again.
At this point, Nextcloud should be accessible in maintenance mode. Do not try to use the web interface yet.
Step 4: Run occ upgrade and Handle Maintenance Mode
Now you need to run the upgrade command to ensure the database schema and filesystem are fully consistent at this version. Nextcloud uses occ commands for this, and you run them through docker exec.
First, take Nextcloud out of maintenance mode if it is in it: docker exec -u www-data nextcloud-app php occ maintenance:mode --off
Then run the upgrade: docker exec -u www-data nextcloud-app php occ upgrade
Watch the output carefully. If the upgrade completes successfully, you will see a message like “Update successful.” If it fails, note the error and check the troubleshooting section below.
If maintenance mode gets stuck on, you can manually disable it in config/config.php by setting 'maintenance' => false, Then run occ upgrade again.
Step 5: Incremental Step-Upgrade to the Next Major Version
Now that your instance is stable at the rolled-back version, you need to upgrade one major version at a time. If you are at v28 and want to reach v30, your next stop is v29.
Change the image in docker-compose.yml: image: nextcloud:29
Stop, pull, and start: docker-compose down && docker-compose pull && docker-compose up -d
Nextcloud will detect that the image is one major version ahead of the data and will attempt the upgrade automatically. Monitor the logs: docker-compose logs -f
If the automatic upgrade does not trigger or stalls, run it manually: docker exec -u www-data nextcloud-app php occ upgrade
Wait for the upgrade to complete fully before moving on. Do not start the next version upgrade until this one finishes.
Step 6: Repeat Until You Reach Your Target Version
Repeat Step 5 for each remaining major version. In the v28 to v30 scenario, you would now change the image to nextcloud:30, restart, and run occ upgrade one final time.
The pattern is always the same: change one major version in the image tag, restart the container, wait for or manually trigger the upgrade, confirm success, then move to the next version.
Once you reach your target version and occ upgrade reports success, your instance is fully recovered. Log in through the web interface, verify your files and apps are working, and check the admin settings page for any remaining warnings.
Troubleshooting Common Recovery Errors
Even with the correct procedure, you may hit secondary errors during recovery. These are the issues I see most frequently, along with how to resolve them.
“Files Were Not Correctly Replaced” for All Apps
This error appears when you edit version.php without properly downgrading the Docker image first. The app files on disk belong to the newer version, but version.php declares the older version, creating a mismatch.
The fix is to make sure the Docker image is actually running the version that matches version.php. Stop the container, verify your docker-compose.yml image tag, pull the correct image, and restart. Then re-run occ upgrade.
If the error persists, you may need to force the app files to update. Run: docker exec -u www-data nextcloud-app php occ upgrade --force-apps This re-extracts the correct app files for the declared version.
Container Stuck in Reboot Loop
A reboot loop usually means the container crashes on startup, Docker restarts it, and it crashes again. The root cause is almost always the version mismatch between the image and the database.
Check the logs to confirm: docker-compose logs --tail=50
Look for the unsupported upgrade error. If you see it, the fix is the rollback procedure above. Make sure version.php matches the database version exactly, including the patch number.
If the logs show a database connection error instead, check that your database container is running and that the credentials in config/config.php are correct. A database that is down will cause similar symptoms.
Database Schema Mismatch After Recovery
Sometimes the filesystem recovery succeeds but the database schema is still at the wrong version. This can happen if a previous upgrade attempt partially ran before failing.
To diagnose, check the database version: docker exec -u www-data nextcloud-app php occ status
This command shows the version string, the version array, and any pending database changes. If there are pending changes, run occ db:add-missing-indices and occ db:add-missing-columns to bring the schema up to date.
MariaDB Upgrade After Nextcloud Recovery
If you also updated your MariaDB Docker image around the same time, you may need to run the MariaDB upgrade script. The symptom is database errors after Nextcloud recovery appears complete.
Run the MariaDB upgrade: docker exec nextcloud-db mariadb-upgrade -u root -pPASSWORD --force
This updates the MySQL system tables to match the new MariaDB version. It is safe to run and will not affect your Nextcloud data.
How to Prevent This from Happening Again
Once you have recovered your instance, take steps to ensure this never happens again. Prevention is straightforward once you understand why the problem occurs.
Never Use the nextcloud:latest Tag
The nextcloud:latest tag is the single biggest cause of skipped major versions. It always points to the newest release, including new major versions, so any docker-compose pull can silently upgrade you past a major version boundary.
Instead, always pin your image to a specific major version at minimum, and ideally to a specific patch version. Use nextcloud:28 rather than nextcloud:latest, or better yet nextcloud:28.0.12-apache for maximum control.
This way, docker-compose pull only pulls patch updates within the version you have chosen. You decide when to move to a new major version, and you can do it deliberately.
Pin Versions in docker-compose.yml
Update your docker-compose.yml to use explicit version tags. Change image: nextcloud:latest to image: nextcloud:30.0.4-apache or whatever specific version you want to track.
When you are ready to upgrade to a new major version, change the tag to the next major version, restart, run occ upgrade, confirm everything works, and then pin the new version. Never jump more than one major version at a time.
Shell Script to Track and Pin Your Version
You can automate version tracking with a simple script that checks your current image tag against the latest available tags on Docker Hub. Here is a starting point you can adapt:
#!/bin/bash
CURRENT=$(docker inspect --format='{{.Config.Image}}' nextcloud-app 2>/dev/null | grep -oP 'nextcloud:K[^ ]+')
LATEST=$(curl -s "https://hub.docker.com/v2/repositories/library/nextcloud/tags/?page_size=10" | grep -oP '"name":s*"(d+.d+.d+)' | head -1 | grep -oP 'd+.d+.d+')
echo "Current: nextcloud:$CURRENT"
echo "Latest: nextcloud:$LATEST"
Run this before any docker-compose pull to see whether a new major version is available. If the major version number differs, upgrade manually one step at a time rather than pulling latest.
Considerations for Nextcloud AIO
If you run Nextcloud AIO (All-In-One), the recovery process differs because AIO manages its own containers and upgrade path. AIO is designed to handle version transitions automatically, but a skip can still occur if the master container is updated while a major version is skipped.
For AIO, the safest approach is to check the AIO interface for any error messages and consult the AIO documentation for version-specific recovery steps. The underlying principle is the same: you need to return to the version your database expects before upgrading forward.
Frequently Asked Questions
What causes Nextcloud to get stuck after a skipped major version upgrade?
Nextcloud gets stuck because each major version includes database migration scripts that expect the database to be at the immediately preceding major version. When you skip a version, those scripts find the wrong database state and abort, triggering the ‘Updates between multiple major versions and downgrades are unsupported’ error.
How do I fix the ‘Updates between multiple major versions and downgrades are unsupported’ error?
Fix this error by downgrading your Docker image to the version your database reports, editing version.php to match that version, starting the container, running ‘occ upgrade’, and then upgrading one major version at a time until you reach your target version.
How do I edit version.php in a Nextcloud Docker container?
Edit version.php using sed through docker exec since the container has no text editor installed. For example: ‘docker exec -u www-data nextcloud-app sed -i u0022s/30, 0, 4, 1/28, 0, 12, 1/u0022 version.php’. You can also edit the file on the host if the volume is bind-mounted.
What is the correct upgrade path when I accidentally skip a Nextcloud major version?
The correct path is to roll back to the version your database expects, confirm version.php matches, run occ upgrade, then upgrade sequentially through each intermediate major version one at a time. Never attempt to jump forward or downgrade data directly.
How do I use docker exec to run Nextcloud occ commands for recovery?
Run occ commands with ‘docker exec -u www-data nextcloud-app php occ’ followed by the command. For example, ‘docker exec -u www-data nextcloud-app php occ upgrade’ runs the upgrade, and ‘docker exec -u www-data nextcloud-app php occ maintenance:mode u002du002doff’ disables maintenance mode.
Can I downgrade a Nextcloud Docker instance after an accidental upgrade?
You can roll back the Docker image, but you cannot downgrade the data. The fix is to roll back the image, edit version.php to match the database version, and then upgrade forward step by step. Nextcloud does not support true data downgrades.
What should I do if Nextcloud apps show ‘files were not correctly replaced’ errors?
This means the app files on disk do not match the version declared in version.php. Fix it by ensuring the Docker image tag matches version.php, restarting the container, and running ‘occ upgrade u002du002dforce-apps’ to re-extract the correct app files.
How do I prevent skipped version upgrades in Nextcloud Docker?
Prevent this by never using the nextcloud:latest tag. Instead, pin your image to a specific version in docker-compose.yml, such as nextcloud:30.0.4-apache. Only change one major version at a time, and always run occ upgrade between version changes.
Conclusion
Recovering a Nextcloud Docker instance after a skipped major version upgrade comes down to three phases: roll back the image and align version.php with your database, run occ upgrade at the rolled-back version, and then step forward through each major version one at a time. The procedure is methodical, and your data survives as long as you follow the steps in order.
The most important takeaway is prevention. Pin your Docker image tags, never use nextcloud:latest, and always upgrade one major version at a time. A few minutes of version management saves hours of recovery work.
If you are currently in the middle of recovery, take a breath, back up your volumes, and work through the steps above. The process works, and your files will be waiting when you finish.