<!-- prepared by Claude Code with Sonnet 5.5, shortened and anonymized -->

# Nextcloud 31 → 33 on a server with PHP 8.5

Nextcloud 31 and 32 do not run on PHP 8.5, Nextcloud 33 does. Major versions cannot be skipped, so: **31 → 32 with a temporary PHP 8.3 in Docker** (only `occ` runs in the container), then **32 → 33 with the native PHP 8.5**. Code, `config/` and `data/` stay on the host.

Sources: [upgrade notes](https://docs.nextcloud.com/server/stable/admin_manual/release_notes/upgrade_to_33.html), [PHP compatibility table](https://github.com/nextcloud/server/wiki/Releases-and-PHP-versions)

Assumptions (example.com): Nextcloud in `/var/www/html/nextcloud`, owned by `www-data`; Apache + php-fpm; MySQL/MariaDB via socket `/var/run/mysqld/mysqld.sock`; no root, but `sudo` works; no Redis. Adapt paths if yours differ.

## 0. Preparation

- Make a full backup (code, `config/`, `data/`, DB dump) and check it.
- Check that your third-party apps have releases for 32 and 33; the others are disabled during the upgrade.
- Run everything in `tmux` or `screen`.

## 1. PHP 8.3 image and `occ` wrapper

```bash
mkdir -p ~/nc-docker && cd ~/nc-docker
cat > Dockerfile <<'EOF'
FROM php:8.3-cli
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions \
 && install-php-extensions pdo_mysql gd intl zip bcmath gmp apcu exif opcache pcntl sysvsem imagick
RUN printf "memory_limit=2G\napc.enable_cli=1\n" > /usr/local/etc/php/conf.d/nc.ini
EOF
sudo docker build -t nc-php83 .

cat > nc-occ <<'EOF'
#!/bin/sh
exec sudo docker run --rm -it --network host \
  --user "$(id -u www-data):$(id -g www-data)" \
  -v /var/www/html/nextcloud:/var/www/html/nextcloud \
  -v /var/run/mysqld:/var/run/mysqld \
  -w /var/www/html/nextcloud nc-php83 \
  php -d pdo_mysql.default_socket=/var/run/mysqld/mysqld.sock occ "$@"
EOF
chmod +x nc-occ
./nc-occ status
```

The same path inside and outside the container keeps the absolute `datadirectory` valid. Debian's PHP looks for the MySQL socket in `/var/run/mysqld/`, the Docker image does not, hence the mount **and** the `-d pdo_mysql.default_socket=…`; without it `occ` fails with `SQLSTATE[HY000] [2002] No such file or directory`. If `dbhost` is a TCP address or a different socket, adapt accordingly; mount a data directory outside the tree with another `-v`.

## 2. Download-and-swap helper

Define once in your shell (`$1` = target major version, `$2` = name for the old tree). It downloads the release, replaces the tree and carries over `config.php`, `data/`, third-party apps and themes. Check that `sha256sum` prints OK for the tarball.

```bash
nc_swap() {
  cd /var/www/html && sudo rm -rf nextcloud-new
  sudo -u www-data mkdir nextcloud-new && cd nextcloud-new
  sudo -u www-data curl -LO https://download.nextcloud.com/server/releases/latest-$1.tar.bz2
  sudo -u www-data curl -LO https://download.nextcloud.com/server/releases/latest-$1.tar.bz2.sha256
  sha256sum -c latest-$1.tar.bz2.sha256     # tarball line must say OK
  sudo -u www-data tar xjf latest-$1.tar.bz2
  cd ..
  sudo -u www-data mv nextcloud $2
  sudo -u www-data mv nextcloud-new/nextcloud nextcloud
  sudo -u www-data cp -a $2/config/config.php nextcloud/config/
  sudo -u www-data mv $2/data nextcloud/data      # skip if datadirectory is elsewhere
  for a in $2/apps/*; do n=$(basename "$a"); [ -e "nextcloud/apps/$n" ] || sudo -u www-data cp -a "$a" nextcloud/apps/; done
  sudo -u www-data cp -a $2/themes/. nextcloud/themes/ 2>/dev/null
  grep OC_VersionString nextcloud/version.php
}
```

## 3. Upgrade 31 → 32 (PHP 8.3 in Docker)

```bash
~/nc-docker/nc-occ maintenance:mode --on
nc_swap 32 nextcloud-31-old
cd /var/www/html
~/nc-docker/nc-occ upgrade
~/nc-docker/nc-occ app:update --all
~/nc-docker/nc-occ db:add-missing-indices
~/nc-docker/nc-occ status                  # must report 32.x, maintenance stays on
```

## 4. Upgrade 32 → 33 (native PHP 8.5)

```bash
nc_swap 33 nextcloud-32-old
cd /var/www/html/nextcloud
sudo -u www-data php occ upgrade
sudo -u www-data php occ app:update --all
sudo -u www-data php occ db:add-missing-indices
sudo -u www-data php occ maintenance:repair --include-expensive
sudo -u www-data php occ maintenance:mode --off
```

Newer releases (34, 35, …) can then be installed with the web updater. If stale code is served, reload php-fpm.

## 5. After the upgrade

Check Administration → Overview and a sync client. A new PHP version installs default `php.ini` files, so earlier settings are lost; for the web, the file that counts is the php-fpm one:

```bash
sudo sed -i 's/^memory_limit = 128M/memory_limit = 512M/' /etc/php/8.5/fpm/php.ini
sudo systemctl reload php8.5-fpm
```

Also check `upload_max_filesize`, `post_max_size` and `max_execution_time` there.

## 6. Clean up (once 33 runs fine)

```bash
# Docker: helper image, base image, leftovers
sudo docker rmi nc-php83 php:8.3-cli
sudo docker system df
rm -rf ~/nc-docker

# old code trees
sudo rm -rf /var/www/html/nextcloud-31-old /var/www/html/nextcloud-32-old /var/www/html/nextcloud-new
```

