Upgrade an archive-based Nextcloud installation from the command line. The script downloads a stable release, backs up the database and configuration, manages maintenance mode and services, and runs the upgrade. It keeps the old application for recovery. Data backups and off-server copies are optional.
- A single Nextcloud installation on Linux with systemd.
- Nginx with PHP-FPM, or Apache with PHP-FPM or
mod_php. - MariaDB/MySQL or PostgreSQL, with the matching client and dump tools installed.
- Bash, GNU tools, PHP CLI with SimpleXML,
sudo,curl,pgrep,flock,findmnt,tarandbzip2. Account cron management also needscrontab. - Monit for the default service manager, or
SERVICE_MANAGER=systemd. - Space for the release, staged application and selected backups.
ZIP releases need unzip/zipinfo. Data backups need rsync and a destination
supporting Unix ownership, permissions, ACLs and extended attributes. SSH backup
destinations need tar and GNU sync on the remote server.
Use your provider's updater for Docker/AIO, Snap, appliances or distribution packages. Clusters, remote web/PHP services, SQLite, Oracle, external configuration directories and external/nested app directories are unsupported. Database connections needing extra TLS options or custom PostgreSQL ports are unsupported.
Installation and backup directories must be existing absolute paths without
symlinks. The application directory cannot be a mount point or contain mounts.
External data directories may contain mounts. Application symlinks must be
relative links to files within the installation, outside user data; configuration
links must stay within config/. Directory links and broken links are unsupported.
Download the archive and checksum from Releases. For example:
release=v1.1.0
url="https://github.com/msanguineti/nextcloud-scripts/releases/download/$release"
curl --fail --location --remote-name "$url/nextcloud-upgrade-$release.tar.gz"
curl --fail --location --remote-name "$url/nextcloud-upgrade-$release.tar.gz.sha256"
sha256sum -c "nextcloud-upgrade-$release.tar.gz.sha256"
tar -xzf "nextcloud-upgrade-$release.tar.gz"
cd "nextcloud-upgrade-$release"Verify the checksum succeeds before extracting. The package contains the script, README and license. Alternatively, clone the repository:
git clone https://github.com/msanguineti/nextcloud-scripts.git
cd nextcloud-scriptsInstall the script and create the default backup directory:
sudo install -o root -g root -m 0755 nextcloud-upgrade.sh /usr/local/sbin/nextcloud-upgrade
sudo install -d -o root -g root -m 0700 /var/backups/nextcloud
nextcloud-upgrade --versionTo update, download a newer release or update your checkout, then repeat the installation command. Release packages require neither Git nor Docker.
Before upgrading:
- Check the target release's requirements and upgrade notes, including CLI/web PHP versions, extensions and database compatibility.
- Check third-party app compatibility and disable incompatible apps.
- Confirm your data backup is current, or enable
BACKUP_DATA=yes. - Pause systemd timers, system cron entries and external CLI workers. The script manages account crontab entries only in cron mode. Stopping shared web/PHP services also interrupts other sites using them.
Find and install the next eligible stable update:
sudo nextcloud-upgradeReview the versions, paths, services, background-job mode and backup settings at
confirmation. Enter y to proceed. Long preparation steps show a spinner and
elapsed time in a terminal, or status lines every 15 seconds in redirected output.
After enabling maintenance, the script allows 30 seconds for active
requests before stopping services and taking backups.
To use a 60-second settling pause:
sudo env WAIT_AFTER_MAINTENANCE=60 nextcloud-upgradeTo select a maintenance release within your installed major version:
sudo nextcloud-upgrade 34.0.5Use automatic discovery for major upgrades, after installing the latest maintenance release of your current major version. Downgrades, skipped major versions and automatic upgrades from non-stable channels are rejected.
After upgrading, check login, file upload/download, client sync and Administration Overview. Confirm background jobs resume, re-enable compatible apps and complete any recommended database maintenance before another major upgrade.
nextcloud-upgrade --help
nextcloud-upgrade --versionColoured [✓], [!] and [x] indicate success, warnings and errors. Use
NO_COLOR=1 to disable colours. Redirected output has no colour escapes.
Defaults are Monit, Nginx, PHP 8.4 FPM and www-data. Keep
SERVICE_MANAGER=monit when Monit supervises the selected services. Monit names
must match their systemd units. Services must already be running and, with
Monit, monitored. Start with maintenance mode disabled.
For Nginx/PHP-FPM managed directly by systemd:
sudo env SERVICE_MANAGER=systemd nextcloud-upgradeFor Apache with embedded PHP:
sudo env SERVICE_MANAGER=systemd WEB_SERVICE=apache2 PHP_SERVICE=none nextcloud-upgradeUse WEB_SERVICE=httpd if that is your service name. For separate PHP-FPM, set
PHP_SERVICE to its unit name. Set WEB_USER, WEB_GROUP and PHP_BIN to match
your installation. In systemd mode, pause other supervisors or activation
mechanisms that could restart these services.
The script detects and preserves your background-job setting:
| Mode | During the upgrade |
|---|---|
| Cron | Suspends this installation's entries in WEB_USER's crontab and restores them afterward. |
| WebCron | Maintenance mode and stopped web services prevent web-triggered jobs. The external schedule is unchanged. |
| AJAX | Maintenance mode and stopped web services prevent page-triggered jobs. |
| Disabled | Jobs remain disabled. |
Cron is recommended by Nextcloud. For systemd timers, system cron or other external schedulers, pause and resume them yourself. You may also pause your WebCron provider to avoid failed requests during downtime. Cron mode without an account crontab is allowed with a warning.
Database and configuration backups are always created. Data backup is off by default. Database settings and the data directory are read from Nextcloud.
To back up data and create a recovery bundle on a mounted destination:
sudo env BACKUP_PATH=/mnt/backups/nextcloud \
BACKUP_DATA=yes BACKUP_ARCHIVE=yes nextcloud-upgradeCreate the destination first, owned by root and not writable by group or others. It must be outside the installation and data directories. Check that a mounted destination is actually mounted before running the script.
Local storage, including /var/backups/nextcloud, is supported. An off-server
copy provides protection against server or disk failure. Storage advisories do
not block the upgrade.
To send an additional recovery archive over SSH:
sudo env BACKUP_SSH_DEST=backup@backup-server:/srv/backups/nextcloud nextcloud-upgradeCreate the remote directory and configure noninteractive SSH access and a
verified host key for root. Use an SSH host alias for custom ports or IPv6.
Local backups are retained; the SSH copy must succeed before migration. Add
BACKUP_DATA=yes to include filesystem data.
Data copies are verified and can extend downtime substantially. Pause other
applications writing directly to the data directory. Allow space for a full
copy and, with BACKUP_ARCHIVE=yes, an additional tar bundle. Old backups are
not deleted automatically.
Data inside the installation keeps its permissions when moved to the new
application. The old application directory contains no separate data copy.
BACKUP_DATA includes nested mounts in an external data directory. External
storage connected through apps needs separate backups. Primary object storage
cannot use BACKUP_DATA=yes.
Each confirmed run creates
BACKUP_PATH/upgrade-SOURCE_VERSION-TIMESTAMP.RANDOM, for example
upgrade-34.0.4.1-20261015T120000Z.A1B2C3D4.
| Item | Contents |
|---|---|
database.sql.gz |
Pre-upgrade database dump. |
config/, .htaccess, .user.ini |
Configuration and server overrides, when present. |
custom/ |
Preserved apps, themes and selected files. |
data/ |
Data copy, only with BACKUP_DATA=yes. |
backup.tar, backup.tar.sha256 |
Optional recovery bundle and checksum. |
manifest, phase, failure, upgrade.log |
Versions, paths, background-job mode, status and diagnostic output, when available. |
cron.original, cron.disabled |
Account cron snapshots, when account cron is managed. |
The previous application remains beside the installation as .old.upgrade-….
Bundles and SSH archives contain the backup set; keep the old application
separately. Files ending in .part or .partial are incomplete.
- Before database migration: the script attempts to restore the original application and resume normal operation. Check the reported result.
- After migration starts: services remain stopped and maintenance enabled. Restore the matching database, configuration and application version. Restore data too if it changed or was lost.
Do not rerun blindly. Read BACKUP_PATH/upgrade-active and the referenced
recovery directory's manifest, phase, failure and upgrade.log. Resolve the
failure before removing the active marker. After a reboot, check that access is
blocked; the marker prevents another upgrade but does not stop services starting.
To inspect and extract a recovery bundle into an empty directory:
cd /path/to/upgrade-SOURCE_VERSION-TIMESTAMP.RANDOM
sha256sum -c backup.tar.sha256
tar -tf backup.tar
sudo tar --acls --xattrs --numeric-owner -xpf backup.tar -C /path/to/empty/restore-directoryFollow Nextcloud's restore guide.
Import SQL into a clean database and use the application version recorded in
manifest. Check ownership and restore compatible custom files before resuming
services and background jobs. If the data backup is outdated, follow the guide's
client-synchronization instructions. Configuration and SQL cannot recover lost
user files. Keep recovery files until the upgraded installation is verified.
The script preserves config.php, configuration fragments, other local configuration
files, custom themes, additional app directories in apps_paths, and third-party
apps absent from the new release. Unmodified shipped files, including .user.ini,
.htaccess and configuration examples, use the new release's defaults. Nextcloud
regenerates its installation-specific .htaccess section during the upgrade.
Modified shipped configuration files are retained with a warning. Review them against the new defaults; they may trigger integrity warnings. Changed configuration templates are compared with reference files fetched from Nextcloud's official GitHub source for the installed version. If original hashes or reference files are unavailable, existing files are retained unless identical to the new defaults. Recovery backups still contain the complete old configuration and both overrides.
Shipped app versions take precedence; removed shipped apps are not carried forward. Check app/theme compatibility before upgrading.
For additional files, create /root/nextcloud-preserve.txt:
# Paths relative to the Nextcloud installation
local-assets
my custom page.html
sudo env PRESERVE_PATHS_FILE=/root/nextcloud-preserve.txt nextcloud-upgradeUse one existing file or directory per line, without quotes or wildcards. Hidden files are included; blank lines and comments are ignored. Do not list data or paths already preserved automatically. Absolute paths, overlapping paths, paths through symlinks and conflicts with release content are rejected.
Pass settings with sudo env NAME=value ... nextcloud-upgrade.
| Setting | Default | Description |
|---|---|---|
NEXTCLOUD_PATH |
/var/www/nextcloud |
Installation directory. |
BACKUP_PATH |
/var/backups/nextcloud |
Recovery backup destination. |
SERVICE_MANAGER |
monit |
monit or systemd. |
WEB_SERVICE |
nginx |
Web-server service name. |
PHP_SERVICE |
php8.4-fpm |
PHP-FPM service, or none for embedded PHP. |
WEB_USER |
www-data |
HTTP account and owner of the managed crontab. |
WEB_GROUP |
Primary group of WEB_USER |
Application file group. |
PHP_BIN |
php |
PHP CLI executable; an absolute path is recommended. |
BACKUP_DATA |
no |
Copy and verify filesystem data (yes/no). |
BACKUP_ARCHIVE |
no |
Create a tar bundle and checksum (yes/no). |
BACKUP_SSH_DEST |
Unset | Additional archive at [user@]host:/absolute/directory. |
PRESERVE_PATHS_FILE |
Unset | File listing extra installation-relative paths. |
WAIT_AFTER_MAINTENANCE |
30 |
Grace period before service shutdown; 0 skips it. |
WAIT_BEFORE_BACKUP |
60 |
Maximum wait for running cron/occ processes. |
WAIT_AFTER_SERVER_START |
20 |
Maximum wait for each service state change. |
DOWNLOAD_FORMAT |
tar.bz2 |
tar.bz2 or zip. |
CHECKSUM_TYPE |
sha256 |
sha256, sha512 or md5. |
NO_COLOR |
Unset | Disable terminal colours. |
Release checksums come from the archive's HTTPS server; they are not independent signature verification. Prefer SHA256 or SHA512 over MD5.
On Debian 13, install the development tools and run all checks:
sudo apt update
sudo apt install git python3 shellcheck php8.4-cli php8.4-xml rsync tar bzip2 unzip
git clone https://github.com/msanguineti/nextcloud-scripts.git
cd nextcloud-scripts
TEST_PHP=/usr/bin/php8.4 bash .github/scripts/check.shTests need PHP 8.4 or newer with SimpleXML. They use temporary files and simulated
services, databases and GitHub requests; no root or live Nextcloud server is
needed. The runner uses two workers and reports slow tests. Use TEST_JOBS=1
for sequential execution. To run one test:
TEST_PHP=/usr/bin/php8.4 python3 -B -m unittest discover -s tests -k test_zip_success -vWith Docker available, build once and run checks without installing the project dependencies on your system:
docker build -t nextcloud-script-dev .devcontainer
docker run --rm --user "$(id -u):$(id -g)" \
--mount "type=bind,source=$PWD,target=/workspace,readonly" \
nextcloud-script-devRebuild after changing the Dockerfile. The source mount is read-only and the
container is removed afterward. Add -e TEST_JOBS=1 before the image name for
sequential tests.
nextcloud-upgrade.sh contains the upgrade and recovery functions. set_phase
records recovery state; on_exit handles failures and interruption. Database
migration marks the boundary for manual recovery.
CI checks PRs and pushes to main. To release, update SCRIPT_VERSION in a PR
and merge it. Run Actions → Prepare release → Run workflow on main.
Review the draft's archive, checksum and release notes, then Publish. An
existing tag or release for that version causes an error. Immutable releases
are supported.
Protect main with required PRs and passing Checks, and restrict the release
environment to main. If v* tag creation is restricted to automation, install
a GitHub App with Contents: read and write, allow it in the tag ruleset, and
set RELEASE_APP_CLIENT_ID (environment variable) and RELEASE_APP_PRIVATE_KEY
(environment secret) in GitHub's release environment.
To build a local package:
bash .github/scripts/package-release.sh v1.1.0Downloads are written to dist/.
MIT. Not affiliated with or endorsed by Nextcloud.