How do you all go about backing up your data, on Linux?

I’m trying to find a good method of making periodic, incremental backups. I assume that the most minimal approach would be to have a Cronjob run rsync periodically, but I’m curious what other solutions may exist.

I’m interested in both command-line, and GUI solutions.

anarchyreloaded,

I use timeshift. It really is the best. For servers I go with restic.

lord_ryvan,

I use timeshift because it was pre-installed. But I can vouch for it; it works really well, and let’s you choose and tweak every single thing in a legible user interface!

HumanPerson,

When I do something really dumb I typically just use dd to create an iso. I should probably find something better.

PhilBro,

I run Openmediavault and I backup using BorgBackup. Super easy to setup, use, and modify

SethranKada,
@SethranKada@lemmy.ca avatar

I use Pika backup, which uses borg backup under the hood. It’s pretty good, with amazing documentation. Main issue I have with it is its really finicky and is kind of a pain to setup, even if it “just works” after that.

nikodunk,

Can you restore from it? That’s the part I’ve always struggled with?

SethranKada,
@SethranKada@lemmy.ca avatar

The way pika backup handles it, it loads the backup as a folder you can browse. I’ve used it a few times when hopping distros to copy and paste stuff from my home folder. Not very elegant, but it works and is very intuitive, even if I wish I could just hit a button and reset everything to the snapshot.

happyhippo,

Vorta + borgbase

The yearly subscription is cheap and fits my storage needs by quite some margin. Gives me peace of mind to have an off-site back up.

I also store my documents on Google Drive.

JoMiran,
@JoMiran@lemmy.ml avatar

At the core it has always been rsync and Cron. Sure I add a NAS and things like rclone+cryptomator to have extra copies of synchronized data (mostly documents and media files) spread around, but it’s always rsync+Cron at the core.

shaulliv,
@shaulliv@lemmy.world avatar

I use Rclone which has both an WEBUI and CLI.

HarriPotero, (edited )
@HarriPotero@lemmy.world avatar

I rotate between a few computers. Everything is synced between them with syncthing and they all have automatic btrfs snapshots. So I have several physical points to roll back from.

For a worst case scenario everything is also synced offsite weekly to a pCloud share. I have a little script that mounts it with pcloudfs, encfs and then rsyncs any updates.

akash_rawal,

I use rsync+btrfs snapshot solution.

  1. Use rsync to incrementally collect all data into a btrfs subvolume
  2. Deduplicate using duperemove
  3. Create a read-only snapshot of the subvolume

I don’t have a backup server, just an external drive that I only connect during backup.

Deduplication is mediocre, I am still looking for snapshot aware duperemove replacement.

JoMiran,
@JoMiran@lemmy.ml avatar

I’m not trying to start a flame war, but I’m genuinely curious. Why do people like btrfs over zfs? Btrfs seems very much so “not ready for prime time”.

Rockslide0482,

I’ve only ever run ZFS on a proxmox/server system but doesn’t it have a not insignificant amount of resources required to run it? BTRFS is not flawless, but it does have a pretty good feature set.

EddyBot,

btrfs is included in the linux kernel, zfs is not on most distros
the tiny chance that an externel kernel module borking with a kernel upgrade happens sometimes and is probably scary enough for a lot of people

JoMiran,
@JoMiran@lemmy.ml avatar

Fair enough

akash_rawal,

Features necessary for most btrfs use cases are all stable, plus btrfs is readily available in Linux kernel whereas for zfs you need additional kernel module. The availability advantage of btrfs is a big plus in case of a disaster. i.e. no additional work is required to recover your files.

(All the above only applies if your primary OS is Linux, if you use Solaris then zfs might be better.)

useless,

I use btrbk to send btrfs snapshots to a local NAS. Consistent backups with no downtime. The only annoyance (for me at least) is that both send and receive ends must use the same SELinux policy or labels won’t match.

kool_newt,

I made my own bash script that uses rsync. I stopped using Github so here’s a paste lol.

I define the backups like this, first item is source, other items on that line are it’s exclusions.

<pre style="background-color:#ffffff;">
<span style="color:#323232;">/home/shared
</span><span style="color:#323232;">/home/jamie     tmp/ dj_music/ Car_Music_USB
</span><span style="color:#323232;">/home/jamie_work
</span><span style="color:#323232;">
</span>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">#!/usr/bin/ssh-agent /bin/bash
</span><span style="color:#323232;">
</span><span style="color:#323232;"># chronicle.sh
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Get absolute directory chronicle.sh is in
</span><span style="color:#323232;">REAL_PATH=`(cd $(dirname "$0"); pwd)`
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Defaults
</span><span style="color:#323232;">BACKUP_DEF_FILE="${REAL_PATH}/backup.conf"
</span><span style="color:#323232;">CONF_FILE="${REAL_PATH}/chronicle.conf"
</span><span style="color:#323232;">FAIL_IF_PRE_FAILS='0'
</span><span style="color:#323232;">FIXPERMS='true'
</span><span style="color:#323232;">FORCE='false'
</span><span style="color:#323232;">LOG_DIR='/var/log/chronicle'
</span><span style="color:#323232;">LOG_PREFIX='chronicle'
</span><span style="color:#323232;">NAME='backup'
</span><span style="color:#323232;">PID_FILE='~/chronicle/chronicle.pid'
</span><span style="color:#323232;">RSYNC_OPTS="-qRrltH --perms --delete --delete-excluded"
</span><span style="color:#323232;">SSH_KEYFILE="${HOME}/.ssh/id_rsa"
</span><span style="color:#323232;">TIMESTAMP='date +%Y%m%d-%T'
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Set PID file for root user
</span><span style="color:#323232;">[ $EUID = 0 ] && PID_FILE='/var/run/chronicle.pid'
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Print an error message and exit
</span><span style="color:#323232;">ERROUT () {
</span><span style="color:#323232;">    TS="$(TS)"
</span><span style="color:#323232;">    echo "$TS $LOG_PREFIX (error): $1"
</span><span style="color:#323232;">    echo "$TS $LOG_PREFIX (error): Backup failed"
</span><span style="color:#323232;">    rm -f "$PID_FILE"
</span><span style="color:#323232;">    exit 1
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Usage output
</span><span style="color:#323232;">USAGE () {
</span><span style="color:#323232;">cat << EOF
</span><span style="color:#323232;">USAGE chronicle.sh [ OPTIONS ]
</span><span style="color:#323232;">
</span><span style="color:#323232;">OPTIONS
</span><span style="color:#323232;">    -f path   configuration file (default: chronicle.conf)
</span><span style="color:#323232;">    -F        force overwrite incomplete backup
</span><span style="color:#323232;">    -h        display this help
</span><span style="color:#323232;">EOF
</span><span style="color:#323232;">exit 0
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Timestamp
</span><span style="color:#323232;">TS ()
</span><span style="color:#323232;">{
</span><span style="color:#323232;">    if
</span><span style="color:#323232;">        echo $TIMESTAMP | grep tai64n &>/dev/null
</span><span style="color:#323232;">    then
</span><span style="color:#323232;">        echo "" | eval $TIMESTAMP
</span><span style="color:#323232;">    else
</span><span style="color:#323232;">        eval $TIMESTAMP
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Logger function
</span><span style="color:#323232;"># First positional parameter is message severity (notice|warn|error)
</span><span style="color:#323232;"># The log message can be the second positional parameter, stdin, or a HERE string
</span><span style="color:#323232;">LOG () {
</span><span style="color:#323232;">    local TS="$(TS)"
</span><span style="color:#323232;">    # local input=""
</span><span style="color:#323232;">
</span><span style="color:#323232;">    msg_type="$1"
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # if [[ -p /dev/stdin ]]; then
</span><span style="color:#323232;">    #     msg="$(cat -)"
</span><span style="color:#323232;">    # else
</span><span style="color:#323232;">        shift
</span><span style="color:#323232;">        msg="${@}"
</span><span style="color:#323232;">    # fi
</span><span style="color:#323232;">    echo "$TS chronicle ("$msg_type"): $msg"
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Logger function
</span><span style="color:#323232;"># First positional parameter is message severity (notice|warn|error)
</span><span style="color:#323232;"># The log message canbe stdin or a HERE string
</span><span style="color:#323232;">LOGPIPE () {
</span><span style="color:#323232;">    local TS="$(TS)"
</span><span style="color:#323232;">    msg_type="$1"
</span><span style="color:#323232;">    msg="$(cat -)"
</span><span style="color:#323232;">    echo "$TS chronicle ("$msg_type"): $msg"
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Process Options
</span><span style="color:#323232;">while
</span><span style="color:#323232;">    getopts ":d:f:Fmh" options; do
</span><span style="color:#323232;">        case $options in
</span><span style="color:#323232;">            d ) BACKUP_DEF_FILE="$OPTARG" ;;
</span><span style="color:#323232;">            f ) CONF_FILE="$OPTARG" ;;
</span><span style="color:#323232;">            F ) FORCE='true' ;;
</span><span style="color:#323232;">            m ) FIXPERMS='false' ;;
</span><span style="color:#323232;">            h ) USAGE; exit 0 ;;
</span><span style="color:#323232;">            * ) USAGE; exit 1 ;;
</span><span style="color:#323232;">    esac
</span><span style="color:#323232;">done
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Ensure a configuration file is found
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    [ "x${CONF_FILE}" = 'x' ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    ERROUT "Cannot find configuration file $CONF_FILE"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Read the config file
</span><span style="color:#323232;">. "$CONF_FILE"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Set the owner and mode for backup files
</span><span style="color:#323232;">if [ $FIXPERMS = 'true' ]; then
</span><span style="color:#323232;">#FIXVAR="--chown=${SSH_USER}:${SSH_USER} --chmod=D770,F660"
</span><span style="color:#323232;">FIXVAR="--usermap=*:${SSH_USER} --groupmap=*:${SSH_USER} --chmod=D770,F660"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Set up logging
</span><span style="color:#323232;">
</span><span style="color:#323232;">if [ "${LOG_DIR}x" = 'x' ]; then
</span><span style="color:#323232;">    ERROUT "(error): ${LOG_DIR} not specified"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">mkdir -p "$LOG_DIR"
</span><span style="color:#323232;">LOGFILE="${LOG_DIR}/chronicle.log"
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Make all output go to the log file
</span><span style="color:#323232;">exec >> $LOGFILE 2>&1
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Ensure a backup definitions file is found
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    [ "x${BACKUP_DEF_FILE}" = 'x' ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    ERROUT "Cannot find backup definitions file $BACKUP_DEF_FILE"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Check for essential variables
</span><span style="color:#323232;">VARS='BACKUP_SERVER SSH_USER BACKUP_DIR BACKUP_QTY NAME TIMESTAMP'
</span><span style="color:#323232;">for var in $VARS; do
</span><span style="color:#323232;">    if [ ${var}x = x ]; then
</span><span style="color:#323232;">        ERROUT "${var} not specified"
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">done
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">LOG notice "Backup started, keeping $BACKUP_QTY snapshots with name "$NAME""
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Export variables for use with external scripts
</span><span style="color:#323232;">export SSH_USER RSYNC_USER BACKUP_SERVER BACKUP_DIR LOG_DIR NAME REAL_PATH
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Check for PID
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    [ -e "$PID_FILE" ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    LOG error "$PID_FILE exists"
</span><span style="color:#323232;">    LOG error 'Backup failed'
</span><span style="color:#323232;">    exit 1
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Write PID
</span><span style="color:#323232;">touch "$PID_FILE"
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Add key to SSH agent
</span><span style="color:#323232;">ssh-add "$SSH_KEYFILE" 2>&1 | LOGPIPE notice -
</span><span style="color:#323232;">
</span><span style="color:#323232;"># enhance script readability
</span><span style="color:#323232;">CONN="${SSH_USER}@${BACKUP_SERVER}"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Make sure the SSH server is available
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    ! ssh $CONN echo -n ''
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    ERROUT "$BACKUP_SERVER is unreachable"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Fail if ${NAME}.0.tmp is found on the backup server.
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    ssh ${CONN} [ -e "${BACKUP_DIR}/${NAME}.0.tmp" ] && [ "$FORCE" = 'false' ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    ERROUT "${NAME}.0.tmp exists, ensure backup data is in order on the server"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Try to create the destination directory if it does not already exist
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    ssh $CONN [ ! -d $BACKUP_DIR ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    if
</span><span style="color:#323232;">        ssh $CONN mkdir -p "$BACKUP_DIR"
</span><span style="color:#323232;">        ssh $CONN chown ${SSH_USER}:${SSH_USER} "$BACKUP_DIR"
</span><span style="color:#323232;">    then :
</span><span style="color:#323232;">    else
</span><span style="color:#323232;">        ERROUT "Cannot create $BACKUP_DIR"
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Create metadata directory
</span><span style="color:#323232;">ssh $CONN mkdir -p "$BACKUP_DIR/chronicle_metadata"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
</span><span style="color:#323232;"># PRE_COMMAND
</span><span style="color:#323232;">
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    [ -n "$PRE_COMMAND" ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    LOG notice "Running ${PRE_COMMAND}"
</span><span style="color:#323232;">    if
</span><span style="color:#323232;">        $PRE_COMMAND
</span><span style="color:#323232;">    then
</span><span style="color:#323232;">        LOG notice "${PRE_COMMAND} complete"
</span><span style="color:#323232;">    else
</span><span style="color:#323232;">        LOG error "Execution of ${PRE_COMMAND} was not successful"
</span><span style="color:#323232;">        if [ "$FAIL_IF_PRE_FAILS" -eq 1 ]; then
</span><span style="color:#323232;">            ERROUT 'Command specified by PRE_COMMAND failed and FAIL_IF_PRE_FAILS enabled'
</span><span style="color:#323232;">        fi
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
</span><span style="color:#323232;"># Backup
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Make a hard link copy of backup.0 to rsync with
</span><span style="color:#323232;">if [ $FORCE = 'false' ]; then
</span><span style="color:#323232;">    ssh $CONN "[ -d ${BACKUP_DIR}/${NAME}.0 ] && cp -al ${BACKUP_DIR}/${NAME}.0 ${BACKUP_DIR}/${NAME}.0.tmp"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">while read -u 9 l; do
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # Skip commented lines
</span><span style="color:#323232;">    if [[ "$l" =~ ^#.* ]]; then
</span><span style="color:#323232;">    continue
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">    if [[ $l = '/*'* ]]; then
</span><span style="color:#323232;">        LOG warn "$SOURCE is not an absolute path"
</span><span style="color:#323232;">        continue
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # Reduce whitespace to one tab
</span><span style="color:#323232;">    line=$(echo $l | tr -s [:space:] 't')
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # get the source
</span><span style="color:#323232;">    SOURCE=$(echo "$line" | cut -f1)
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # get the exclusions
</span><span style="color:#323232;">    EXCLUSIONS=$(echo "$line" | cut -f2-)
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # Format exclusions for the rsync command
</span><span style="color:#323232;">    unset exclude_line
</span><span style="color:#323232;">    if [ ! "$EXCLUSIONS" = '' ]; then
</span><span style="color:#323232;">        for each in $EXCLUSIONS; do
</span><span style="color:#323232;">            exclude_line="$exclude_line--exclude $each "
</span><span style="color:#323232;">        done
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">    LOG notice "Using SSH transport for $SOURCE"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # get directory metadata
</span><span style="color:#323232;">    PERMS="$(getfacl -pR "$SOURCE")"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # Copy metadata
</span><span style="color:#323232;">    ssh $CONN mkdir -p ${BACKUP_DIR}/chronicle_metadata/${SOURCE}
</span><span style="color:#323232;">    echo "$PERMS" | ssh $CONN -T "cat > ${BACKUP_DIR}/chronicle_metadata/${SOURCE}/metadata"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">    LOG debug "rsync $RSYNC_OPTS $exclude_line "$FIXVAR" "$SOURCE" 
</span><span style="color:#323232;">    "${SSH_USER}"@"$BACKUP_SERVER":"${BACKUP_DIR}/${NAME}.0.tmp""
</span><span style="color:#323232;">
</span><span style="color:#323232;">    rsync $RSYNC_OPTS $exclude_line $FIXVAR "$SOURCE" 
</span><span style="color:#323232;">    "${SSH_USER}"@"$BACKUP_SERVER":"${BACKUP_DIR}/${NAME}.0.tmp"
</span><span style="color:#323232;">
</span><span style="color:#323232;">done 9< "${BACKUP_DEF_FILE}"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
</span><span style="color:#323232;"># Try to see if the backup succeeded
</span><span style="color:#323232;">
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    ssh $CONN [ ! -d "${BACKUP_DIR}/${NAME}.0.tmp" ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    ERROUT "${BACKUP_DIR}/${NAME}.0.tmp not found, no new backup created"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Test for empty temp directory
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    ssh $CONN [ ! -z "$(ls -A ${BACKUP_DIR}/${NAME}.0.tmp 2>/dev/null)" ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    ERROUT "No new backup created"
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
</span><span style="color:#323232;"># Rotate
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Number of oldest backup
</span><span style="color:#323232;">X=`expr $BACKUP_QTY - 1`
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;">LOG notice 'Rotating previous backups'
</span><span style="color:#323232;">
</span><span style="color:#323232;"># keep oldest directory temporarily in case rotation fails
</span><span style="color:#323232;">ssh $CONN [ -d "${BACKUP_DIR}/${NAME}.${X}" ] && 
</span><span style="color:#323232;">ssh $CONN mv "${BACKUP_DIR}/${NAME}.${X}" "${BACKUP_DIR}/${NAME}.${X}.tmp"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Rotate previous backups
</span><span style="color:#323232;">until [ $X -eq -1 ]; do
</span><span style="color:#323232;">    Y=$X
</span><span style="color:#323232;">    X=`expr $X - 1`
</span><span style="color:#323232;">
</span><span style="color:#323232;">    ssh $CONN [ -d "${BACKUP_DIR}/${NAME}.${X}" ] && 
</span><span style="color:#323232;">    ssh $CONN mv "${BACKUP_DIR}/${NAME}.${X}" "${BACKUP_DIR}/${NAME}.${Y}"
</span><span style="color:#323232;">    [ $X -eq 0 ] && break
</span><span style="color:#323232;">done
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Create "backup.0" directory
</span><span style="color:#323232;">ssh $CONN mkdir -p "${BACKUP_DIR}/${NAME}.0"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Get individual items in "backup.0.tmp" directory into "backup.0"
</span><span style="color:#323232;"># so that items removed from backup definitions rotate out
</span><span style="color:#323232;">while read -u 9 l; do
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # Skip commented lines
</span><span style="color:#323232;">    if [[ "$l" =~ ^#.* ]]; then
</span><span style="color:#323232;">    continue
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # Skip invalid sources that are not an absolute path"
</span><span style="color:#323232;">    if [[ $l = '/*'* ]]; then
</span><span style="color:#323232;">        continue
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">
</span><span style="color:#323232;">    # Reduce multiple tabs to one
</span><span style="color:#323232;">    line=$(echo $l | tr -s [:space:] 't')
</span><span style="color:#323232;">
</span><span style="color:#323232;">    source=$(echo "$line" | cut -f1)
</span><span style="color:#323232;">
</span><span style="color:#323232;">    source_basedir="$(dirname $source)"
</span><span style="color:#323232;">
</span><span style="color:#323232;">    ssh $CONN mkdir -p "${BACKUP_DIR}/${NAME}.0/${source_basedir}"
</span><span style="color:#323232;">
</span><span style="color:#323232;">    LOG debug "ssh $CONN cp -al "${BACKUP_DIR}/${NAME}.0.tmp${source}" "${BACKUP_DIR}/${NAME}.0${source_basedir}""
</span><span style="color:#323232;">
</span><span style="color:#323232;">    ssh $CONN cp -al "${BACKUP_DIR}/${NAME}.0.tmp${source}" "${BACKUP_DIR}/${NAME}.0${source_basedir}"
</span><span style="color:#323232;">
</span><span style="color:#323232;">done 9< "${BACKUP_DEF_FILE}"
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Remove oldest backup
</span><span style="color:#323232;">X=`expr $BACKUP_QTY - 1` # Number of oldest backup
</span><span style="color:#323232;">ssh $CONN rm -Rf "${BACKUP_DIR}/${NAME}.${X}.tmp"
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Set time stamp on backup directory
</span><span style="color:#323232;">ssh $CONN touch -m "${BACKUP_DIR}/${NAME}.0"
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Delete temp copy of backup
</span><span style="color:#323232;">ssh $CONN rm -Rf "${BACKUP_DIR}/${NAME}.0.tmp"
</span><span style="color:#323232;">
</span><span style="color:#323232;">#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
</span><span style="color:#323232;"># Post Command
</span><span style="color:#323232;">
</span><span style="color:#323232;">if
</span><span style="color:#323232;">    [ ! "${POST_COMMAND}x" = 'x' ]
</span><span style="color:#323232;">then
</span><span style="color:#323232;">    LOG notice "Running ${POST_COMMAND}"
</span><span style="color:#323232;">    if
</span><span style="color:#323232;">        $POST_COMMAND
</span><span style="color:#323232;">    then
</span><span style="color:#323232;">        LOG notice "${POST_COMMAND} complete"
</span><span style="color:#323232;">    else
</span><span style="color:#323232;">        LOG warning "${POST_COMMAND} complete with errors"
</span><span style="color:#323232;">    fi
</span><span style="color:#323232;">fi
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Delete PID file
</span><span style="color:#323232;">rm -f "$PID_FILE"
</span><span style="color:#323232;">
</span><span style="color:#323232;"># Log success message
</span><span style="color:#323232;">LOG notice 'Backup completed successfully'
</span><span style="color:#323232;">
</span>
BCsven,

DejaDup on one computer. Another is using Syncthing, another I do a manual Grsync. i really should have a better plan. lol

avidamoeba,
@avidamoeba@lemmy.ca avatar

Setup

Machine A:

  • RAIDz1 takes care of single-disk failure
  • ZFS doing regular snapshots
  • Syncthing replicates the data off-site to Machine B

Machine B:

  • RAIDz1 takes care of single-disk failure
  • ZFS doing regular snapshots
  • Syncthing receiving data from Machine A

Implications

  • Any single-disk hardware failure on machine A or B results in no data loss
  • Physical destruction of A won’t affect B and the other way around
  • Any accidentally deleted or changed file can be recovered from a previous snapshot
  • Any ZFS corruption at A doesn’t affect B because send/recv isn’t used. The two filesystems are completely independent
  • Any malicious data destruction on A can be recovered from B even if it’s replicated via snapshot at B. The reverse is also true. A malicious actor would have to have root access on both A and B in order to destroy the data and the snapshots on both machines to prevent recovery
  • Any data destruction caused by Syncthing can be recovered from snapshot at A or B
fmstrat,

zfs snap and zfs send to an external or another server.

rodbiren,

Use synching on several devices to replicate data I want to keep backups of. Family photos, journals, important docs, etc. Works perfect and I run a relay node to give back to the community given I am on a unlimited data connection.

stewsters,

I use syncthing for my documents as well. My source code is in GitHub if it’s important, and I can reinstall everything else if I need.

  • All
  • Subscribed
  • Moderated
  • Favorites
  • linux@lemmy.ml
  • DreamBathrooms
  • magazineikmin
  • everett
  • InstantRegret
  • rosin
  • Youngstown
  • slotface
  • love
  • khanakhh
  • kavyap
  • tacticalgear
  • GTA5RPClips
  • thenastyranch
  • modclub
  • anitta
  • mdbf
  • tester
  • Durango
  • ethstaker
  • osvaldo12
  • cubers
  • ngwrru68w68
  • provamag3
  • normalnudes
  • Leos
  • cisconetworking
  • megavids
  • JUstTest
  • All magazines