From 9327e9d050590c6693ab811ee0f4ed5defd2e382 Mon Sep 17 00:00:00 2001 From: Julien Lutran Date: Tue, 25 Nov 2025 17:54:48 +0000 Subject: [PATCH] first commit --- README.md | 8 ++++++ incus-backup.db | 41 ++++++++++++++++++++++++++++ incus-backup.sh | 55 ++++++++++++++++++++++++++++++++++++++ incus-container-upgrade.sh | 15 +++++++++++ incus-copy.sh | 22 +++++++++++++++ incus-snapshot.sh | 13 +++++++++ 6 files changed, 154 insertions(+) create mode 100644 README.md create mode 100644 incus-backup.db create mode 100755 incus-backup.sh create mode 100755 incus-container-upgrade.sh create mode 100755 incus-copy.sh create mode 100755 incus-snapshot.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..53ec007 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Scripts + +Some helpful bash scripts for [incus](https://linuxcontainers.org/incus) + +- incus-container-upgrade: Perform `apt dist-upgrade` command on running instances. +- incus-backup: Backup instances FS and DBs to a remote location using rsync over ssh. +- incus-copy: Run a differential copy of all running instances to a remote incus server. +- incus-snapshot: Take a snapshot on all running instances. diff --git a/incus-backup.db b/incus-backup.db new file mode 100644 index 0000000..b61d439 --- /dev/null +++ b/incus-backup.db @@ -0,0 +1,41 @@ +{ + "bitwarden": { + "FS": ["/opt/bitwarden"] + }, + "databap": { + "DB": ["databap"] + }, + "freshrss": { + "DB": ["freshrss"] + }, + "gateway": { + "FS": ["/var/www"] + }, + "git": { + "DB": ["gitea"], + "FS": ["/home/git/projects"] + }, + "mail": { + "DB": ["mailserver"], + "FS": ["/var/vmail", "/var/www"] + }, + "nextcloud": { + "DB": ["nextcloud"], + "FS": ["/nextcloud"] + }, + "seafile": { + "DB": ["ccnet-db", "seafile-db", "seahub-db"], + "FS": ["/opt/seafile"] + }, + "solar": { + "DB": ["solar"], + "FS": ["/var/www/html/solar"] + }, + "spot": { + "DB": ["spot"], + "FS": ["/var/www"] + }, + "wedding": { + "FS": ["/var/www/mariage"] + } +} diff --git a/incus-backup.sh b/incus-backup.sh new file mode 100755 index 0000000..5e3fc43 --- /dev/null +++ b/incus-backup.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# +# Backup instances FS and DBs to a remote location using rsync over ssh. +# + +while getopts d:u:i:p:f:s: flag +do + case "${flag}" in + d) DEST=${OPTARG};; + u) USER=${OPTARG};; + i) SSH_KEY=${OPTARG};; + p) SSH_PORT=${OPTARG};; + f) DB_FILE=${OPTARG};; + s) STORAGE_POOL=${OPTARG};; + esac +done + +HOST=$(hostname -s) +BKP_DIR="/backup/${HOST}" +SSH_CMD="ssh -i ${SSH_KEY} -p ${SSH_PORT}" +CT_PREFIX="/var/lib/incus/storage-pools/${STORAGE_POOL}/containers" + + +# Backup incus DB +/usr/bin/incus admin sql local .dump | ${SSH_CMD} ${USER}@${DEST} "cat - > ${BKP_DIR}/incus-local-db.sql" +/usr/bin/incus admin sql global .dump | ${SSH_CMD} ${USER}@${DEST} "cat - > ${BKP_DIR}/incus-global-db.sql" + +for CT in $(cat ${DB_FILE} | jq -r 'keys[]') ; do + SRC_DIR="${CT_PREFIX}/${CT}" + DST_DIR="${BKP_DIR}/${CT}" + + # Backup container info + if [ -f "${SRC_DIR}/backup.yaml" ] ; then + echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting $CT backup.yaml" + /usr/bin/rsync -a --del -e "${SSH_CMD}" ${SRC_DIR}/backup.yaml ${USER}@${DEST}:${DST_DIR}/ + fi + + # Backup Mysql dumps + for DB in $(cat $DB_FILE | jq -r ".${CT} | select(.DB != null) | .DB[]") ; do + echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting $CT $DB database backup" + /usr/bin/incus exec -n $CT -- mariadb-dump --single-transaction --databases ${DB} | ${SSH_CMD} ${USER}@${DEST} "mkdir -p ${DST_DIR} ; cat - > ${DST_DIR}/mysql-${DB}.sql" + done + + # Backup container rootfs paths + for FS in $(cat $DB_FILE | jq -r ".${CT} | select(.FS != null) | .FS[]") ; do + # Skip missing rootfs dir + if [ ! -d ${SRC_DIR}/rootfs ] ; then + echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARNING : Missing rootfs for container ${CT}, skipping FS ${FS} ..." + continue + fi + # "/." used by rsync to limit the amount of path information that is sent as implied directories + echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting $CT $FS filesystem backup" + /usr/bin/rsync -aR --del -e "${SSH_CMD}" ${SRC_DIR}/rootfs/.${FS} ${USER}@${DEST}:${DST_DIR}/ + done +done diff --git a/incus-container-upgrade.sh b/incus-container-upgrade.sh new file mode 100755 index 0000000..366fe08 --- /dev/null +++ b/incus-container-upgrade.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# + + +distUpgrade() { + CT=$1 + DATE=$(date '+%Y-%m-%d %H:%M:%S') + echo -e "\n*** [$DATE] - Dist-upgrading $CT container ***\n" + incus exec $CT --env DEBIAN_FRONTEND=noninteractive -- apt -qq update + incus exec $CT --env DEBIAN_FRONTEND=noninteractive -- apt -qq -y dist-upgrade +} + +for CT in $(incus ls -c n -f compact,noheader status=RUNNING) ; do + distUpgrade "$CT" | tee -a /var/log/incus-container-upgrade.log +done diff --git a/incus-copy.sh b/incus-copy.sh new file mode 100755 index 0000000..ffcb187 --- /dev/null +++ b/incus-copy.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# https://linuxcontainers.org/incus/docs/main/howto/move_instances/ +# + +#set -x + +while getopts d:u:i:p:f:s: flag +do + case "${flag}" in + d) DEST=${OPTARG};; + m) MODE=${OPTARG};; + esac +done + + +for CT in $(/usr/bin/incus list -c n -f compact,noheader status=RUNNING) ; do + DATE=$(date '+%Y-%m-%d %H:%M:%S') + echo "[${DATE}] incus copy ${CT} ${DEST}:$CT --refresh --mode push" + /usr/bin/incus copy $CT ${DEST}:$CT --refresh --mode ${MODE} 2>&1 > /dev/null +done + diff --git a/incus-snapshot.sh b/incus-snapshot.sh new file mode 100755 index 0000000..a189fd5 --- /dev/null +++ b/incus-snapshot.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# +# https://linuxcontainers.org/incus/docs/main/howto/instances_backup/ +# + +#set -x + +for CT in $(/usr/bin/incus list -c n -f compact,noheader status=RUNNING) ; do + DATE=$(date '+%Y-%m-%d %H:%M:%S') + echo "[${DATE}] incus snapshot $CT" + /usr/bin/incus snapshot $CT 2>&1 > /dev/null +done +