aboutsummaryrefslogtreecommitdiff
path: root/tw/services/files/nextcloud-backup
blob: 4c53375890ba951ecb5576ced5f144dca4d01447 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/sh -e
# Nextcloud backup script, to run nightly.
# Documentation on backups:
# https://docs.nextcloud.com/server/latest/admin_manual/maintenance/backup.html
# https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html
# https://git.mdns.eu/nextcloud/passwords/-/wikis/Administrators/Backups

. /etc/default/nextcloud-backup
: "${DATABASE_PASSWORD:?You must pass the MySQL database password as DATABASE_PASSWORD}"

php_ini=$1
backup_dir=/var/backups/nextcloud/$(date -u '+%Y-%m-%d')
nextcloud_dir=/var/www/nextcloud
nextcloud_data_partition=/var/data  # mountpoint of the partition containing Nextcloud data dir
nextcloud_data_path=nextcloud       # relative to $nextcloud_data_partition
snapshot=$nextcloud_data_partition/tmp-nextcloud-backup

nc_maintenance () {
  # Enable (--on) or disable (--off) Nextcloud's maintenance mode.
  sudo -nu httpd php ${php_ini:+-c "$php_ini"} "$nextcloud_dir/occ" maintenance:mode "$@"
}

# If there is a previous backup, compare against it later (so we don't have to
# transfer every file).
last_backup_dir=$(ls -1d "$(dirname "$backup_dir")"/????-??-?? | LC_ALL=C sort | tail -1)
[ -d "$last_backup_dir" ] || unset last_backup_dir

# Don't overwrite existing backups. mkdir will fail if $backup_dir exists.
mkdir -m 750 "$backup_dir"

# Always turn off maintenance mode and clean up the temporary snapshot on exit,
# whether or not the backup succeeded.
cleanup () {
  nc_maintenance --off || :
  if [ -d "$snapshot" ]; then
    btrfs subvolume delete -c "$snapshot" || :
  fi
}
trap cleanup EXIT HUP INT TERM  # can't trap KILL

# Turn Nextcloud off temporarily so the data doesn't change during the backup.
nc_maintenance --on

# Backup the database. This can only be done offline.
mysqldump --single-transaction --default-character-set=utf8mb4 \
          -u nextcloud -p"$DATABASE_PASSWORD" nextcloud > "$backup_dir/nextcloud.sql"

# These shouldn't be copied while Nextcloud is online.
rsync -AUXHavx ${last_backup_dir+--link-dest="$last_backup_dir"} \
      "$nextcloud_dir/config" "$nextcloud_dir/themes" "$backup_dir"

# Make sure everything is synced to disk so it's in our snapshot.
btrfs filesystem sync "$nextcloud_data_partition/$nextcloud_data_path"
btrfs subvolume snapshot -r "$nextcloud_data_partition" "$snapshot"

# At this point, the data directory is in the snapshot, so Nextcloud can be
# turned on again.
nc_maintenance --off

# --link-dest is brittle (it only hardlinks to the old file if no metadata has
# changed). Reflinks would be better, but rsync doesn't seem to support them.
# We don't need files under preview/, as those are thumbnails from the Previews
# "app" and can be regenerated using `php -f occ preview:pre-generate`.
rsync -AUXHavx --exclude='appdata_*/preview' --exclude='appdata_*/passwords/*Cache' \
      ${last_backup_dir+--link-dest="$last_backup_dir/data"} \
      "$snapshot/$nextcloud_data_path/" "$backup_dir/data"
# Make sure everything is written out to the backup disk before we exit.
btrfs filesystem sync "$backup_dir"