aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2025-12-15 19:12:50 -0800
committerFranck Cuny <franck@fcuny.net>2025-12-15 19:12:50 -0800
commit6e991d98de3b7e975a5c3f4d4c9e5823f408900f (patch)
tree8e5c6c7dbf3cf4205bc4e212668bb2116dfd2e11
parentrsync some medias to the NAS (diff)
downloadinfra-6e991d98de3b7e975a5c3f4d4c9e5823f408900f.tar.gz
backup my mac with restic
-rw-r--r--home/profiles/restic.nix197
-rw-r--r--machines/mba-m2.nix1
2 files changed, 198 insertions, 0 deletions
diff --git a/home/profiles/restic.nix b/home/profiles/restic.nix
new file mode 100644
index 0000000..96ae8d4
--- /dev/null
+++ b/home/profiles/restic.nix
@@ -0,0 +1,197 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ nasHost = "nas";
+ repoPath = "/backups/workstation";
+
+ resticRepository = "sftp:${nasHost}:${repoPath}";
+
+ backupPaths = [
+ "${config.home.homeDirectory}/Documents"
+ "${config.home.homeDirectory}/Pictures"
+ ];
+
+ excludeFile = "${config.home.homeDirectory}/.config/restic/exclude";
+ includeFile = "${config.home.homeDirectory}/.config/restic/includes";
+in
+{
+ home.packages = with pkgs; [ restic ];
+
+ age.secrets.restic-password = {
+ file = ../../secrets/restic-pw.age;
+ path = "${config.home.homeDirectory}/.config/restic/password";
+ mode = "400";
+ };
+
+ home.sessionVariables = {
+ RESTIC_REPOSITORY = resticRepository;
+ RESTIC_PASSWORD_FILE = config.age.secrets.restic-password.path;
+ };
+
+ home.file.".config/restic/includes" = {
+ text = lib.concatStringsSep "\n" backupPaths;
+ onChange = ''
+ echo "Restic backup paths updated"
+ '';
+ };
+
+ home.file.".config/restic/exclude" = {
+ text = ''
+ # macOS specific
+ .DS_Store
+ .Trash
+ .Spotlight-V100
+ .fseventsd
+ .TemporaryItems
+ .DocumentRevisions-V100
+ .VolumeIcon.icns
+ .AppleDouble
+ .LSOverride
+ Library/Caches
+ Library/Logs
+
+ # Development artifacts
+ **/node_modules
+ **/.venv
+ **/__pycache__
+ **/*.pyc
+ **/venv
+ **/target # Rust
+ **/dist
+ **/build
+ **/.tox
+ **/.pytest_cache
+ **/.coverage
+ **/.mypy_cache
+
+ # Large files that might not need backup
+ *.dmg
+ *.iso
+ *.pkg
+
+ # Version control
+ **/.git/objects
+ **/.git/lfs
+
+ # IDE
+ **/.idea
+ **/.vscode
+ *.swp
+ *~
+ '';
+ };
+
+ home.file.".local/bin/restic-now" = {
+ executable = true;
+ text = ''
+ #!/usr/bin/env bash
+ set -euo pipefail
+
+ # Colors for output
+ RED='\033[0;31m'
+ GREEN='\033[0;32m'
+ YELLOW='\033[1;33m'
+ NC='\033[0m' # No Color
+
+ echo -e "''${GREEN}Starting restic backup...''${NC}"
+
+ # Check if repository exists, initialize if not
+ echo -e "''${YELLOW}Checking repository...''${NC}"
+ if ! ${pkgs.restic}/bin/restic cat config > /dev/null 2>&1; then
+ echo -e "''${YELLOW}Repository not found. Initializing...''${NC}"
+ ${pkgs.restic}/bin/restic init
+ fi
+
+ # Run backup
+ echo -e "''${GREEN}Running backup...''${NC}"
+ ${pkgs.restic}/bin/restic backup \
+ --compression max \
+ --files-from="${includeFile}" \
+ --exclude-file="${excludeFile}" \
+ --verbose=1 \
+ --host="$(hostname -s)"
+
+ # Unlock in case of stale locks
+ echo -e "''${YELLOW}Checking for stale locks...''${NC}"
+ ${pkgs.restic}/bin/restic unlock || true
+
+ # Prune old snapshots
+ echo -e "''${GREEN}Pruning old snapshots...''${NC}"
+ ${pkgs.restic}/bin/restic forget \
+ --prune \
+ --keep-daily=7 \
+ --keep-weekly=4 \
+ --keep-monthly=12 \
+ --compression max \
+ --verbose=1
+
+ # Check repository integrity (optional, can be slow)
+ echo -e "''${GREEN}Checking repository integrity...''${NC}"
+ ${pkgs.restic}/bin/restic check --read-data-subset=5%
+
+ echo -e "''${GREEN}Backup completed successfully!''${NC}"
+ '';
+ };
+
+ home.file.".local/bin/restic-status" = {
+ executable = true;
+ text = ''
+ #!/usr/bin/env bash
+ set -euo pipefail
+
+ echo "Repository: $RESTIC_REPOSITORY"
+ echo ""
+ echo "=== Latest snapshots ==="
+ ${pkgs.restic}/bin/restic snapshots --latest 5 --compact
+ echo ""
+ echo "=== Repository stats ==="
+ ${pkgs.restic}/bin/restic stats
+ '';
+ };
+
+ home.file.".local/bin/restic-mount" = {
+ executable = true;
+ text = ''
+ #!/usr/bin/env bash
+ set -euo pipefail
+
+ MOUNT_POINT="''${1:-$HOME/mnt/restic}"
+
+ if [ ! -d "$MOUNT_POINT" ]; then
+ echo "Creating mount point: $MOUNT_POINT"
+ mkdir -p "$MOUNT_POINT"
+ fi
+
+ echo "Mounting restic repository at $MOUNT_POINT"
+ echo "Press Ctrl+C to unmount"
+ ${pkgs.restic}/bin/restic mount "$MOUNT_POINT"
+ '';
+ };
+
+ home.file.".local/bin/restic-restore" = {
+ executable = true;
+ text = ''
+ #!/usr/bin/env bash
+ set -euo pipefail
+
+ if [ $# -lt 1 ]; then
+ echo "Usage: $0 <snapshot-id> [target-directory]"
+ echo ""
+ echo "Available snapshots:"
+ ${pkgs.restic}/bin/restic snapshots --compact
+ exit 1
+ fi
+
+ SNAPSHOT="$1"
+ TARGET="''${2:-$HOME/restic-restore}"
+
+ echo "Restoring snapshot $SNAPSHOT to $TARGET"
+ ${pkgs.restic}/bin/restic restore "$SNAPSHOT" --target "$TARGET"
+ '';
+ };
+}
diff --git a/machines/mba-m2.nix b/machines/mba-m2.nix
index 398bf6d..e9fd500 100644
--- a/machines/mba-m2.nix
+++ b/machines/mba-m2.nix
@@ -43,6 +43,7 @@
../home/profiles/mac.nix
../home/profiles/media.nix
../home/profiles/security.nix
+ ../home/profiles/restic.nix
];
userinfo = {
email = "franck@fcuny.net";