Tutorials  /  Linux Basics

Back Up and Restore Nextcloud: Data, Database, Config

LLudwig · August 2026 ·10 min read ·Linux Basics, Tutorial

A Nextcloud instance keeps its state in three places: the data directory, the SQL database, and config/config.php. Back up only one of them and you get an installation that either refuses to start or starts with a file list that no longer matches what is on disk. This tutorial covers a consistent backup of all three parts and a restore you can verify.

What belongs in a Nextcloud backup?

A complete Nextcloud backup consists of three parts: the web root including config/config.php and the apps directory, the data directory with all user files, and a dump of the MySQL, MariaDB or PostgreSQL database.

The three parts are not interchangeable, and each one is useless without the others:

Component Typical path Why it matters
Web root /var/www/nextcloud Contains the code version, installed apps and themes
Configuration /var/www/nextcloud/config/config.php Holds instanceid, passwordsalt, secret and the DB credentials
Data directory /var/www/nextcloud/data User files, previews, encryption keys, .ocdata marker
Database nextcloud schema File index, shares, users, app state, activity

If server-side encryption is enabled, the keys in data/files_encryption and the secret value in config.php only work together. Restoring one without the other leaves the files unreadable.

Find out where the data directory actually lives before you start. On many installations it sits outside the web root:

Console
$ sudo -u www-data php /var/www/nextcloud/occ config:system:get datadirectory
/var/www/nextcloud/data

Prerequisites

  • A running Nextcloud 25 or newer on a Linux host, for example on a scalable Cloud-VPS
  • Root or sudo access, plus the PHP CLI binary used by your installation
  • The database credentials from config.php (dbname, dbuser, dbpassword)
  • A backup target with free space for the full data directory and the SQL dump
  • rsync and either mysqldump (MySQL/MariaDB) or pg_dump (PostgreSQL)

Read the credentials directly from the configuration instead of guessing them:

Console
$ sudo grep -E "dbname|dbuser|dbhost|dbtype" /var/www/nextcloud/config/config.php

Why does a Nextcloud backup need maintenance mode?

Maintenance mode blocks all client and web access to Nextcloud, so no user can upload a file between the moment the file copy runs and the moment the database dump is written. Without it, the two halves of the backup describe different states.

A desktop client that uploads a 2 GB file during the backup window is enough to produce a database row for a file that never made it into the copy. On restore, Nextcloud lists the file and returns a 404 when someone clicks it.

Console
$ sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --on
Maintenance mode enabled

The order of operations matters. Enable maintenance mode first, copy files, dump the database, then disable maintenance mode:

graph TD
  A["occ maintenance:mode --on"] --> B["rsync web root and data directory"]
  A --> C["mysqldump of the nextcloud database"]
  B --> D["Backup target: /mnt/backup/nextcloud"]
  C --> D
  D --> E["occ maintenance:mode --off"]
  E --> F["Verify with occ status and files:scan"]
VM

Matching infrastructure at centron

No hardware needed to follow along: ccloud³ VMs with full root access, billed by the hour and ready in seconds. Rent a cloud server →

Back up the files

Copy the web root and the data directory with rsync. The flags matter: -A keeps ACLs, -a keeps ownership, timestamps and permissions, -x prevents rsync from crossing into other mounted filesystems.

Console
$ sudo mkdir -p /mnt/backup/nextcloud/$(date +%F)
$ sudo rsync -Aax --delete /var/www/nextcloud/ /mnt/backup/nextcloud/$(date +%F)/nextcloud/

If the data directory is mounted separately, back it up as its own target so -x does not silently skip it:

Console
$ sudo rsync -Aax --delete /srv/nextcloud-data/ /mnt/backup/nextcloud/$(date +%F)/data/

The --delete flag makes the target an exact mirror of the source. Use it only when each run writes into a dated directory or when you keep separate snapshots on the storage layer. Pointing --delete at a single shared target turns your backup into a live mirror, which propagates deletions instead of protecting against them.

Back up the database

Dump the database while maintenance mode is still active. On MySQL and MariaDB, --single-transaction produces a consistent snapshot of InnoDB tables without locking them:

Console
$ sudo mysqldump --single-transaction --default-character-set=utf8mb4 \
  -u nextcloud -p nextcloud > /mnt/backup/nextcloud/$(date +%F)/nextcloud-db.sql

On PostgreSQL, use pg_dump with the Nextcloud database user:

Console
$ sudo -u postgres pg_dump nextcloud -f /mnt/backup/nextcloud/$(date +%F)/nextcloud-db.sql

Compress the dump afterwards. A 500 MB SQL file typically shrinks below 60 MB, and gzip is fast enough that it does not extend the maintenance window in any meaningful way:

Console
$ sudo gzip /mnt/backup/nextcloud/$(date +%F)/nextcloud-db.sql
$ sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --off

Automate the backup

Put the whole sequence into a script so the maintenance window is always closed, including when a step fails. set -euo pipefail aborts on the first error, and the trap guarantees that maintenance mode is turned off even then.

bash
#!/bin/bash
set -euo pipefail
NC_DIR="/var/www/nextcloud"
DEST="/mnt/backup/nextcloud/$(date +%F)"
OCC="sudo -u www-data php ${NC_DIR}/occ"
DB_NAME="nextcloud"
DB_USER="nextcloud"
DB_PASS="YOUR_DB_PASSWORD"
mkdir -p "${DEST}"
trap '${OCC} maintenance:mode --off' EXIT
${OCC} maintenance:mode --on
rsync -Aax --delete "${NC_DIR}/" "${DEST}/nextcloud/"
mysqldump --single-transaction --default-character-set=utf8mb4 \
  -u "${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" > "${DEST}/nextcloud-db.sql"
gzip "${DEST}/nextcloud-db.sql"
find /mnt/backup/nextcloud -maxdepth 1 -type d -mtime +14 -exec rm -rf {} +

Store the script as /usr/local/sbin/nextcloud-backup.sh, set the permissions to 0700 so the database password is not world-readable, and schedule it for a low-traffic hour:

Console
$ sudo chmod 700 /usr/local/sbin/nextcloud-backup.sh
$ sudo crontab -e
cron
15 2 * * * /usr/local/sbin/nextcloud-backup.sh >> /var/log/nextcloud-backup.log 2>&1

The find line at the end of the script keeps 14 daily generations. Adjust -mtime to your retention policy, and copy at least one generation per week to storage that the Nextcloud host cannot write to. A backup that a compromised host can delete is not a backup.

What breaks if you restore only the data directory?

Restoring only the data directory leaves the database holding file entries, share links and user accounts from a different point in time, so Nextcloud lists files that no longer exist on disk and ignores files that do exist.

The file index in the oc_filecache table is authoritative for the web interface and for the sync clients. occ files:scan can rebuild missing entries from disk, but it cannot recreate share links, comments, tags or app data that only existed in the database. Always restore both parts from the same backup run.

Restore Nextcloud from the backup

Restore into the same Nextcloud major version the backup came from. A dump from Nextcloud 28 imported under a Nextcloud 30 code base fails at the schema check, and the upgrade path only runs forward.

  1. Enable maintenance mode on the target host, or stop the web server if the instance does not start at all.
Console
$ sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --on
  1. Move the current installation aside instead of deleting it. If the restore fails, you still have the broken state to inspect.
Console
$ sudo mv /var/www/nextcloud /var/www/nextcloud-broken
  1. Copy the directories back from the backup:
Console
$ sudo rsync -Aax /mnt/backup/nextcloud/2026-08-16/nextcloud/ /var/www/nextcloud/
  1. Recreate the database and import the dump. Dropping the schema first avoids leftover tables from the newer state:
Console
$ sudo mysql -u root -p -e "DROP DATABASE nextcloud; CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
$ sudo mysql -u root -p -e "GRANT ALL ON nextcloud.* TO 'nextcloud'@'localhost';"
$ zcat /mnt/backup/nextcloud/2026-08-16/nextcloud-db.sql.gz | sudo mysql -u nextcloud -p nextcloud
  1. Fix ownership so PHP-FPM can write to the tree:
Console
$ sudo chown -R www-data:www-data /var/www/nextcloud
  1. Reset the data fingerprint and leave maintenance mode. The fingerprint tells desktop and mobile clients that the server state moved backwards, which makes them prompt for a resync instead of deleting local files:
Console
$ sudo -u www-data php /var/www/nextcloud/occ maintenance:data-fingerprint
$ sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --off

Verify the restore

Check the instance status first. A healthy installation reports installed: true and the expected version:

Console
$ sudo -u www-data php /var/www/nextcloud/occ status
  - installed: true
  - version: 30.0.2.2
  - versionstring: 30.0.2
  - maintenance: false

Then reconcile the file index with what is actually on disk. On large instances this takes several minutes and prints one line per user:

Console
$ sudo -u www-data php /var/www/nextcloud/occ files:scan --all

Finally, log in through the web interface, download a file that was created shortly before the backup, and confirm that an existing share link still resolves. A restore is only verified once file content has actually left the server.

Troubleshooting

"Your data directory is invalid" — the .ocdata marker file is missing from the data directory or the ownership is wrong. Recreate it and reset the owner:

Console
$ sudo touch /var/www/nextcloud/data/.ocdata
$ sudo chown -R www-data:www-data /var/www/nextcloud/data

Unknown collation: 'utf8mb4_0900_ai_ci' — the dump came from MySQL 8 and is being imported into MariaDB, which does not know that collation. Rewrite it during the import:

Console
$ zcat nextcloud-db.sql.gz | sed 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' | sudo mysql -u nextcloud -p nextcloud

Clients re-download the whole account after a restore — the data fingerprint was not reset, so the clients treat the older server state as a mass deletion. Run occ maintenance:data-fingerprint and let the clients pick up the change on their next sync cycle.

Next steps

The backup script is only half of the procedure. Schedule a restore test into a throwaway host at least quarterly and time it, because the number you need during an incident is how long a full restore takes, not how large the archive is. Keep one generation on storage outside the Nextcloud host, and record the Nextcloud version alongside each backup set so you always know which code base the dump belongs to.

Read next

Jetzt 200 € Guthaben sichern

Testen Sie Ihr Setup auf ccloud³

Registrieren Sie sich in der ccloud³ und erhalten Sie 200 € Startguthaben für Ihr Projekt – z. B. für eine PostgreSQL-VM mit automatischen Backups.

Ludwig Technische Redaktion

Schreibt bei centron über Linux-Administration, Container und Datenbanken – mit Fokus auf Anleitungen, die im Betrieb tatsächlich funktionieren.

Kategorie Linux Basics
Teilen
Noch offene Fragen?

Our team will help you with your specific setup - in German or English, by people who run the platform themselves.

War dieses Tutorial hilfreich?

Your answer is stored anonymously and helps us improve our tutorials.

Kommentare

No comments yet - be the first to ask a question about this tutorial.

Sign in to comment

Comments are open to centron customers. Sign in to your account to ask a question about this tutorial.

Weiterlesen

Das könnte Sie auch interessieren

Jetzt kostenlos anfangen

Melden Sie sich an und erhalten Sie in den ersten 60 Tagen ein Guthaben von 200 € bei centron.

Dieses Werbeangebot gilt nur für neue Konten. Angebot ausschließlich für Gewerbetreibende.

Jetzt loslegen Sales kontaktieren