Some checks failed
CI / check (push) Failing after 12m14s
Add a new system-level role for mounting WebDAV filesystems via rclone. Includes rclone-torbox-setup helper script that uses rbw to bootstrap credentials from Bitwarden. Key features: - Configurable WebDAV URL, username, mount point - VFS cache mode and buffer size tuning for media streaming - RequiresMountsFor option for ZFS pool dependencies - Obscured password storage via environment file Enable on john-endesktop for TorBox WebDAV access by rdt-client and Jellyfin. Mount waits for /media ZFS pool before starting. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
150 lines
4.8 KiB
Nix
150 lines
4.8 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.roles.rclone-mount;
|
|
|
|
# Generate systemd service for a single mount
|
|
mkMountService = name: mountCfg: {
|
|
description = "rclone mount for ${name}";
|
|
after = [ "network-online.target" ];
|
|
wants = [ "network-online.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
# Wait for parent mount points (e.g., ZFS pools) to be available
|
|
unitConfig = mkIf (mountCfg.requiresMountsFor != []) {
|
|
RequiresMountsFor = mountCfg.requiresMountsFor;
|
|
};
|
|
|
|
serviceConfig = {
|
|
Type = "notify";
|
|
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mountCfg.mountPoint}";
|
|
ExecStart = concatStringsSep " " ([
|
|
"${pkgs.rclone}/bin/rclone mount"
|
|
":webdav:${mountCfg.remotePath}"
|
|
"${mountCfg.mountPoint}"
|
|
"--webdav-url=${mountCfg.webdavUrl}"
|
|
"--webdav-vendor=${mountCfg.webdavVendor}"
|
|
"--webdav-user=${mountCfg.username}"
|
|
"--allow-other"
|
|
"--vfs-cache-mode=${mountCfg.vfsCacheMode}"
|
|
"--dir-cache-time=${mountCfg.dirCacheTime}"
|
|
"--poll-interval=${mountCfg.pollInterval}"
|
|
"--log-level=${mountCfg.logLevel}"
|
|
] ++ mountCfg.extraArgs);
|
|
ExecStop = "${pkgs.fuse}/bin/fusermount -uz ${mountCfg.mountPoint}";
|
|
Restart = "on-failure";
|
|
RestartSec = "10s";
|
|
EnvironmentFile = mountCfg.environmentFile;
|
|
};
|
|
};
|
|
in
|
|
{
|
|
options.roles.rclone-mount = {
|
|
enable = mkEnableOption "Enable rclone WebDAV mounts";
|
|
|
|
mounts = mkOption {
|
|
type = types.attrsOf (types.submodule {
|
|
options = {
|
|
webdavUrl = mkOption {
|
|
type = types.str;
|
|
description = "WebDAV server URL (e.g., https://webdav.torbox.app)";
|
|
};
|
|
|
|
webdavVendor = mkOption {
|
|
type = types.enum [ "other" "nextcloud" "owncloud" "sharepoint" "sharepoint-ntlm" "fastmail" ];
|
|
default = "other";
|
|
description = "WebDAV server vendor for optimizations";
|
|
};
|
|
|
|
username = mkOption {
|
|
type = types.str;
|
|
description = "WebDAV username (often email address)";
|
|
};
|
|
|
|
environmentFile = mkOption {
|
|
type = types.path;
|
|
description = ''
|
|
Path to environment file containing RCLONE_WEBDAV_PASS.
|
|
The password should be obscured using: rclone obscure <password>
|
|
File format: RCLONE_WEBDAV_PASS=<obscured_password>
|
|
'';
|
|
};
|
|
|
|
mountPoint = mkOption {
|
|
type = types.str;
|
|
description = "Local mount point path";
|
|
};
|
|
|
|
remotePath = mkOption {
|
|
type = types.str;
|
|
default = "/";
|
|
description = "Remote path on WebDAV server to mount";
|
|
};
|
|
|
|
vfsCacheMode = mkOption {
|
|
type = types.enum [ "off" "minimal" "writes" "full" ];
|
|
default = "full";
|
|
description = ''
|
|
VFS cache mode. For streaming media, 'full' is recommended.
|
|
- off: No caching (direct reads/writes)
|
|
- minimal: Cache open files only
|
|
- writes: Cache writes and open files
|
|
- full: Full caching of all files
|
|
'';
|
|
};
|
|
|
|
dirCacheTime = mkOption {
|
|
type = types.str;
|
|
default = "5m";
|
|
description = "Time to cache directory entries";
|
|
};
|
|
|
|
pollInterval = mkOption {
|
|
type = types.str;
|
|
default = "1m";
|
|
description = "Poll interval for remote changes";
|
|
};
|
|
|
|
logLevel = mkOption {
|
|
type = types.enum [ "DEBUG" "INFO" "NOTICE" "ERROR" ];
|
|
default = "INFO";
|
|
description = "rclone log level";
|
|
};
|
|
|
|
extraArgs = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "Extra arguments to pass to rclone mount";
|
|
};
|
|
|
|
requiresMountsFor = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = ''
|
|
List of mount points that must be available before this service starts.
|
|
Use this when the mount point's parent is on a ZFS pool or other filesystem
|
|
that may not be mounted at boot time.
|
|
Example: [ "/media" ] to wait for the media ZFS pool to mount.
|
|
'';
|
|
};
|
|
};
|
|
});
|
|
default = {};
|
|
description = "Attribute set of rclone WebDAV mounts to configure";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
# Ensure FUSE is available
|
|
environment.systemPackages = [ pkgs.rclone pkgs.fuse ];
|
|
programs.fuse.userAllowOther = true;
|
|
|
|
# Create systemd services for each mount
|
|
systemd.services = mapAttrs' (name: mountCfg:
|
|
nameValuePair "rclone-mount-${name}" (mkMountService name mountCfg)
|
|
) cfg.mounts;
|
|
};
|
|
}
|