Files
nextcloud-to-aio/README.md
T
dillard 2626036a21 Initial commit: Nextcloud bare/Hansson → AIO migration toolkit
Scripts 01-05 voor sequentiële migratie: transfer, staging, upgrade-keten,
AIO-install en DB-import. PostgreSQL + MySQL/MariaDB bronnen ondersteund.
Proxmox/OPNSense VM-builder optioneel in proxmox/. README met 12 gotchas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 01:18:01 +02:00

9.0 KiB
Raw Blame History

nextcloud-to-aio

Migration toolkit: Nextcloud bare/Hansson install → Nextcloud All-in-One (AIO).

Tested path: NC v30 (Debian 12, Apache + PHP-FPM + PostgreSQL) → AIO v13 (NC v33) on Debian 13. Works for any NC version that can be upgraded sequentially to the AIO target version.


What it does

  1. Transfer — rsync files + database dump from the old server to the new VM
  2. Build staging — minimal Apache + PHP + DB stack to run the upgrade chain
  3. Upgrade — sequential major-version upgrades (e.g. v30 → v31 → v32 → v33)
  4. Install AIO — Docker + AIO mastercontainer; guided wizard
  5. Import — restore the upgraded DB into AIO, carry over instanceid/passwordsalt/secret

The data directory (/mnt/ncdata) is rsynced once upfront and delta-synced at cutover. Downtime window is only the final delta-sync + DNS cutover — typically under an hour.


Prerequisites

  • New VM with Debian 12/13, SSH root access, enough disk for data
  • Old server accessible via SSH from the new VM (key-based)
  • DNS control for your NC domain
  • ~/.ssh/config aliases set up for all hosts

Optional:

  • Proxmox for live snapshots between upgrade steps (USE_PROXMOX=true)
  • OPNSense with public VIP routing (proxmox/build-vm.sh)

Quick start

cp config.sh config.local.sh   # never commit this file
# edit config.local.sh — fill in all variables
source config.local.sh

./01-transfer.sh                # start background rsync (can take hours)
./02-build-staging.sh           # build Apache+PHP+DB on target
./03-upgrade.sh                 # sequential NC upgrade chain
./04-install-aio.sh test        # AIO on staging domain first
# → complete wizard in browser at https://<target>:8080
./05-import.sh                  # import DB + data into AIO

# After verification:
./04-install-aio.sh production  # switch to production domain + LE cert
./05-import.sh                  # re-run import on production AIO

Configuration reference (config.sh)

Variable Description
SOURCE_HOST IP/hostname of the existing NC server
SOURCE_NC_PATH Path to NC app directory (default /var/www/nextcloud)
SOURCE_DATA_PATH Path to ncdata (default /mnt/ncdata)
SOURCE_DB Database name on source
SOURCE_DB_TYPE postgres or mysql
SOURCE_DB_PASS DB password — only needed for MySQL
TARGET_SSH SSH target for new VM (root@10.x.x.x or alias)
TARGET_DATA_PATH Where ncdata lives on target
NC_DOMAIN Production domain for AIO + Let's Encrypt
NC_STAGING_DOMAIN Staging domain (HTTP only, internal)
STAGING_DB_PASS Password for staging DB (created in step 02)
NC_UPGRADE_VERSIONS Array of NC versions to upgrade through
USE_PROXMOX true/false — enable snapshot after each upgrade step
PROXMOX_HOST SSH alias for PVE node
PROXMOX_VMID VM ID for snapshots

MySQL → PostgreSQL

AIO uses PostgreSQL exclusively. If your source uses MySQL/MariaDB, you need one extra step after the upgrade chain and before 05-import.sh:

# On the target VM, after 03-upgrade.sh completes:
sudo -u www-data php8.3 /var/www/nextcloud/occ \
    db:convert-type --all-apps pgsql \
    <staging_db_user> <staging_db_pass> <staging_db_name>

This converts the staging MariaDB to PostgreSQL in-place. After conversion, 05-import.sh takes a pg_dump of that PostgreSQL DB and imports it into AIO.


Upgrade path

NC requires sequential major-version upgrades — you cannot skip a major. Set NC_UPGRADE_VERSIONS in config.sh based on your source version:

Source Example path
v28/v29 ("29.0.x" "30.0.x" "31.0.x" "32.0.x" "33.0.x")
v30 ("31.0.14" "32.0.12" "33.0.6")
v32 ("33.0.6")

Check the NC release archive for the latest patch version per major. AIO ships a specific NC version — check the AIO release notes to know which final version to target.


Proxmox: creating the VM

If you use Proxmox, proxmox/build-vm.sh creates the target VM automatically: Debian 13 cloud image, separate LVM data disk, loopback VIP (optional), OPNSense routing (optional).

Fill in the Proxmox VM section of config.sh, then:

./proxmox/build-vm.sh

After the VM is up, set TARGET_SSH to match VM_PRIVATE_IP and continue with 01-transfer.sh.


Known issues & gotchas

These caused real failures during development. Read before running.

PHP version

Debian 13 ships PHP 8.4. NC v30 requires PHP ≤ 8.3. 02-build-staging.sh adds the sury.org repo for PHP 8.3 automatically. If your source is NC v32+, you can remove the sury.org step and use the system PHP.

rsync --exclude anchoring

--exclude='config/' matches any directory named config in the tree, including apps/someapp/config/. Use --exclude='/config/' (leading slash) to anchor it to the root of the source — this is what the scripts use.

apc.enable_cli

APCu is disabled in CLI by default (apc.enable_cli=0). occ commands that touch APCu caches silently fail or produce wrong results. 02-build-staging.sh sets apc.enable_cli=1 in /etc/php/8.3/cli/php.ini automatically.

require vs include for config.php

NC's config.php defines $CONFIG but does not return it. include('/path/to/config.php') returns 1 (bool), not the config array. Use require instead — the scripts do this.

Apps without appinfo/info.xml

Third-party apps that are present in /var/www/nextcloud/apps/ but lack appinfo/info.xml (abandoned, partially deleted, or leftover) cause occ upgrade to crash. 03-upgrade.sh removes them automatically before each upgrade step.

AIO mastercontainer port binding

The AIO mastercontainer must not bind ports 80, 443, or 3478. AIO's apache sub-container manages those ports itself. Binding them on the mastercontainer causes the domain check to fail with a conflict — two processes listening on port 443. 04-install-aio.sh does not bind those ports.

AIO container names (v13+)

Older AIO docs refer to nextcloud-aio-postgresql — this was renamed. Current names used by 05-import.sh:

Role Container/resource name
DB container nextcloud-aio-database
Database nextcloud_database
NC DB user oc_nextcloud
DB superuser nextcloud

CPU spike on first AIO start after migration

When AIO starts for the first time with existing data, Elasticsearch indexes all files, ClamAV downloads its virus database, and Recognize scans all photos. On a large installation (500K+ files) this can take 12 hours at high CPU load.

Plan this for late evening — do not run the first AIO start during business hours or when the server is serving other workloads.

instanceid / passwordsalt / secret

These three values in config.php are the identity of the NC instance. They must match what is in the database. 05-import.sh reads them from the staging stack and writes them into the AIO container's config. If the staging stack has been removed before running 05-import.sh, note these values from the source config.php and set them manually.

systemd-resolved blocking DNS

On Debian 12/13, systemd-resolved can intercept DNS and cause resolution failures inside Docker containers. If containers cannot resolve hostnames:

systemctl disable --now systemd-resolved
echo "nameserver 1.1.1.1" > /etc/resolv.conf

Docker DNS cache after IP change

Docker caches the upstream DNS from /etc/resolv.conf at daemon start. After a VM IP change or network reconfiguration, containers may get DNS timeouts even though the host resolves correctly. Fix:

systemctl restart docker
docker start $(docker ps -aq)

notify-push after import

05-import.sh stops nextcloud-aio-notify-push to release DB connections. After import, restart it via the AIO admin UI or:

docker start nextcloud-aio-notify-push

OPNSense: fw01 root shell is csh

If you use OPNSense, its root shell is csh. Shell redirects like 2>/dev/null are interpreted differently by csh and can corrupt commands. Always wrap non-trivial commands in sh -c '...' when SSHing to OPNSense. proxmox/build-vm.sh does this for all fw01 commands.


After import: apps to reinstall

03-upgrade.sh prints a list of apps that need manual reinstall after AIO import. These are apps that were enabled in your source but are not included in AIO. Common examples:

docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install occweb
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install drawio
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install maps
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:install extract
docker exec nextcloud-aio-nextcloud php /var/www/html/occ app:enable  notes

AIO includes natively (no action needed): fulltextsearch, notify_push, Talk/spreed, whiteboard, recognize, files_antivirus (ClamAV), imaginary.