Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: programs.borgmatic should support Darwin #5757

Open
2 tasks done
kamushadenes opened this issue Aug 19, 2024 · 6 comments
Open
2 tasks done

bug: programs.borgmatic should support Darwin #5757

kamushadenes opened this issue Aug 19, 2024 · 6 comments
Assignees
Labels
bug triage Issues or feature request that have not been triaged yet

Comments

@kamushadenes
Copy link

kamushadenes commented Aug 19, 2024

Are you following the right branch?

  • My Nixpkgs and Home Manager versions are in sync

Is there an existing issue for this?

  • I have searched the existing issues

Issue description

While services.borgmatic doesn't work in Darwin, borgmatic itself does, and thus programs.borgmatic should not have

lib.platforms.linux)

I tested locally and it works flawlessly, and I can use https://github.com/LnL7/nix-darwin to do the service part with launchd.

Maintainer CC

No response

System information

 - system: `"aarch64-darwin"`
 - host os: `Darwin 23.6.0, macOS 14.6.1`
 - multi-user?: `yes`
 - sandbox: `no`
 - version: `nix-env (Nix) 2.18.4`
 - channels(root): `"nixpkgs"`
 - nixpkgs: `/nix/var/nix/profiles/per-user/root/channels/nixpkgs`
@kamushadenes kamushadenes added bug triage Issues or feature request that have not been triaged yet labels Aug 19, 2024
@kamushadenes
Copy link
Author

kamushadenes commented Aug 19, 2024

If anyone is wondering how I did the nix-darwin thing:

{ config, pkgs, ... }:
let
  borgmaticRunScript = pkgs.writeScriptBin "borgmatic-run" ''
    #!/bin/bash

    LOCKFILE="/tmp/borgmatic.lock"

    # Check if lock file exists and if the process is still running
    if [ -f "$LOCKFILE" ]; then
      PID=$(cat "$LOCKFILE")
      if ps -p $PID > /dev/null 2>&1; then
        echo "A previous instance of borgmatic is still running."
        exit 1
      else
        echo "Stale lock file found. Removing it."
        rm -f "$LOCKFILE"
      fi
    fi

    # Create a lock file with the current PID
    echo $$ > "$LOCKFILE"

    # Run borgmatic
    ${pkgs.borgmatic}/bin/borgmatic -v 1 $@

    # Remove the lock file after the process completes
    rm -f "$LOCKFILE"
  '';
in
{
  environment.systemPackages = with pkgs; [ borgmaticRunScript ];

  environment.launchDaemons."org.torsion.borgmatic.plist" = {
    enable = true;
    text = ''
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
        <dict>
          <key>Label</key>
          <string>org.torsion.borgmatic</string>

          <key>ProgramArguments</key>
          <array>
                  <string>${borgmaticRunScript}/bin/borgmatic-run</string>
          </array>

          <key>StandardOutPath</key>
          <string>/tmp/borgmatic.log</string>

          <key>StandardErrorPath</key>
          <string>/tmp/borgmatic.err</string>

          <key>StartInterval</key>
          <integer>3600</integer>

          <key>UserName</key>
          <string>${config.users.users.kamushadenes.name}</string>

          <key>RunAtLoad</key>
          <true/>
        </dict>
      </plist>
    '';
  };
}

@kamushadenes
Copy link
Author

kamushadenes commented Aug 19, 2024

And my home-manager config (using https://github.com/ryantm/agenix) that also works in Linux with no changes :

{
  config,
  pkgs,
  lib,
  machine,
  ...
}:
let
  borgmaticage = "${config.xdg.configHome}/borgmatic.d/borgmatic.age";

  mkExclusionList =
    path:
    let
      content = builtins.readFile path;
      lines = builtins.split "\n" content;
      nonEmptyLines = lib.filter (
        line: (builtins.isString (line) && line != "" && !lib.strings.hasPrefix "#" line)
      ) lines;
    in
    nonEmptyLines;

  commonExclusions = lib.concatMap (path: mkExclusionList path) [
    ./resources/borgmatic/exclusions/common.lst
  ];

  macOsExclusions = lib.optionals pkgs.stdenv.isDarwin (
    lib.concatMap (path: mkExclusionList path) [
      ./resources/borgmatic/exclusions/macos/core.lst
      ./resources/borgmatic/exclusions/macos/applications.lst
      ./resources/borgmatic/exclusions/macos/programming.lst
    ]
  );

  mkBackup =
    machine: name: healthCheckId: patterns: repos: extraConfigOverrides: consistencyOverrides: retentionOverrides: {
      location = {
        patterns = patterns ++ commonExclusions ++ macOsExclusions;

        repositories = repos;
        excludeHomeManagerSymlinks = true;

        extraConfig = lib.mkMerge [
          {
            compression = "auto,zstd";
            archive_name_format = "{hostname}-{now:%Y-%m-%d-%H%M%S}";
            ssh_command = "ssh -o StrictHostKeyChecking=accept-new -i ${config.age.secrets."borg.age".path}";
            exclude_if_present = [ ".nobackup" ];
            healthchecks = {
              ping_url = "https://hc-ping.com/${healthCheckId}";
            };
          }
          extraConfigOverrides
        ];
      };

      consistency = lib.mkMerge [
        { checks = [ ]; }
        consistencyOverrides
      ];

      retention = lib.mkMerge [
        {
          keepWeekly = 4;
          keepMonthly = 12;
        }
        retentionOverrides
      ];

      storage = {
        encryptionPasscommand = "${pkgs.age}/bin/age --decrypt -i ${config.home.homeDirectory}/.age/age.pem -o - ${borgmaticage}";
      };
    };
in
{
  home.packages = with pkgs; [ age ];

  age.secrets."borg.age" = {
    file = ./resources/ssh/keys/borg.age;
    path = "${config.home.homeDirectory}/.ssh/keys/borg";
  };

  home.file."borgmatic.age" = {
    source = ./resources/borgmatic/borgmatic.age;
    target = borgmaticage;
  };

  services = lib.mkMerge [
    (lib.mkIf pkgs.stdenv.isLinux {
      borgmatic = {
        enable = true;
        frequency = "hourly";
      };
    })
  ];

  programs.borgmatic = {
    enable = true;
    package = pkgs.borgmatic;

    backups = lib.mkMerge [
      (lib.mkIf (machine == "Kamus-Mac-Studio") {
        home = (
          mkBackup machine "home" "XXXXXXXXX-2e51-4db4-8eaf-XXXXXXXXX" [
            "R ${config.home.homeDirectory}"
            "- ${config.home.homeDirectory}/Applications"
            "- ${config.home.homeDirectory}/.config/nix"
            "- ${config.home.homeDirectory}/Dropbox"
          ] [ "ssh://XXXXXXXXX@ XXXXXXXXX.repo.borgbase.com/./repo" ] { } { } { }
        );

        dropbox = (
          mkBackup machine "dropbox" "XXXXXXXXX-8302-442e-a3b9-XXXXXXXXX" [ "R /Volumes/Dropbox" ] [
            "ssh://XXXXXXXXX@ XXXXXXXXX.repo.borgbase.com/./repo"
          ] { } { } { }
        );
      })
    ];
  };
}

Exclusion lists are based on https://github.com/SterlingHooten/borg-backup-exclusions-macos.

@szethh
Copy link

szethh commented Sep 4, 2024

Hi @kamushadenes, I'm also trying to set up borg/borgmatic on darwin. I'm not too sure I understand how you managed to get it to work on macos. Like, how did you get past the "The module programs.borgmatic does not support your platform" error message?

Also, I tried your launchD script but compiling borgbackup fails due to some tests not passing. Did you have to configure anything else to get it to work on macos?

@kamushadenes
Copy link
Author

Hi @szethh , I just replaced the assertion on my fork kamushadenes@7e41084#diff-f14ed0b5cfaa1a90e13d7639d071709108038591ff076e42d2241f9a6de03e96R277.

As for the test failing, that's weird, I didn't face any such issue. I'm running on stable though, maybe that's related?

@szethh
Copy link

szethh commented Sep 5, 2024

hi @kamushadenes thanks, I thought about forking but was hoping for a solution that didn't involve keeping a fork of the project ahahah.

yeah, I'm running unstable so that might be it. will try on 24.05, hopefully it works then

edit: wait no actually i'm running 24.05 on darwin... not sure why compiling borg fails on my machine.

@szethh
Copy link

szethh commented Sep 6, 2024

for anyone stumbling into this, I had to create an overlay and disable the offending tests, now it works :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug triage Issues or feature request that have not been triaged yet
Projects
None yet
Development

No branches or pull requests

5 participants