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>
This commit is contained in:
2026-07-07 01:18:01 +02:00
commit 2626036a21
9 changed files with 1311 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
# 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
```bash
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`:
```bash
# 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](https://nextcloud.com/changelog/) 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:
```bash
./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](https://packages.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:
```bash
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:
```bash
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:
```bash
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:
```bash
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.