Skip to content

Nextcloud AIO "Upgrade to Latest Major": Benign Stream Error and the 33/34 Version Split

When you use the Nextcloud All-in-One (AIO) "Upgrade to latest major" button to jump ahead of the stable channel (e.g. 33.x → 34), two things can make a perfectly healthy instance look broken:

  1. The mastercontainer logs a scary RuntimeException: Unable to read from stream during the upgrade.
  2. Afterward, the AIO web interface and the container's environment report the old major (33.x) while occ reports the new one (34.x) — the instance appears to disagree with itself.

Both are expected. This article explains why, how to confirm the upgrade actually succeeded, and how to verify the instance is safe across restarts.

Symptom

  • The AIO mastercontainer log shows, during RunNextcloudUpgradeToLatestMajor:
NOTICE: PHP message: Slim Application Error
Type: RuntimeException
Message: Unable to read from stream
File: /var/www/docker-aio/php/vendor/guzzlehttp/psr7/src/Stream.php
...
#2 DockerActionManager->execCommandInContainer(...)
#3 DockerController->RunNextcloudUpgradeToLatestMajor(...)
  • The AIO interface shows Nextcloud on the stable-channel version (e.g. 33.0.6), but running occ status reports a newer major (e.g. 34.0.1).
  • The nextcloud-aio-nextcloud container's env still reads the old version:
NEXTCLOUD_VERSION=33.0.5

Investigation

Confirm what the running code actually is — check three independent sources. They will not all agree, and that's the whole point:

# 1. What the application actually runs (authoritative)
docker exec -u www-data nextcloud-aio-nextcloud php occ status | grep -E "versionstring|maintenance|needsDbUpgrade"
#   versionstring: 34.0.1
#   maintenance: false
#   needsDbUpgrade: false

# 2. The code on the persistent volume (authoritative, matches #1)
docker exec nextcloud-aio-nextcloud sh -c 'grep OC_VersionString /var/www/html/version.php'
#   $OC_VersionString = '34.0.1';

# 3. The container wrapper env (STALE by design — reports the stable-channel major)
docker inspect nextcloud-aio-nextcloud --format '{{range .Config.Env}}{{println .}}{{end}}' | grep NEXTCLOUD_VERSION
#   NEXTCLOUD_VERSION=33.0.5

# The AIO flag that pins the target major (set by the "upgrade to latest major" button)
docker exec nextcloud-aio-mastercontainer sh -c 'cat /mnt/docker-aio-config/data/configuration.json' \
  | python3 -c 'import sys,json;print("install_latest_major =", json.load(sys.stdin).get("install_latest_major"))'
#   install_latest_major = 34

Correlate the timestamps to prove the upgrade completed despite the stream error:

docker exec nextcloud-aio-nextcloud sh -c 'stat -c "%y" /var/www/html/version.php'

You will typically see version.php rewritten to the new major within a minute of the Unable to read from stream error — i.e. the upgrade continued and finished; only the log-follow stream broke.

Root Cause

The "Unable to read from stream" error is cosmetic

The "Upgrade to latest major" action runs occ upgrade inside the nextcloud container via a Docker exec, and the mastercontainer follows that exec's output stream to display live progress in the UI (the execCommandInContainer → Guzzle Stream->read() path in the stack trace). If that streamed exec connection is interrupted — a mastercontainer restart, a timeout, the UI navigating away — the PHP layer throws RuntimeException: Unable to read from stream. The underlying occ upgrade keeps running to completion inside the container. The error is about reading the progress log, not about the upgrade itself.

Confirm success the normal way: needsDbUpgrade: false, maintenance: false, and all AIO containers healthy.

The 33/34 split is by design (install_latest_major)

AIO's stable channel deliberately lags mainline Nextcloud by a few weeks. When you click "Upgrade to latest major", AIO sets install_latest_major to the target major (e.g. 34) and upgrades the code on the nextcloud_aio_nextcloud volume to that major — but it keeps running that code under the stable-channel container image and its NEXTCLOUD_VERSION env until the stable channel itself ships the new major.

So the steady state is intentional:

Source Reports Meaning
occ status / version.php 34.0.1 the code actually executing
container env NEXTCLOUD_VERSION 33.0.5 the stable-channel image the wrapper came from
AIO web UI 33.0.6 what the stable channel offers existing instances
install_latest_major flag 34 tells the entrypoint to keep the 34 volume, not downgrade

The container entrypoint sees the installed version is newer than its image's bundled version, checks install_latest_major, and starts against the newer volume without complaint instead of triggering AIO's usual downgrade guard. The env number self-corrects to 34 only once AIO's stable channel reaches 34 and a normal update recreates the container with a 34 image.

Do not "fix" the NEXTCLOUD_VERSION env var. It is not a fault, and hand-editing the container is unsupported — AIO manages the docker run parameters.

The real risk: an un-recreated container

Immediately after a latest-major upgrade, the nextcloud container is often still the pre-upgrade one (created before the code was swapped) and has never been recreated. On many AIO installs a health watchdog runs a plain docker restart nextcloud-aio-nextcloud when the container is unhealthy for too long (see Nextcloud AIO Unhealthy 20h After Nightly Update). A reboot does the same. You want to be sure the container starts cleanly against the newer volume before one of those events forces the issue unattended.

Fix / Verification

Prove restart-safety with an AIO-orchestrated Stop → Start (a full recreate), not a manual docker command. First confirm you have a restore point:

# Borg backup archives (recovery points) — make sure a recent one exists
docker exec nextcloud-aio-mastercontainer sh -c 'tail -5 /mnt/docker-aio-config/data/backup_archives.list'

Then in the AIO interface (https://<host>:8080): Stop containers → wait → Start containers. Because install_latest_major pins the target major and the volume already holds the new code with needsDbUpgrade: false, AIO recreates the container and it comes up healthy on the new major with no migration and no downgrade error.

Verify from the outside:

curl -s -o /dev/null -w "HTTP %{http_code}\n" https://<domain>/status.php
curl -s https://<domain>/status.php | python3 -c \
  'import sys,json;d=json.load(sys.stdin);print({k:d[k] for k in ["installed","maintenance","needsDbUpgrade","versionstring"]})'
# HTTP 200
# {'installed': True, 'maintenance': False, 'needsDbUpgrade': False, 'versionstring': '34.0.1'}

Also confirm the entrypoint logged no version complaint:

docker logs nextcloud-aio-nextcloud 2>&1 | grep -iE "downgrade|not allowed|refus" || echo "no version complaints"

A clean cold Stop/Start proves the instance survives a reboot or a watchdog-triggered restart. The NEXTCLOUD_VERSION=33.x env will remain until AIO stable catches up — that is normal and safe as long as install_latest_major stays set.

See Also