This gist belongs to the blog post Home Server Blueprint: Rock-Solid Home Server with Unattended Reboots, Secure Disk Encryption, and Cost-Effective Offsite Backups.
-
-
Save cs224/34eebc2f9389404d7c0192d45cae7259 to your computer and use it in GitHub Desktop.
Home Server Blueprint: Rock-Solid Home Server with Unattended Reboots, Secure Disk Encryption, and Cost-Effective Offsite Backups
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# .env file | |
# Secret (avoid storing real secrets in version control!) | |
KOPIA_REPOSITORY_SECRET=mysupersecret | |
LUKS_PASSPHRASE=abc123 | |
# Toggle alerting | |
ALERT_ENABLED=false | |
# Pushover credentials | |
PUSHOVER_TOKEN=your_pushover_token | |
PUSHOVER_USER=your_pushover_user_key | |
# Daily backup time e.g. 04:00:00 for 4 AM | |
DAILY_BACKUP_TIME=*-*-* 04:00:00 | |
# Etc. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=BTRFS to Kopia Backup (Offsite) | |
Requires=network-online.target | |
After=network-online.target | |
[Service] | |
Type=oneshot | |
Environment=HOME="/root" | |
ExecStart=/usr/local/bin/btrfs-kopia-backup.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# --------------------------------------------------------- | |
# btrfs-kopia-backup.sh | |
# | |
# 1) Create a read-only BTRFS snapshot | |
# 2) Run a Kopia snapshot | |
# 3) Rename the snapshot with a date/time suffix | |
# 4) (Optional) Send a Pushover alert if something fails | |
# --------------------------------------------------------- | |
set -e | |
############################################################################### | |
# CONFIG SECTION | |
# | |
# Toggle this to "true" to enable Pushover alerts on error, or to "false" (or | |
# comment out) if you do not want alerts. | |
############################################################################### | |
ALERT_ENABLED=true | |
############################################################################### | |
# "I am alive" message configuration | |
# Possible values: "daily", "weekly", "monthly", or anything else means "false" | |
############################################################################### | |
ALIVE_MESSAGE_ENABLED="false" | |
# Pushover credentials (DO NOT store secrets in version control!) | |
PUSHOVER_TOKEN="...sometoken..." | |
PUSHOVER_USER="...someuser..." | |
############################################################################### | |
# Pushover function | |
# You can comment out the entire function if you never want to use it, | |
# or just set ALERT_ENABLED=false above. | |
############################################################################### | |
function pushover_alert() { | |
local message="$1" | |
local title="$2" | |
# If your Pushover config includes more data (HTML, link, etc.), adapt as needed | |
curl -s --fail \ | |
-F "token=${PUSHOVER_TOKEN}" \ | |
-F "user=${PUSHOVER_USER}" \ | |
-F "sound=none" \ | |
-F "title=${title}" \ | |
-F "message=${message}" \ | |
-F "html=1" \ | |
https://api.pushover.net/1/messages.json \ | |
|| echo "Warning: Failed to send Pushover alert (curl error)." | |
} | |
############################################################################### | |
# Error Handler | |
# This function is called automatically on any command error | |
# (because of 'set -e' plus the 'trap' directive). | |
############################################################################### | |
function error_handler() { | |
local exit_code=$? | |
local line_no=$1 | |
echo "ERROR: Backup script failed at line ${line_no}. Exit code: ${exit_code}" | |
if [ "$ALERT_ENABLED" = "true" ]; then | |
# Build an alert message. Adjust to taste: | |
pushover_alert \ | |
"Backup on host $(hostname) failed at line ${line_no} (exit code ${exit_code})" \ | |
"BTRFS-Kopia Backup Failed" | |
fi | |
# Exit with the same code to signal failure | |
exit "$exit_code" | |
} | |
# If alerts are enabled, set a trap to catch errors and call 'error_handler' | |
if [ "$ALERT_ENABLED" = "true" ]; then | |
trap 'error_handler $LINENO' ERR | |
fi | |
############################################################################### | |
# Main Backup Logic | |
############################################################################### | |
SRC_SUBVOL="/mnt/luks_btrfs_volume/@offsite_backup_storage" | |
SNAP_DEST="/mnt/luks_btrfs_volume/@offsite_backup_storage_backup-snap" | |
echo "Creating read-only BTRFS snapshot...: btrfs subvolume snapshot -r ${SRC_SUBVOL} ${SNAP_DEST}" | |
btrfs subvolume snapshot -r "$SRC_SUBVOL" "$SNAP_DEST" | |
echo "Running Kopia snapshot...: /usr/bin/kopia snapshot create --parallel=1 ${SNAP_DEST} " | |
/usr/bin/kopia snapshot create --parallel=1 "$SNAP_DEST" | |
TIMESTAMP="$(date +%Y-%m-%d-%H%M)" | |
NEW_NAME="/mnt/luks_btrfs_volume/@offsite_backup_storage_snapshot-${TIMESTAMP}" | |
echo "Renaming snapshot to $NEW_NAME" | |
mv "$SNAP_DEST" "$NEW_NAME" | |
echo "Backup completed successfully!" | |
# Optionally send an "I am alive" message if enabled and successful | |
if [ "$ALERT_ENABLED" = "true" ]; then | |
day_of_week="$(date +%u)" # Monday=1 ... Sunday=7 | |
day_of_month="$(date +%d)" # 01..31 | |
case "$ALIVE_MESSAGE_ENABLED" in | |
daily) | |
pushover_alert \ | |
"Backup on host $(hostname) completed successfully." \ | |
"BTRFS-Kopia Backup Succeeded" | |
;; | |
weekly) | |
if [ "$day_of_week" = "1" ]; then | |
pushover_alert \ | |
"Backup on host $(hostname) completed successfully (weekly check)." \ | |
"BTRFS-Kopia Backup Succeeded" | |
fi | |
;; | |
monthly) | |
if [ "$day_of_month" = "01" ]; then | |
pushover_alert \ | |
"Backup on host $(hostname) completed successfully (monthly check)." \ | |
"BTRFS-Kopia Backup Succeeded" | |
fi | |
;; | |
*) | |
# No "alive" messages if set to anything else | |
;; | |
esac | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=Daily BTRFS to Kopia Backup at 4:00 AM | |
[Timer] | |
OnCalendar=*-*-* 04:00:00 | |
Persistent=true | |
[Install] | |
WantedBy=timers.target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# <mapped_name> <source> <keyfile> <options> | |
luks_btrfs_volume /opt/luks-btrfs-volume.img none luks,noauto,loop,timeout=120 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
RequiresMountsFor=/opt/docker_services | |
After=opt-docker_services.mount |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# <file system> <mount point> <type> <options> <dump> <pass> | |
/dev/mapper/luks_btrfs_volume /mnt/luks_btrfs_volume btrfs noauto,x-systemd.automount,relatime,compress=zstd:3,defaults 0 0 | |
/mnt/luks_btrfs_volume/@offsite_backup_storage/storage /opt/offsite_backup_storage none bind,noauto,x-systemd.automount,x-systemd.requires=/mnt/luks_btrfs_volume,x-systemd.after=/mnt/luks_btrfs_volume 0 0 | |
/mnt/luks_btrfs_volume/@offsite_backup_storage/docker_services /opt/docker_services none bind,noauto,x-systemd.automount,x-systemd.requires=/opt/offsite_backup_storage,x-systemd.after=/opt/offsite_backup_storage 0 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################################################### | |
# Makefile for setting up a LUKS-encrypted BTRFS volume, Docker systemd override, | |
# and Kopia/Rclone backup infrastructure on a Debian/Ubuntu-based system. | |
# | |
# USAGE: | |
# 1) Set any desired environment variables in ".env". | |
# 2) Run "make all" to perform the entire setup. | |
# 3) If a step fails, fix the issue and re-run "make all"; it will continue | |
# from where it left off. | |
# | |
# OPTIONAL TARGETS (not part of "make all"): | |
# make kopia-repository-create | |
# make kopia-repository-connect | |
# | |
# NOTES / RECOMMENDATIONS FOR CUSTOMIZATION: | |
# - Adjust volume size (IMG_SIZE) below. | |
# - If you want to store your LUKS file in a different location, change | |
# LUKS_BTRFS_IMG and LUKS_BTRFS_NAME accordingly. | |
# - If you need to pass other arguments to cryptsetup, adjust LUKS_FORMAT_OPTS. | |
# - Tweak subvolume paths, Docker override paths, or systemd unit install paths | |
# as you see fit. | |
# - The .env file is used for "offline templating" of some files (like | |
# btrfs-kopia-backup.timer and btrfs-kopia-backup.sh). Once the Makefile | |
# finishes, you can remove .env (unless you need to re-run the Makefile and | |
# want the same settings). | |
############################################################################### | |
SHELL := /bin/bash | |
# | |
# 1) Load environment variables from .env (if present). | |
# We assume simple KEY=VALUE lines with no spaces. | |
# | |
ifneq ("$(wildcard .env)","") | |
include .env | |
# export $(shell sed 's/=.*//' .env) | |
# This way, lines that start with # or don’t have an = sign are safely ignored. | |
export $(shell grep -E '^[[:alnum:]_]+=' .env | cut -d= -f1) | |
endif | |
############################################################################### | |
# CONFIGURABLE VARIABLES (can be overridden in .env or via environment) | |
############################################################################### | |
HOSTNAME ?= $(shell hostname) | |
# you can set in .env or pass from CLI: LUKS_PASSPHRASE=abc123 | |
LUKS_PASSPHRASE ?= abc123 | |
# The size of the LUKS volume image: | |
IMG_SIZE ?= 100G | |
# Path and name of the LUKS volume image: | |
LUKS_BTRFS_IMG ?= /opt/luks-btrfs-volume.img | |
# Mapped name inside /dev/mapper: | |
LUKS_BTRFS_NAME ?= luks_btrfs_volume | |
# Subvolume directory name inside /mnt/luks_btrfs_volume: | |
BTRFS_SUBVOLUME ?= @offsite_backup_storage | |
# Additional sub-dirs that will hold persistent data: | |
DOCKER_SERVICES ?= docker_services | |
OFFSITE_STORAGE ?= storage | |
# Where it will be mounted (on-demand, after LUKS password is given): | |
LUKS_MOUNTPOINT ?= /mnt/luks_btrfs_volume | |
# Docker data mount point: | |
DOCKER_DIR ?= /opt/docker_services | |
# Offsite data mount point: | |
OFFSITE_DIR ?= /opt/offsite_backup_storage | |
# Default cryptsetup format options (adjust if needed): | |
LUKS_FORMAT_OPTS ?= --type luks2 -y -v | |
# Global policy for Kopia (some recommended settings): | |
KOPIA_POLICY_PARALLELISM ?= --max-parallel-snapshots=1 --max-parallel-file-reads=1 | |
KOPIA_POLICY_RETENTION ?= --keep-latest=7 --keep-hourly=0 --keep-daily=14 --keep-weekly=4 --keep-monthly=12 --keep-annual=3 | |
KOPIA_POLICY_COMPRESSION ?= --compression=pgzip | |
# A directory for stamp files to track progress: | |
STAMP_DIR ?= .stamp | |
# The time for the daily backup from the .env or fallback: | |
# e.g., "*-*-* 04:00:00" for daily at 4 AM | |
DAILY_BACKUP_TIME ?= *-*-* 04:00:00 | |
# The Kopia repository password from .env | |
# e.g.: KOPIA_REPOSITORY_SECRET=...mysecret... | |
KOPIA_REPOSITORY_SECRET ?= CHANGEME | |
############################################################################### | |
# HELPER CONFIG / DO NOT CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING | |
############################################################################### | |
APT_UPDATED_STAMP := $(STAMP_DIR)/apt-updated | |
CRYPTSETUP_INSTALLED := $(STAMP_DIR)/cryptsetup-installed | |
RCLONE_INSTALLED := $(STAMP_DIR)/rclone-installed | |
KOPIA_INSTALLED := $(STAMP_DIR)/kopia-installed | |
LUKS_VOLUME_IMG_CREATED := $(STAMP_DIR)/luks-volume-img-created | |
LUKS_FORMATTED_STAMP := $(STAMP_DIR)/luks-formatted | |
LUKS_OPENED_STAMP := $(STAMP_DIR)/luks-opened | |
BTRFS_FORMATTED_STAMP := $(STAMP_DIR)/btrfs-formatted | |
BTRFS_SUBVOL_CREATED := $(STAMP_DIR)/btrfs-subvol-created | |
SYSTEMD_CONFIG_INSTALLED := $(STAMP_DIR)/systemd-config-installed | |
KOPIA_POLICY_STAMP := $(STAMP_DIR)/kopia-policy | |
MKDIR_OPT_DOCKER := $(STAMP_DIR)/mkdir-opt-docker | |
MKDIR_OPT_OFFSITE := $(STAMP_DIR)/mkdir-opt-offsite | |
# For convenience, define directories we want: | |
DIRS_TO_CREATE = $(DOCKER_DIR) $(OFFSITE_DIR) $(STAMP_DIR) | |
.PHONY: all | |
all: \ | |
apt-update \ | |
install-cryptsetup \ | |
install-rclone \ | |
install-kopia \ | |
$(LUKS_BTRFS_IMG) \ | |
luks-format \ | |
luks-open \ | |
btrfs-format \ | |
btrfs-subvolume \ | |
mkdirs \ | |
install-systemd-configs | |
# kopia-global-policy | |
@echo "Setup complete!" | |
@echo "You may now check your systemd configurations, crypttab, and fstab." | |
@echo "Remember that after a reboot, the encrypted volume remains locked until you provide the password." | |
@echo "Once the system is up, run: systemctl start docker.service" | |
@echo "That will trigger the full chain of systemd dependencies requiring the LUKS passphrase." | |
############################################################################### | |
# STEP: apt update | |
############################################################################### | |
.PHONY: apt-update | |
apt-update: $(APT_UPDATED_STAMP) | |
$(APT_UPDATED_STAMP): | |
@echo "=== [APT UPDATE] Updating package lists..." | |
apt-get update | |
@mkdir -p $(STAMP_DIR) | |
@touch $@ | |
############################################################################### | |
# STEP: install-cryptsetup | |
############################################################################### | |
.PHONY: install-cryptsetup | |
install-cryptsetup: $(CRYPTSETUP_INSTALLED) | |
$(CRYPTSETUP_INSTALLED): $(APT_UPDATED_STAMP) | |
@echo "=== [INSTALL] Installing cryptsetup..." | |
apt-get install -y cryptsetup unzip p7zip-full btrfs-progs file | |
@touch $@ | |
############################################################################### | |
# STEP: install-rclone | |
############################################################################### | |
.PHONY: install-rclone | |
install-rclone: $(RCLONE_INSTALLED) | |
$(RCLONE_INSTALLED): $(APT_UPDATED_STAMP) | |
@echo "=== [INSTALL] Installing rclone via official script..." | |
@curl -fsSL https://rclone.org/install.sh | sudo bash; \ | |
status=$$?; \ | |
if [ $$status -eq 3 ]; then \ | |
echo "rclone already up to date, continuing..."; \ | |
elif [ $$status -ne 0 ]; then \ | |
exit $$status; \ | |
fi | |
@touch $@ | |
############################################################################### | |
# STEP: install-kopia | |
############################################################################### | |
.PHONY: install-kopia | |
install-kopia: $(KOPIA_INSTALLED) | |
$(KOPIA_INSTALLED): $(APT_UPDATED_STAMP) | |
@echo "=== [INSTALL] Installing Kopia from official repository..." | |
@echo "=== [INSTALL] Adding Kopia signing key..." | |
curl -s https://kopia.io/signing-key | gpg --dearmor --batch --yes -o /etc/apt/keyrings/kopia-keyring.gpg | |
@echo "=== [INSTALL] Adding Kopia repo to /etc/apt/sources.list.d/kopia.list..." | |
echo "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" | tee /etc/apt/sources.list.d/kopia.list | |
apt-get update | |
apt-get install -y kopia | |
@touch $@ | |
############################################################################### | |
# STEP: Create LUKS volume image file | |
############################################################################### | |
$(LUKS_BTRFS_IMG): $(LUKS_VOLUME_IMG_CREATED) | |
$(LUKS_VOLUME_IMG_CREATED): | |
@echo "=== [CREATE] LUKS volume image at $(LUKS_BTRFS_IMG) with size $(IMG_SIZE)" | |
@if [ ! -f "$(LUKS_BTRFS_IMG)" ]; then \ | |
echo "File $(LUKS_BTRFS_IMG) not found. Creating it now..."; \ | |
truncate -s $(IMG_SIZE) $(LUKS_BTRFS_IMG); \ | |
else \ | |
echo "File $(LUKS_BTRFS_IMG) already exists."; \ | |
echo "To change the current file, remove it (and the stamp file) manually. For example:"; \ | |
echo " rm $(LUKS_BTRFS_IMG) && rm -f $(STAMP_DIR)/$(LUKS_VOLUME_IMG_CREATED)"; \ | |
fi | |
@mkdir -p $(STAMP_DIR) | |
@touch $@ | |
############################################################################### | |
# STEP: LUKS-format the image | |
############################################################################### | |
.PHONY: luks-format | |
luks-format: $(LUKS_FORMATTED_STAMP) | |
$(LUKS_FORMATTED_STAMP): $(LUKS_VOLUME_IMG_CREATED) | |
@if cryptsetup isLuks $(LUKS_BTRFS_IMG) >/dev/null 2>&1; then \ | |
echo "File $(LUKS_BTRFS_IMG) is already LUKS formatted."; \ | |
echo "If you want to re-format (this may cause data loss), please remove the LUKS header and stamp file manually."; \ | |
echo "For example:"; \ | |
echo " cryptsetup luksErase $(LUKS_BTRFS_IMG) && rm -f $(LUKS_FORMATTED_STAMP)"; \ | |
else \ | |
echo "=== [LUKS FORMAT] luksFormat for $(LUKS_BTRFS_IMG) non-interactively..."; \ | |
echo -n "$(LUKS_PASSPHRASE)" | cryptsetup $(LUKS_FORMAT_OPTS) --batch-mode luksFormat $(LUKS_BTRFS_IMG) --key-file=-; \ | |
fi | |
@touch $@ | |
############################################################################### | |
# STEP: Open the LUKS volume | |
# NOTE: This requires user to type the LUKS passphrase. | |
############################################################################### | |
.PHONY: luks-open | |
# luks-open: $(LUKS_OPENED_STAMP) | |
# $(LUKS_OPENED_STAMP): $(LUKS_FORMATTED_STAMP) | |
# @echo "=== [LUKS OPEN] Opening the LUKS volume: $(LUKS_BTRFS_IMG)" | |
# cryptsetup open $(LUKS_BTRFS_IMG) $(LUKS_BTRFS_NAME) | |
# @touch $@ | |
luks-open: $(LUKS_OPENED_STAMP) | |
$(LUKS_OPENED_STAMP): $(LUKS_FORMATTED_STAMP) | |
@echo "=== [LUKS OPEN] Opening the LUKS volume non-interactively..." | |
echo -n "$(LUKS_PASSPHRASE)" | cryptsetup open $(LUKS_BTRFS_IMG) $(LUKS_BTRFS_NAME) --key-file=- | |
@touch $@ | |
############################################################################### | |
# STEP: Format the opened device as BTRFS | |
############################################################################### | |
.PHONY: btrfs-format | |
btrfs-format: $(BTRFS_FORMATTED_STAMP) | |
$(BTRFS_FORMATTED_STAMP): $(LUKS_OPENED_STAMP) | |
@target=$$(readlink -f /dev/mapper/$(LUKS_BTRFS_NAME)); \ | |
if file -s $$target | grep -q "BTRFS"; then \ | |
echo "BTRFS filesystem already exists on /dev/mapper/$(LUKS_BTRFS_NAME)."; \ | |
echo "If you wish to reformat (WARNING: this will destroy your data), remove the filesystem signature and the stamp file manually."; \ | |
echo "For example:"; \ | |
echo " wipefs -a /dev/mapper/$(LUKS_BTRFS_NAME) && rm -f $(BTRFS_FORMATTED_STAMP)"; \ | |
else \ | |
echo "=== [BTRFS FORMAT] Creating BTRFS on /dev/mapper/$(LUKS_BTRFS_NAME)"; \ | |
mkfs.btrfs /dev/mapper/$(LUKS_BTRFS_NAME); \ | |
fi | |
@touch $@ | |
############################################################################### | |
# STEP: Create BTRFS subvolume | |
# We must mount the device temporarily, create subvolume(s), then unmount. | |
############################################################################### | |
.PHONY: btrfs-subvolume | |
btrfs-subvolume: $(BTRFS_SUBVOL_CREATED) | |
$(BTRFS_SUBVOL_CREATED): $(BTRFS_FORMATTED_STAMP) | |
@echo "=== [BTRFS] Mounting device to create subvolume: $(BTRFS_SUBVOLUME)" | |
mkdir -p $(LUKS_MOUNTPOINT) | |
mount -t btrfs -o noatime,compress=zstd /dev/mapper/$(LUKS_BTRFS_NAME) $(LUKS_MOUNTPOINT) | |
# create the subvolume | |
btrfs subvolume create "$(LUKS_MOUNTPOINT)/$(BTRFS_SUBVOLUME)" | |
mkdir -p "$(LUKS_MOUNTPOINT)/$(BTRFS_SUBVOLUME)/$(DOCKER_SERVICES)" | |
mkdir -p "$(LUKS_MOUNTPOINT)/$(BTRFS_SUBVOLUME)/$(OFFSITE_STORAGE)" | |
# @echo "=== [BTRFS] Unmounting after subvolume creation..." | |
# umount $(LUKS_MOUNTPOINT) | |
@touch $@ | |
############################################################################### | |
# STEP: Create mountpoint directories on the host | |
############################################################################### | |
.PHONY: mkdirs | |
mkdirs: $(MKDIR_OPT_DOCKER) $(MKDIR_OPT_OFFSITE) | |
$(MKDIR_OPT_DOCKER): | |
@echo "=== [MOUNTPOINT] Creating $(DOCKER_DIR) if it does not exist..." | |
mkdir -p $(DOCKER_DIR) | |
@touch $@ | |
$(MKDIR_OPT_OFFSITE): | |
@echo "=== [MOUNTPOINT] Creating $(OFFSITE_DIR) if it does not exist..." | |
mkdir -p $(OFFSITE_DIR) | |
@touch $@ | |
############################################################################### | |
# STEP: Install systemd config fragments (crypttab, fstab) and Docker override | |
############################################################################### | |
.PHONY: install-systemd-configs | |
install-systemd-configs: $(SYSTEMD_CONFIG_INSTALLED) | |
$(SYSTEMD_CONFIG_INSTALLED): crypttab.fragment fstab.fragment docker.service.override.conf btrfs-kopia-backup.timer btrfs-kopia-backup.service btrfs-kopia-backup.sh | |
@echo "=== [SYSTEMD CONFIG] Appending crypttab.fragment to /etc/crypttab (if not already present)..." | |
@if ! grep -q '$(LUKS_BTRFS_NAME)' /etc/crypttab 2>/dev/null; then \ | |
cat crypttab.fragment >> /etc/crypttab; \ | |
else \ | |
echo " -> /etc/crypttab already has an entry for '$(LUKS_BTRFS_NAME)'"; \ | |
fi | |
@echo "=== [SYSTEMD CONFIG] Appending fstab.fragment to /etc/fstab (if not already present)..." | |
@if ! grep -q '$(LUKS_MOUNTPOINT)' /etc/fstab 2>/dev/null; then \ | |
cat fstab.fragment >> /etc/fstab; \ | |
else \ | |
echo " -> /etc/fstab already has an entry for '$(LUKS_MOUNTPOINT)'"; \ | |
fi | |
@echo "=== [SYSTEMD CONFIG] Installing Docker override in /etc/systemd/system/docker.service.d/override.conf ..." | |
mkdir -p /etc/systemd/system/docker.service.d | |
cp docker.service.override.conf /etc/systemd/system/docker.service.d/override.conf | |
@echo "=== [SYSTEMD CONFIG] Templating btrfs-kopia-backup.timer -> /etc/systemd/system/btrfs-kopia-backup.timer ..." | |
@# We'll replace OnCalendar= with the value from $(DAILY_BACKUP_TIME) | |
sed "s|OnCalendar=.*|OnCalendar=$(DAILY_BACKUP_TIME)|g" btrfs-kopia-backup.timer > /etc/systemd/system/btrfs-kopia-backup.timer | |
@echo "=== [SYSTEMD CONFIG] Installing btrfs-kopia-backup.service -> /etc/systemd/system/" | |
cp btrfs-kopia-backup.service /etc/systemd/system/btrfs-kopia-backup.service | |
@echo "=== [SYSTEMD CONFIG] Installing btrfs-kopia-backup.sh -> /usr/local/bin (with envsubst for ALERT settings)" | |
cp btrfs-kopia-backup.sh btrfs-kopia-backup.sh.tmp | |
# We'll envsubst just the lines that mention ALERT, PUSHOVER_TOKEN, and PUSHOVER_USER if present: | |
@# If your script has placeholders, adapt accordingly. Here, we assume the user put placeholders or checks. | |
sed -i "s|^ALERT_ENABLED=.*|ALERT_ENABLED=${ALERT_ENABLED}|" btrfs-kopia-backup.sh.tmp | |
sed -i "s|^PUSHOVER_TOKEN=.*|PUSHOVER_TOKEN=\"${PUSHOVER_TOKEN}\"|" btrfs-kopia-backup.sh.tmp | |
sed -i "s|^PUSHOVER_USER=.*|PUSHOVER_USER=\"${PUSHOVER_USER}\"|" btrfs-kopia-backup.sh.tmp | |
install -m 0755 btrfs-kopia-backup.sh.tmp /usr/local/bin/btrfs-kopia-backup.sh | |
rm -f btrfs-kopia-backup.sh.tmp | |
@echo "=== [SYSTEMD] Reloading daemon to register new/modified units..." | |
systemctl daemon-reload | |
@echo "=== [SYSTEMD] Making sure that `opt-offsite_backup_storage.mount` and `opt-docker_services.mount` are started ..." | |
systemctl start opt-docker_services.mount | |
mount | grep '/opt/' | |
findmnt -T /opt/docker_services | |
findmnt -T /opt/offsite_backup_storage | |
@echo "=== [SYSTEMD] Enabling the btrfs-kopia-backup.timer so it starts on boot... XXX DO THIS YOURSELF XXX ..." | |
echo systemctl enable btrfs-kopia-backup.timer | |
@mkdir -p $(STAMP_DIR) | |
@touch $@ | |
############################################################################### | |
# STEP: Set Kopia Global Policies | |
############################################################################### | |
.PHONY: kopia-global-policy | |
kopia-global-policy: $(KOPIA_POLICY_STAMP) | |
$(KOPIA_POLICY_STAMP): $(KOPIA_INSTALLED) | |
@echo "=== [KOPIA] Setting global Kopia policies..." | |
kopia policy set --global $(KOPIA_POLICY_PARALLELISM) | |
kopia policy set --global $(KOPIA_POLICY_RETENTION) | |
kopia policy set --global $(KOPIA_POLICY_COMPRESSION) | |
@touch $@ | |
############################################################################### | |
# Optional: Create a new Kopia repository (NOT part of all) | |
############################################################################### | |
.PHONY: kopia-repository-create | |
kopia-repository-create: $(KOPIA_INSTALLED) | |
@echo "=== [KOPIA] Creating new Kopia repository with Rclone remote..." | |
@echo "Replace 'rclone-backup-sharepoint-site:\"${HOSTNAME}\"' with your actual remote path." | |
kopia repository create rclone --password "${KOPIA_REPOSITORY_SECRET}" --remote-path=rclone-backup-sharepoint-site:"${HOSTNAME}" --description="My Offsite BTRFS Snapshots for ${HOSTNAME}" --content-cache-size-mb=512 --metadata-cache-size-mb=512 | |
@echo "=== [INFO] Kopia repository created. Adjust your remote path/password if needed." | |
############################################################################### | |
# Optional: Connect to an existing Kopia repository (NOT part of all) | |
############################################################################### | |
.PHONY: kopia-repository-connect | |
kopia-repository-connect: | |
@echo "=== [KOPIA] Connecting to existing Kopia repository with Rclone remote..." | |
@echo "Replace 'rclone-backup-sharepoint-site:\"${HOSTNAME}\"' with your actual remote path." | |
kopia repository connect rclone --remote-path=rclone-backup-sharepoint-site:"${HOSTNAME}" | |
@echo "=== [INFO] Kopia repository connected." | |
############################################################################### | |
# Housekeeping: a phony target to nudge systemd to reload (if you tweak configs) | |
############################################################################### | |
.PHONY: systemd-reload | |
systemd-reload: | |
@echo "=== [SYSTEMD] Reloading daemon..." | |
systemctl daemon-reload | |
############################################################################### | |
# Deinstall: a phony target to deinstall the systemd timers, services and overrides | |
############################################################################### | |
.PHONY: deinstall | |
deinstall: | |
@echo "=== [DEINSTALL] Disabling and stopping the btrfs-kopia-backup.timer (if active)..." | |
-systemctl disable --now btrfs-kopia-backup.timer 2>/dev/null || true | |
# @echo "=== [DEINSTALL] Removing systemd unit files for the backup..." | |
# rm -f /etc/systemd/system/btrfs-kopia-backup.timer | |
# rm -f /etc/systemd/system/btrfs-kopia-backup.service | |
# @echo "=== [DEINSTALL] Removing the backup script from /usr/local/bin..." | |
# rm -f /usr/local/bin/btrfs-kopia-backup.sh | |
@echo "=== [DEINSTALL] Removing Docker override (if present)..." | |
rm -f /etc/systemd/system/docker.service.d/override.conf | |
@echo "=== [DEINSTALL] Reloading systemd to reflect changes..." | |
systemctl daemon-reload | |
@echo "=== [DEINSTALL] Done. The rest of your system remains intact." | |
############################################################################### | |
# Housekeeping: Clean stamp files (won't delete your system changes!) | |
############################################################################### | |
.PHONY: clean | |
clean: | |
@echo "=== Removing local stamp files (this does NOT revert system changes!)" | |
rm -rf $(STAMP_DIR) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment