From f9e37ae21177889c9a671155e1ff2c56d3dabadc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 18:54:43 +0000 Subject: [PATCH 1/6] Initial plan From 6c283544f6dd6e956a419c2daf7a223ef496f164 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:01:44 +0000 Subject: [PATCH 2/6] Fix shell script quality issues and remove .DS_Store files Co-authored-by: payrequestio <9915051+payrequestio@users.noreply.github.com> --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 2 ++ LICENSE | 2 +- etc/.DS_Store | Bin 6148 -> 0 bytes etc/restic/.DS_Store | Bin 6148 -> 0 bytes etc/systemd/.DS_Store | Bin 6148 -> 0 bytes etc/systemd/system/.DS_Store | Bin 6148 -> 0 bytes usr/.DS_Store | Bin 6148 -> 0 bytes usr/local/.DS_Store | Bin 6148 -> 0 bytes usr/local/sbin/directadmin-vps-backup.sh | 10 ++++----- usr/local/sbin/mysql.sh | 11 +++++----- usr/local/sbin/restic_backup.sh | 25 +++++++++++++++-------- usr/local/sbin/restic_check.sh | 2 +- 13 files changed, 29 insertions(+), 23 deletions(-) delete mode 100644 .DS_Store delete mode 100644 etc/.DS_Store delete mode 100644 etc/restic/.DS_Store delete mode 100644 etc/systemd/.DS_Store delete mode 100644 etc/systemd/system/.DS_Store delete mode 100644 usr/.DS_Store delete mode 100644 usr/local/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index abd76e3a1e5021c6c18ef28175afadff1381cd32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!EVz)5S?uTT?Zj@Kx&V^AaRILBC1pfRWfOEhy)eF2o8W+yEfFqwX4`6f)L~j zA3^`1pVBYjL-+uAyE`D_mP0QHl^tpJ&CbrOwQse%9wHKhY1AWX6On`}Y_`yxBWhgf z8ChYE0~BhGd_PR{5Ir@W70ZBS;9qAzo!th-RMP%9T{^$_F>?F67QG0L3TMR;pYqf2 zC{2p8+x;q58qF(LSEX#p*7epa{~(|FWl&CwK`?otdXIzhAY}V#o~OU;Abgg@v)%U1 z?K~@kB#S3HIf-I~ynLQyQ9c{w(=00VJiZx_(vi+?dwo9dcl#Z;w{dULap#+x-Hy9) zx4&38a_v@cYrJ>({=@v^;?ozi3j&vvmK%&e;TQ%l@0~x%(mZb8qp5< zG^$Z_f{5QKlJtlIdJ8&VQ$i`l%kg5Z7Vpp#%>00c$Q#ieI;5zU@wt}KpeYqJsYQJ| zKkD*sl_sIKRp7wNg&5%DAg^60%LZEpECc@o1L}Top$a|5%AmeF(C88X*o0dPZ2j{O zbd>|>F;)iQfe4cdG^v7148gU7FzFcgIKDDy(n;tqV;=sp;0i@>=^9*DTKl*e1zno-ymI2GarD8xdhyHMYCGp#OW^wARb)kDuB`U5ms8i7JbF2;Z e6t6?IV9Zqp&||C&!UM5C0-6TfSO)$o13v(@bIX1J diff --git a/.gitignore b/.gitignore index 4836d90a..51160170 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ b2_env.sh b2_pw.txt +.DS_Store +**/.DS_Store diff --git a/LICENSE b/LICENSE index 07e15f3e..1445d668 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ restic-systemd-automatic-backup - My restic backup solution using Backblaze B2 storage, systemd timers (or cron) and email notifications on failure. -Copyright (c) 2018, see commit log for auhtors +Copyright (c) 2018, see commit log for authors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/etc/.DS_Store b/etc/.DS_Store deleted file mode 100644 index ab6b6af5009da1280a50750e11695bf44a4b1953..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!EO^V5FNK6b%GE%AhkzdDsjjm5mhP$h zzX5;1r|<=Q4$O=P)J@_4kqz;* z5f#so!NpN-FXG(BE79KJ8So6O8v}H9+c1F?N~oZDeyJ#YMScE*g_quljc4Vxob_+< zb)J>gVDMdSY_=Y?x3uo+?vw8O@Y2r0DypXCIGVhr-kYepjKw~-Hver$@ky4>&U(*Y z+oFoHBAsw@mZTW+?ldbBI~&_+k(4}7=mxaz=+0ShXFeYdMtw8fd%5VF`TqW(Z}wh{ z7K@JF-W|R=I={O8IRCV``^?(_g$0Y{so*d8f<|MLH{qnnZSev9l(U6I-7;AzTtRZL z+#y|R8zb)1wG{nO30vCc5@pDVmo;QSj(~o=hHLXn@(g$e{x1Vm9}FsCAXq!((t!;> z0T5g0W>rGt{khmtQVd7&u&I?iu6 z98}=Yx1IsdK$C$@*X_~$fB5(Je>2JVJOiGA2gQJD9fSvC+>+d_E1RRc)}WrFl2Baj mkW;Xc>zEC?ig!?1aLkZ_7zox5(SzcD1egZjcn1C{13v-7Sg=w6 diff --git a/etc/restic/.DS_Store b/etc/restic/.DS_Store deleted file mode 100644 index a1ad317bdf86f5b07453ba4ef7f409f31d905570..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%Sr=55Ukc90YP)q;~YH+{=pLB!LuKrZW0i(8bKo-_u|L+m0JB!vg_Ui5$T5M z>gnm8N5Rf)1CZ%vaRy8PjM)@LjR8^j;LuJ4?-Ir4*x&(ISmFiqfrVNCuLDA2Yx=TV=fOm^v9q29kj<2K0U?Y>HK2>u6U8m6ZU*5#1)V zwU*GF5?BScjvS$grxHC?qQnqSr@us871%m@IwXn@iIqQ#7qQhjf3a{#<(N7dNCvtL zXzfdx==*=pPiC;lw~$oHKr--O8IaNJayI4Z;%)tQPJL?&+clet#uX+`Wo&|@xwhaOriEh55F_0XHJq6d|_Yl~gjZlt@l)LPl!&_D7E z{2ZN0(n6^o6_pv7yxGZ2HhEc+9mW`M4B8dOii|MVjmfCkx2+7#-GY zhU&%-mA?>8j^D@t?%gtr*$6aY^ZSP%#XjiHyB0nDxmbS~-`Tx>_3y$UPRivsp2_Bx zmRE!*h{9Uo%I&J5n|R3}Zg~9*d^+)xu5ayArNWu*`Da0A*eY%uswnY-sMFWULAwJX zm*+v$R>OuGMD19Q|VZjHyaa;+pQ)xAkcj_dVuNmh4jlSy7|ZdVSP*S*oh z_;K>|te3$!e6U*9El%MDjJZYLyZtCs(JlC=In!7|Vt^PR2L31m_Ba!(e{}vdZDN2J z_&x^ke2}1sj>XiVK02V$B>-R!+*-iK-$u-l79ESJLFfVDCKb@6a$90>lMa4q;~a~r zL6gq7Ek3w?Gq)8A*KddMr4DD@F-R>jKn%Fy~2p*8JY0u2Cls$gxG#TO#uqD!(Bdv=IHePo!RgC2$`mS%g0 zGvEyTHU{MEp7F#Mctno*^E*NlGd$pl*yX9t(8rYRCTsUPw~q^s=CqvAK0|&p7-!wV zxJ#eBP{K3u{9Q$tefo_!6Gj+HtV)cxN{r);VyyjsyjFZ4ZRmT|H}6;co?yhaHOa1E z&O1~%%^6L&9lwhVr zGYv7t2xiXyB+g4CmWF1I5R;D(BZruw1dUGriB3mI4ZU;*oPk9KwyoKa_y2lz{=aDC zEoZsTKl%$;*#Rqy0kcXYZK}nRYc~MhHAnhKF3;-r}%)X!m&gu#57`Q$R3LS N2q+C+I0HY*zz56Jkrn^| diff --git a/usr/.DS_Store b/usr/.DS_Store deleted file mode 100644 index a18a5612607c0a59d6a673b7a6de0c4268f82be9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!Ab)$5KU^;Y(?xru*X~k4?VOfEh55F>%p6_q6d|_Yl|+do6>Gwv{v>v^pE@k zKSyVhv{b4`MP&viZ+0@1-MoZkhcU(*gLaj%B4bQ|BIX=uJ`wCkospdN%mH#eMsyIV z%OFz0Oe{HmCj+>4^DJVDT{53duODuS9ng(8E&B7PB7?NwwJZG+bfd7JR4T7LHl^;wOWC!|$Eq)1jYS2i87PDw^1Sa2$5hR&jMt#fcxrot{n(+Z_lw zI|<{qN*ihrxBGe=&vXdk2&Yvn4TrT#tt_is+oQ4^*6WqB+}f;-MvhopukIdP+}zy{ zA4ZQ)dJ>GoX4SG{aSYF3Oil6D>&20ZufRLb7)KHk1H=F^@TVEDhniUW)3czDCkBXt z?_dDW2MLPkTFeaUqXQaU0s!X0tp#lSal{;H(Y2TvgdPxXQUOgWwEM?(&b62s zH0g}n;)B~ab6cTs{dO2%>Tt$ggVYiO#K1=eu=WEf;{Ct>b^Wh{s38W3f&a+>&o{kh z1LkJ$)|px2T`NH^Kv6I*GdM{BLtn)Zi&t?8R15ee8i20F%piC`=tn@)Kn*eQqYS(Q D)AC$| diff --git a/usr/local/.DS_Store b/usr/local/.DS_Store deleted file mode 100644 index 17acfc65650e3220d498d3ab2199213b2eb9516f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!AiqG5Z!I7ZYyFBf<5LUc<7+S`WPm5k081i7hq|v!zKb8Y}q?{Ug7? z&(WFPZ9%Iy5h*h;`zEt98|H1;%`nDzbKI*kR%DC`P{d3ghHnJtQ5Pg*JXt`FYqSG- z?FG_2z|j+*Z10-sLYc;FfPRLWp(yWW}KpLB{_M>33EKkScGvft}N$mO{o z_T;1~$6+s0>o|Hsh`h*milro}SL$V}THBwNt)$VYl&#uceLBsH?VajjyL)^8kUUPG zp7maE_)wK>8l1rk81n+}oKYCa@CJOd_*onwF+dCu153bwIoibf64XK^5Cg=(?=pbr zg9VCc8%#B-qXP!j0s!W~tpsfK>knw51JE{@Y6K4m*QtOym75lWo6dpjbeOkIe5z5W zGp^nY>zFrl)1h$FcyLRFGj407kr*HbJ~M#5A4n1J|Kp$ge-%UnF+dC~Cj&g!a#~HO z&D^aERpMQ1fp$SrFfP?NPXR++#Sn{EaS2oj_$@R5ZG)*s@PN>dfTDp0V&G31_y9s7 BTL=IE diff --git a/usr/local/sbin/directadmin-vps-backup.sh b/usr/local/sbin/directadmin-vps-backup.sh index 6d88f348..5b67b4ca 100644 --- a/usr/local/sbin/directadmin-vps-backup.sh +++ b/usr/local/sbin/directadmin-vps-backup.sh @@ -5,11 +5,10 @@ bash /usr/local/sbin/directadmin-cleaner.sh & wait $! -## add your discord channel webhook -discord="URL" +## add your discord channel webhook (or set DISCORD_WEBHOOK in env.sh) +discord="${DISCORD_WEBHOOK:-URL}" # Check available disk space -total_space=$(df -H | awk '{if($NF=="/") print $2}' | tr -d 'G') free_space=$(df -H | awk '{if($NF=="/") print $4}' | tr -d 'G') required_space=$(du -sh /var/lib/mysql | tr -d 'G' | awk '{print $1}') if (( $(echo "$free_space < $required_space" | bc -l) )); then @@ -106,9 +105,8 @@ wait $! #wait $! RESTICSNAPSHOTS="restic snapshots --no-lock --json --repo ${RESTIC_REPOSITORY}" -RESTICOUTPUT=$(eval "$RESTICSNAPSHOTS" | grep -oP '"short_id":"\K[0-9a-f]+|"time":"\K[^"]+' | paste -d' ' - - | sed 's/T/ /; s/\.\(.*\)Z/\1/' ) -COUNT=$(restic snapshots --compact --repo ${RESTIC_REPOSITORY} | awk -F '\t' '{print $1}' | wc -l) -HOSTNAME=`hostname` +COUNT=$(restic snapshots --compact --repo "${RESTIC_REPOSITORY}" | awk -F '\t' '{print $1}' | wc -l) +HOSTNAME=$(hostname) diff --git a/usr/local/sbin/mysql.sh b/usr/local/sbin/mysql.sh index 222c03e4..63a36815 100644 --- a/usr/local/sbin/mysql.sh +++ b/usr/local/sbin/mysql.sh @@ -1,10 +1,10 @@ #!/bin/bash USER="da_admin" -PASSWORD="`grep passwd= /usr/local/directadmin/conf/mysql.conf | cut -d\= -f2`" +PASSWORD="$(grep passwd= /usr/local/directadmin/conf/mysql.conf | cut -d= -f2)" BACKUP_RETAIN_DAYS=0 ## Number of days to keep local backup copy DB_BACKUP_PATH='/home/mysqlbackups' -TODAY=`date +"%Y%m%d"` +TODAY=$(date +"%Y%m%d") #OUTPUT="/Users/rabino/DBs" @@ -16,13 +16,12 @@ fi find ${DB_BACKUP_PATH} -name '*.zst' -type f -mtime +${BACKUP_RETAIN_DAYS} -exec rm -f {} \; -databases=`mysql -u $USER -p$PASSWORD -e "SHOW DATABASES;" | tr -d "| " | grep -v Database` +databases=$(mysql -u "$USER" -p"$PASSWORD" -e "SHOW DATABASES;" | tr -d "| " | grep -v Database) for db in $databases; do if [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]] ; then echo "Dumping database: $db" - #mysqldump -u $USER -p$PASSWORD --databases $db | gzip > ${DB_BACKUP_PATH}/${TODAY}.$db.sql.gz - mysqldump -u $USER -p$PASSWORD --databases $db | zstd > ${DB_BACKUP_PATH}/${TODAY}.$db.sql.zst - # gzip $OUTPUT/`date +%Y%m%d`.$db.sql + #mysqldump -u "$USER" -p"$PASSWORD" --databases "$db" | gzip > "${DB_BACKUP_PATH}/${TODAY}.${db}.sql.gz" + mysqldump -u "$USER" -p"$PASSWORD" --databases "$db" | zstd > "${DB_BACKUP_PATH}/${TODAY}.${db}.sql.zst" fi done diff --git a/usr/local/sbin/restic_backup.sh b/usr/local/sbin/restic_backup.sh index baa0d84d..986a9436 100644 --- a/usr/local/sbin/restic_backup.sh +++ b/usr/local/sbin/restic_backup.sh @@ -1,3 +1,6 @@ +#!/usr/bin/env bash +# Backup script using restic +# This script is typically run by: /etc/systemd/system/directadmin-vps-backup.{service,timer} # Set all environment variables like # B2_ACCOUNT_ID, B2_ACCOUNT_KEY, RESTIC_REPOSITORY etc. @@ -23,7 +26,7 @@ wait $! restic backup \ --verbose \ --one-file-system \ - --tag $BACKUP_TAG \ + --tag "$BACKUP_TAG" \ $BACKUP_EXCLUDES \ $BACKUP_PATHS & wait $! @@ -33,13 +36,13 @@ wait $! # --group-by only the tag and path, and not by hostname. This is because I create a B2 Bucket per host, and if this hostname accidentially change some time, there would now be multiple backup sets. restic forget \ --verbose \ - --tag $BACKUP_TAG \ + --tag "$BACKUP_TAG" \ --prune \ --group-by "paths,tags" \ - --keep-daily $RETENTION_DAYS \ - --keep-weekly $RETENTION_WEEKS \ - --keep-monthly $RETENTION_MONTHS \ - --keep-yearly $RETENTION_YEARS & + --keep-daily "$RETENTION_DAYS" \ + --keep-weekly "$RETENTION_WEEKS" \ + --keep-monthly "$RETENTION_MONTHS" \ + --keep-yearly "$RETENTION_YEARS" & wait $! # Check repository for errors. @@ -47,6 +50,10 @@ wait $! #restic check & #wait $! -RESTICOUTPUT=`restic snapshots --repo ${RESTIC_REPOSITORY}` -HOSTNAME=`hostname` -echo "Backup ${HOSTNAME} has finished, we keep ${RETENTION_DAYS} each day. \n ${RESTICOUTPUT}" | mail -s "Backup done ${HOSTNAME}" geertjan@hostingwalk.com +RESTICOUTPUT=$(restic snapshots --repo "${RESTIC_REPOSITORY}") +HOSTNAME=$(hostname) +# Configure email recipient in env.sh by setting BACKUP_EMAIL variable +# Example: export BACKUP_EMAIL="admin@example.com" +if [ -n "${BACKUP_EMAIL:-}" ]; then + printf "Backup %s has finished, we keep %s each day.\n\n%s" "${HOSTNAME}" "${RETENTION_DAYS}" "${RESTICOUTPUT}" | mail -s "Backup done ${HOSTNAME}" "${BACKUP_EMAIL}" +fi diff --git a/usr/local/sbin/restic_check.sh b/usr/local/sbin/restic_check.sh index 8b547867..62ea6048 100644 --- a/usr/local/sbin/restic_check.sh +++ b/usr/local/sbin/restic_check.sh @@ -16,7 +16,7 @@ exit_hook() { trap exit_hook INT TERM -source /etc/restic/b2_env.sh +source /etc/restic/env.sh # How many network connections to set up to B2. Default is 5. B2_CONNECTIONS=50 From 2a65d2a1acf448a6c9828e6b41d4a84ed614e477 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:04:21 +0000 Subject: [PATCH 3/6] Update documentation and env.sh.template with modern instructions Co-authored-by: payrequestio <9915051+payrequestio@users.noreply.github.com> --- README.md | 143 +++++++++++++++++++++++-------------- etc/restic/env.sh.template | 6 ++ 2 files changed, 94 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 91653d29..065daad1 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,57 @@ -## Directadmin Restic Backup using S3 Block Storage +## DirectAdmin Restic Backup using S3 Block Storage # Requirements -* `restic >=v0.9.6` -* `zstd: for mysql backups` +* `restic` (latest stable version recommended, minimum v0.9.6) +* `zstd` for MySQL backups +* `git` for installation ## Required: Install Restic [restic](https://restic.net/) is a command-line tool for making backups. -Ubuntu: +Ubuntu/Debian: ```bash -$ apt-get install restic && apt-get install git - -sudo apt-get update && sudo apt-get install -y software-properties-common && sudo add-apt-repository -y ppa:copart/restic && sudo apt-get update && sudo apt-get install -y restic git -```` +sudo apt-get update +sudo apt-get install -y restic zstd git +``` -CentOS: +CentOS/RHEL: ```bash -$ yum install yum-plugin-copr && yum copr enable copart/restic && yum install restic && yum install git +sudo yum install -y epel-release +sudo yum install -y restic zstd git +``` -sudo apt-get update && sudo apt-get install -y software-properties-common && sudo add-apt-repository -y ppa:copart/restic && sudo apt-get update && sudo apt-get install -y restic git -```` +For the latest version, you can download from the [official releases page](https://github.com/restic/restic/releases). -## Installguide Directadmin VPS Backup +## Installation Guide for DirectAdmin VPS Backup -Tip: The steps in this section will instruct you to copy files from this repo to system directories. +The following steps will install the backup scripts and configuration files to system directories. ```bash -$ git clone https://github.com/payrequestio/directadmin-vps-backup.git -$ cd directadmin-vps-backup -$ sudo make install -```` +git clone https://github.com/payrequestio/directadmin-restic-backup.git +cd directadmin-restic-backup +sudo make install +``` ### 1. Configure S3 credentials -Put these files in `/etc/restic/`: -* `env.sh`: Fill this file out with your S3 bucket settings. The reason for putting these in a separate file is that it can be used also for you to simply source, when you want to issue some restic commands. For example: +Edit `/etc/restic/env.sh` with your S3 bucket settings. The installation will create this file from the template if it doesn't exist. + +Required configuration: +* `AWS_ACCESS_KEY_ID`: Your S3 access key +* `AWS_SECRET_ACCESS_KEY`: Your S3 secret key +* `RESTIC_PASSWORD`: Password for encrypting backups +* `RESTIC_REPOSITORY`: S3 repository URL (e.g., `s3:https://s3.amazonaws.com/your-bucket-name`) + +Optional configuration: +* `BACKUP_EMAIL`: Email address for backup notifications (used by restic_backup.sh) +* `DISCORD_WEBHOOK`: Discord webhook URL for notifications (used by directadmin-vps-backup.sh) + +Once configured, you can source the file to use restic commands easily: ```bash -$ source /etc/restic/env.sh -$ restic snapshots # You don't have to supply all parameters like --repo, as they are now in your environment! -```` +source /etc/restic/env.sh +restic snapshots # You don't have to supply all parameters like --repo, as they are now in your environment! +``` ### 2. Initialize remote repo Now we must initialize the repository on the remote end: @@ -47,59 +59,80 @@ Now we must initialize the repository on the remote end: source /etc/restic/env.sh && restic init ``` -### 3. Script for doing the backup -Put this file in `/usr/local/sbin`: -* `directadmin-vps-backup.sh`: A script that defines how to run the backup. Edit this file to respect your needs in terms of backup which paths to backup, retention (number of backups to save), etc. +### 3. Configure backup settings +The installation places these files: +* `/usr/local/sbin/directadmin-vps-backup.sh`: Main backup script. Edit this to customize: + - Backup paths (default: `/`, `/boot`, `/home`) + - Retention policy (default: 7 days, 4 weeks, 3 months, 0 years) + - Discord notifications +* `/etc/restic/backup_exclude`: File patterns to exclude from backups (cache, logs, temporary files, etc.) -Put this file in `/`: -* `.backup_exclude`: A list of file pattern paths to exclude from you backups, files that just occupy storage space, backup-time, network and money. +You can also create per-user exclusion files at `$HOME/.backup_exclude` for each user. -### 4. Make first backup & verify -Now see if the backup itself works, by running +### 4. Run first backup & verify +Test the backup to ensure everything works: ```bash -$ /usr/local/sbin/directadmin-vps-backup.sh -$ restic snapshots -```` - -### 5. Backup automatically; systemd service + timer -Now we can do the modern version of a cron-job, a systemd service + timer, to run the backup every day! +/usr/local/sbin/directadmin-vps-backup.sh +source /etc/restic/env.sh +restic snapshots +``` +### 5. Enable automatic backups with systemd +The installation includes systemd service and timer units: +* `directadmin-vps-backup.service`: Service that executes the backup script +* `directadmin-vps-backup.timer`: Timer that runs backups daily +* `directadmin-vps-backup-check.service`: Service for checking backup integrity +* `directadmin-vps-backup-check.timer`: Timer that runs checks monthly -Put these files in `/etc/systemd/system/`: -* `directadmin-vps-backup.service`: A service that calls the backup script. -* `directadmin-vps-backup.timer`: A timer that starts the backup every day. +Enable and start the backup timer: +```bash +systemctl daemon-reload +systemctl enable --now directadmin-vps-backup.timer +systemctl enable --now directadmin-vps-backup-check.timer +``` +### 6. Managing backups -Now simply enable the timer with: +View scheduled backup times: ```bash -$ systemctl start directadmin-vps-backup.timer -$ systemctl enable directadmin-vps-backup.timer -```` +systemctl list-timers | grep directadmin-vps-backup +``` -You can see when your next backup is scheduled to run with +Check backup status: ```bash -$ systemctl list-timers | grep directadmin-vps-backup +systemctl status directadmin-vps-backup ``` -and see the status of a currently running backup with - +Start a backup manually: ```bash -$ systemctl status directadmin-vps-backup +systemctl start directadmin-vps-backup ``` -or start a backup manually +View backup logs in real-time: +```bash +journalctl -f -u directadmin-vps-backup.service +``` +View all backup logs: ```bash -$ systemctl start directadmin-vps-backup +journalctl -u directadmin-vps-backup.service ``` -You can follow the backup stdout output live as backup is running with: +## Additional Scripts -```bash -$ journalctl -f -u directadmin-vps-backup.service -```` +This repository includes additional helper scripts: +* `restic_backup.sh`: Alternative backup script with email notifications +* `restic_check.sh`: Repository integrity check script +* `mysql.sh`: MySQL database backup script (dumps to `/home/mysqlbackups` with zstd compression) + +## Troubleshooting -(skip `-f` to see all backups that has run) +If you encounter issues: +1. Check that restic is properly installed: `restic version` +2. Verify your S3 credentials in `/etc/restic/env.sh` +3. Test repository access: `source /etc/restic/env.sh && restic snapshots` +4. Review service logs: `journalctl -u directadmin-vps-backup.service` +5. Ensure sufficient disk space for MySQL dumps and temporary files diff --git a/etc/restic/env.sh.template b/etc/restic/env.sh.template index 1b70e462..c35a8816 100644 --- a/etc/restic/env.sh.template +++ b/etc/restic/env.sh.template @@ -4,3 +4,9 @@ export AWS_ACCESS_KEY_ID="IDhere" export AWS_SECRET_ACCESS_KEY="Keyhere" export RESTIC_PASSWORD="passwordhere" export RESTIC_REPOSITORY="s3:https://s3.amazonaws.com/bucketname" + +# Optional: Email notifications (used by restic_backup.sh) +# export BACKUP_EMAIL="admin@example.com" + +# Optional: Discord webhook for notifications (used by directadmin-vps-backup.sh) +# export DISCORD_WEBHOOK="https://discord.com/api/webhooks/your-webhook-url" From 4c49a0a8ea34d5a2f5ca5b454c950135c7e384df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:06:56 +0000 Subject: [PATCH 4/6] Remove unused variable and improve configurability Co-authored-by: payrequestio <9915051+payrequestio@users.noreply.github.com> --- etc/restic/env.sh.template | 3 +++ etc/systemd/system/status-email-user@.service | 6 +++++- usr/local/sbin/directadmin-vps-backup.sh | 9 +++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/etc/restic/env.sh.template b/etc/restic/env.sh.template index c35a8816..1aceaa92 100644 --- a/etc/restic/env.sh.template +++ b/etc/restic/env.sh.template @@ -8,5 +8,8 @@ export RESTIC_REPOSITORY="s3:https://s3.amazonaws.com/bucketname" # Optional: Email notifications (used by restic_backup.sh) # export BACKUP_EMAIL="admin@example.com" +# Optional: Systemd failure notification email (used by status-email-user@.service) +# export SYSTEMD_EMAIL="admin@example.com" + # Optional: Discord webhook for notifications (used by directadmin-vps-backup.sh) # export DISCORD_WEBHOOK="https://discord.com/api/webhooks/your-webhook-url" diff --git a/etc/systemd/system/status-email-user@.service b/etc/systemd/system/status-email-user@.service index a2897eda..9acadf75 100644 --- a/etc/systemd/system/status-email-user@.service +++ b/etc/systemd/system/status-email-user@.service @@ -1,11 +1,15 @@ # Source: https://serverfault.com/questions/876233/how-to-send-an-email-if-a-systemd-service-is-restarted # Source: https://wiki.archlinux.org/index.php/Systemd/Timers#MAILTO +# +# NOTE: Replace 'admin@example.com' with your actual email address +# or set SYSTEMD_EMAIL environment variable in /etc/restic/env.sh [Unit] Description=Send status email for %i to user [Service] Type=oneshot -ExecStart=/usr/local/sbin/systemd-email abc@gmail.com %i +EnvironmentFile=-/etc/restic/env.sh +ExecStart=/usr/local/sbin/systemd-email ${SYSTEMD_EMAIL:-admin@example.com} %i User=root Group=systemd-journal diff --git a/usr/local/sbin/directadmin-vps-backup.sh b/usr/local/sbin/directadmin-vps-backup.sh index 5b67b4ca..c9b9e2ed 100644 --- a/usr/local/sbin/directadmin-vps-backup.sh +++ b/usr/local/sbin/directadmin-vps-backup.sh @@ -1,9 +1,11 @@ #!/usr/bin/env bash # Make backup my system with restic -# Run first Directadmin Cleaner -bash /usr/local/sbin/directadmin-cleaner.sh & -wait $! +# Run first Directadmin Cleaner (optional - create this script if you need cleanup before backup) +if [ -f /usr/local/sbin/directadmin-cleaner.sh ]; then + bash /usr/local/sbin/directadmin-cleaner.sh & + wait $! +fi ## add your discord channel webhook (or set DISCORD_WEBHOOK in env.sh) discord="${DISCORD_WEBHOOK:-URL}" @@ -104,7 +106,6 @@ wait $! #restic check & #wait $! -RESTICSNAPSHOTS="restic snapshots --no-lock --json --repo ${RESTIC_REPOSITORY}" COUNT=$(restic snapshots --compact --repo "${RESTIC_REPOSITORY}" | awk -F '\t' '{print $1}' | wc -l) HOSTNAME=$(hostname) From 35e6b36a99f4d794adfe225911a27ece1dad0aed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:09:07 +0000 Subject: [PATCH 5/6] Add systemd-email wrapper for proper environment variable handling Co-authored-by: payrequestio <9915051+payrequestio@users.noreply.github.com> --- etc/systemd/system/status-email-user@.service | 7 +++---- usr/local/sbin/systemd-email-wrapper.sh | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100755 usr/local/sbin/systemd-email-wrapper.sh diff --git a/etc/systemd/system/status-email-user@.service b/etc/systemd/system/status-email-user@.service index 9acadf75..d82c8f2d 100644 --- a/etc/systemd/system/status-email-user@.service +++ b/etc/systemd/system/status-email-user@.service @@ -1,15 +1,14 @@ # Source: https://serverfault.com/questions/876233/how-to-send-an-email-if-a-systemd-service-is-restarted # Source: https://wiki.archlinux.org/index.php/Systemd/Timers#MAILTO # -# NOTE: Replace 'admin@example.com' with your actual email address -# or set SYSTEMD_EMAIL environment variable in /etc/restic/env.sh +# NOTE: Set SYSTEMD_EMAIL environment variable in /etc/restic/env.sh +# or the default admin@example.com will be used [Unit] Description=Send status email for %i to user [Service] Type=oneshot -EnvironmentFile=-/etc/restic/env.sh -ExecStart=/usr/local/sbin/systemd-email ${SYSTEMD_EMAIL:-admin@example.com} %i +ExecStart=/usr/local/sbin/systemd-email-wrapper.sh %i User=root Group=systemd-journal diff --git a/usr/local/sbin/systemd-email-wrapper.sh b/usr/local/sbin/systemd-email-wrapper.sh new file mode 100755 index 00000000..81c3b0e0 --- /dev/null +++ b/usr/local/sbin/systemd-email-wrapper.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Wrapper script for systemd-email to handle environment variables +# This ensures SYSTEMD_EMAIL is properly set before sending notifications + +# Source environment if available +if [ -f /etc/restic/env.sh ]; then + source /etc/restic/env.sh +fi + +# Use SYSTEMD_EMAIL from environment, or fall back to default +EMAIL="${SYSTEMD_EMAIL:-admin@example.com}" +UNIT="$1" + +# Call the actual systemd-email script +exec /usr/local/sbin/systemd-email "$EMAIL" "$UNIT" From 3ab03c861cf0d614a2ae9b941a8c6adc05fd3294 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:10:43 +0000 Subject: [PATCH 6/6] Improve systemd-email-wrapper with validation and POSIX compliance Co-authored-by: payrequestio <9915051+payrequestio@users.noreply.github.com> --- usr/local/sbin/systemd-email-wrapper.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/usr/local/sbin/systemd-email-wrapper.sh b/usr/local/sbin/systemd-email-wrapper.sh index 81c3b0e0..3eba9e97 100755 --- a/usr/local/sbin/systemd-email-wrapper.sh +++ b/usr/local/sbin/systemd-email-wrapper.sh @@ -2,9 +2,15 @@ # Wrapper script for systemd-email to handle environment variables # This ensures SYSTEMD_EMAIL is properly set before sending notifications +# Validate required argument +if [ -z "$1" ]; then + echo "Error: Unit name required" >&2 + exit 1 +fi + # Source environment if available if [ -f /etc/restic/env.sh ]; then - source /etc/restic/env.sh + . /etc/restic/env.sh fi # Use SYSTEMD_EMAIL from environment, or fall back to default