Skip to main content

Setting up Samba shares on NixOS (with support for macOS Time Machine backups)

·6 mins

I’ve been wanting to set up proper backups for my Mac for quite some time now. Not only that, I also would like to take advantage of the storage space I have available on my home server to store and archive miscellaneous files, in a way that’s easily accessible from my Mac, properly integrated within finder but without having a copy of every file locally (as I would with Nextcloud sync, for example).

For backups, I was considering setting up borgmatic with its home-manager integration, so that it would also work on my Linux desktops.

But I later found that you can use macOS’s native backups mechanism “Time Machine” with remote Samba shares, so I looked further into that, to have a better and more nicely integrated experience. By setting up Samba on my home server, I could make a share for my time machine backups and another share for miscelleanous files, integrated within Finder.

So here’s how I did it.

Setting up the server #

Following the NixOS wiki page, here’s a basic setup for the Samba server:

  services = {
    samba = {
      enable = true;

      settings = {
        global = {
          "workgroup" = "WORKGROUP";
          "server string" = "smbnix";
          "netbios name" = "smbnix";
          "security" = "user";
        };

        "my_share" = {
          "path" = "/home/john"
          "valid users" = "john";
          "force user" = "john";
          "public" = "no";
          "writeable" = "yes";
        };
      };
    };

    samba-wsdd = {
      enable = true;
      discovery = true;
    };

    avahi = {
      enable = true;

      publish.enable = true;
      publish.userServices = true;
      nssmdns4 = true;
    };
  };
}

With this, you should now be able to access the on Samba clients at smb://<ip_address>/my_share. And the nice part is that Gnome Files, macOS Finder, and other applications can use this directly.

And here’s my own setup, where I also add some options for better macOS compatibility, and declaratively set ownership of the shares’ directories on the server system:

let
  user = "samba";
  privatePath = "/mnt/samba/private";
  tmPath = "/mnt/samba/tm_share";
in
{
  # https://wiki.nixos.org/wiki/Samba#Server_setup
  services = {
    samba = {
      enable = true;

      settings = {
        global = {
          "workgroup" = "WORKGROUP";
          "server string" = "smbnix";
          "netbios name" = "smbnix";
          "security" = "user";

          # Only available on localhost and Tailscale
          # note: localhost is the ipv6 localhost ::1
          "hosts allow" = "100.64.0.0/10 127.0.0.1 localhost";
          "hosts deny" = "0.0.0.0/0";
          "guest account" = "nobody";
          "map to guest" = "bad user";
        };

        "private" = {
          "path" = privatePath;
          "valid users" = user;
          "public" = "no";
          "writeable" = "yes";
          "force user" = user;
          "fruit:aapl" = "yes";
          "vfs objects" = "catia fruit streams_xattr";
        };

        "tm_share" = {
          "path" = tmPath;
          "valid users" = user;
          "public" = "no";
          "writeable" = "yes";
          "force user" = user;
          # Below are the most imporant for macOS compatibility
          # Change the above to suit your needs
          "fruit:aapl" = "yes";
          "fruit:time machine" = "yes";
          "vfs objects" = "catia fruit streams_xattr";
        };
      };
    };

    samba-wsdd = {
      enable = true;
      discovery = true;
    };

    avahi = {
      enable = true;

      publish.enable = true;
      publish.userServices = true;
      nssmdns4 = true;

      # https://wiki.nixos.org/wiki/Samba#Apple_Time_Machine
      extraServiceFiles = {
        timemachine = ''
          <?xml version="1.0" standalone='no'?>
          <!DOCTYPE service-group SYSTEM "avahi-service.dtd">
          <service-group>
            <name replace-wildcards="yes">%h</name>
            <service>
              <type>_smb._tcp</type>
              <port>445</port>
            </service>
              <service>
              <type>_device-info._tcp</type>
              <port>0</port>
              <txt-record>model=TimeCapsule8,119</txt-record>
            </service>
            <service>
              <type>_adisk._tcp</type>
              <!--
                change tm_share to share name, if you changed it.
              -->
              <txt-record>dk0=adVN=tm_share,adVF=0x82</txt-record>
              <txt-record>sys=waMa=0,adVF=0x100</txt-record>
            </service>
          </service-group>
        '';
      };
    };
  };

  fileSystems."${privatePath}" = {
    device = "zsafe/samba";
    fsType = "zfs";
    options = [ "zfsutil" ];
  };

  fileSystems."${tmPath}" = {
    device = "zsafe/timemachine";
    fsType = "zfs";
    options = [ "zfsutil" ];
  };

  # Set up password: https://wiki.nixos.org/wiki/Samba#User_Authentication
  users.users.${user}.isNormalUser = true;

  # Share path must be owned by the respective unix user. (e.g. ❯ chown -R samba: /samba)
  systemd.tmpfiles.rules = [
    "d ${privatePath} 0755 ${user} users"
    "d ${tmPath} 0755 ${user} users"
  ];
}

In my setup, I also make it so that only devices in my Tailscale network can access the shares. Unfortunately, I couldn’t seem to connect to Samba shares using Tailscale’s MagicDNS, so I had to fallback to using the server’s Tailscale IP instead.

I also created a separate user just for this, as I would prefer not to connect directly as root. Something that wasn’t immediately obvious is that each Samba user needs to have a corresponding unix user on the host system and also that the directories must be accessible by that user. So I also added systemd-tmpfiles rules to do that automatically.

To set the samba user’s password: sudo smbpasswd -a samba

This version also has a separate share just for Time Machine backups. To set that up, first connect through Finder > Go > Connect to Server (CMD + K) > smb://<ip_address>/tm_share and connect with the Samba user and its samba password.

Finally, this should now show up as an available backup disk in Time Machine settings. Here, I set up a more or less arbitrary quota of 1TB (which you can’t seem to easily change later) and also enabled encryption. After that, Time Machine should be set up and you can now use the fancy interface to show the contents of a directory in the past and restore files (and also just fully restore on freshly set up Mac).

Some little tricks that weren’t immediately obvious were how to pin a Samba folder to Finder’s sidebar and Time Machine complaining that it lost access to the share (because I became offline and/or suspended the computer).

To pin the folder to Finder’s sidebar, open a terminal and do open /Volumes/<share_name> after mounting it through Finder as described above. Now, on Finder > View > Show Path Bar (CMD + OPT + P) and drag the icon next to the share’s name to the Sidebar.

After becoming offline, accessing (and therefore mounting) the share didnt’ seem to be enough. I still quite haven’t figured out how to recover reliably, but manually opening the share and mounting/opening its Sparse Bundle file seems to allow Time Machine to be able to see it again.

Closing remarks #

I’m happy that after some configuration and troubleshooting, all appears to be working smoothly.

It seems that time machine backups are basically APFS snapshots, which themselves seem to be analagous to ZFS snapshots. So I’m happy that, in a way, my setup is similar to having automatic snapshots on my Mac and then sending them encrypted to some remote server, as I’ve been meaning to do on my remaining NixOS servers with a Sanoid/Syncoid setup.

References #