How to allow Transmission to write to directories other than incomplete, complete and watch?

I’m running Transmission and I need it to have access to files located in /DATA dir which is where I mount media harddrives. This works fine for the directories specified as complete/incomplete/watch in the config, but for others Transmission complains of readonly filesystem.

I found a clue here to add BindPaths to the config, but I cannot find it as a option so where do I add this?

{pkgs, ...}: {
  services.transmission = {
    enable = true;
    package = pkgs.transmission;
    openFirewall = true;
    openRPCPort = true;
    settings = {
      home = "/DATA/D1/TM";
      download-dir = "/DATA/D1/TM/complete";
      incomplete-dir = "/DATA/D1/TM/incomplete";
      watch-dir = "/DATA/D1/TM/watch";
      watch-dir-enabled = false;
      rpc-bind-address = "0.0.0.0";
      rpc-port = 9091;
      rpc-host-whitelist = "192.168.1.245";
      rpc-whitelist = "192.168.1.*";
    };
  };
}

You’ll need to start transmission with a combination of user/group (or group) that has access to that directory tree, and rw permissions on the final folders, example:

        transmission = {
          enable = true;
          user = "media-user";
          group = "media-group";

To bind paths you can use:

  fileSystems."/target/path" = {
    device = "/source/path";
    options = ["bind" "rw"];
  };

This is not sufficient. I’ve tried running the service under my user which can write, but Transmission still claims readonly. I did add the rw option to their mounts as well, just to be sure, but that made no difference either.

Then something in the way the module is configured is denying the access to those volumes…
You can experiment with systemd-run for example:

# cd /tmp && systemd-run --shell --uid=1013 --gid=1013 --property=PrivateTmp=True

Replace 1013 with your own id (or transmission user id). You can play with the properties, probably one of them is denying the access to your volume.

Transmission module enables some security settings, see:

Personally I run some services together with transmission inside of a nixos container that isn’t always enabled: nixconf/default.nix at 56d488e94760f16ba88250333eb9b89852fe3f3a · aorith/nixconf · GitHub

Could you paste the error from the transmission service unit?

A systemctl cat transmission.service would be helpful too.

I’m not familiar with systemd, is this the entire command? It doesn’t seem to produce anything; just results in Main processes terminated with: code=exited/status=217

apr 11 14:19:21 helios transmission-daemon[12609]: [2023-04-11 14:19:21.278] Error while flushing completed pieces from cache (/build/source/libtransmission/session.c:571)
apr 11 14:19:21 helios transmission-daemon[12609]: [2023-04-11 14:19:21.278] Saved "/var/lib/transmission/.config/transmission-daemon/stats.json" (/build/source/libtransmission/>
apr 11 14:25:21 helios transmission-daemon[12609]: [2023-04-11 14:25:21.278] Couldn't create "/DATA/D2/MEDIA": Read-only file system (/build/source/libtransmission/file-posix.c:>
apr 11 14:25:21 helios transmission-daemon[12609]: [2023-04-11 14:25:21.278] Couldn't create "/DATA/D2/MEDIA/": Read-only file system (/build/source/libtransmission/>
apr 11 14:25:21 helios transmission-daemon[12609]: [2023-04-11 14:25:21.278] Kizuna no Allele - 02.mkv tr_fdFileCheckout failed for "/DATA/D2/MEDIA/>
apr 11 14:25:21 helios transmission-daemon[12609]: [2023-04-11 14:25:21.278] Error while flushing completed pieces from cache (/build/source/libtransmission/session.c:571)
apr 11 14:31:21 helios transmission-daemon[12609]: [2023-04-11 14:31:21.278] Couldn't create "/DATA/D2/MEDIA": Read-only file system (/build/source/libtransmission/file-posix.c:>
apr 11 14:31:21 helios transmission-daemon[12609]: [2023-04-11 14:31:21.278] Couldn't create "/DATA/D2/MEDIA/": Read-only file system (/build/source/libtransmission/>
apr 11 14:31:21 helios transmission-daemon[12609]: [2023-04-11 14:31:21.278] Kizuna no Allele - 02.mkv tr_fdFileCheckout failed for "/DATA/D2/MEDIA/>
[Unit]
After=network.target apparmor.service
Description=Transmission BitTorrent Service
Requires=apparmor.service

[Service]
Environment="CURL_CA_BUNDLE=/nix/store/7yrdb4cb3xqy8xlqmvdn5yalzcjz98r6-nss-cacert-3.86/etc/ssl/certs/ca-bundle.crt"
Environment="LOCALE_ARCHIVE=/nix/store/lj44nmfy9i3fhqxkawx107almiqd51a6-glibc-locales-2.35-224/lib/locale/locale-archive"
Environment="PATH=/nix/store/lyicmql3ws929d7azr65h25b2hyakmb6-coreutils-9.1/bin:/nix/store/hk8w4kray4jbc8bbpaxb0dfa95jm7syj-findutils-4.9.0/bin:/nix/store/kp1chfvl8bai7rd2g80lk3>
Environment="TZDIR=/nix/store/w2swil1dw4fxmvkggcik4l0jiywwp5vp-tzdata-2022g/share/zoneinfo"



AmbientCapabilities=
BindPaths=/var/lib/transmission/.config/transmission-daemon
BindPaths=/DATA/D1/TM/complete
BindPaths=/DATA/D1/TM/incomplete
BindReadOnlyPaths=/nix/store
BindReadOnlyPaths=/etc
BindReadOnlyPaths=/run

The option is systemd.services.transmission.serviceConfig.BindPaths, here’s an example:

{
  systemd.services.transmission.serviceConfig.BindPaths = [ "/DATA" ];
}

I think that the cat to the unit was truncated, but anyway you can try what Infinisil suggested and bind /DATA/D1/TM too, not sure why it isn’t working with your config as it should be already binded, double check the permissions for all the paths used.

The systemd-run command is complete (but it only has one property enabled as an example), it should drop you into a shell with the user pid defined in --uid. It’s useful to test systemd units but maybe too cumbersome in this case.

Btw, you won’t find systemd.services.transmission. directly in nixos options but you can search for systemd.services.<name>. to see which options are available, the unit is created by services.transmission.enable = true;

Sorry, you’re correct. Full output:

[Unit]
After=network.target apparmor.service
Description=Transmission BitTorrent Service
Requires=apparmor.service

[Service]
Environment="CURL_CA_BUNDLE=/nix/store/7yrdb4cb3xqy8xlqmvdn5yalzcjz98r6-nss-cacert-3.86/etc/ssl/certs/ca-bundle.crt"
Environment="LOCALE_ARCHIVE=/nix/store/lj44nmfy9i3fhqxkawx107almiqd51a6-glibc-locales-2.35-224/lib/locale/locale-archive"
Environment="PATH=/nix/store/lyicmql3ws929d7azr65h25b2hyakmb6-coreutils-9.1/bin:/nix/store/hk8w4kray4jbc8bbpaxb0dfa95jm7syj-findutils-4.9.0/bin:/nix/store/kp1chfvl8bai7rd2g80lk3xjz4v76j1v-gnugrep-3.7/bin:/nix/store/hdq087rhq35fl5d5p6pw2m7hr2ib0f4h-gnused-4.9/bin:/nix/store/33pkg1f1ad2wh2rkl2c48hf8kfxcjgw5-systemd-253.1/bin:/nix/store/lyicmql3ws929d7azr65h25b2hyakmb6-coreutils-9.1/sbin:/nix/store/hk8w4kray4jbc8bbpaxb0dfa95jm7syj-findutils-4.9.0/sbin:/nix/store/kp1chfvl8bai7rd2g80lk3xjz4v76j1v-gnugrep-3.7/sbin:/nix/store/hdq087rhq35fl5d5p6pw2m7hr2ib0f4h-gnused-4.9/sbin:/nix/store/33pkg1f1ad2wh2rkl2c48hf8kfxcjgw5-systemd-253.1/sbin"
Environment="TZDIR=/nix/store/w2swil1dw4fxmvkggcik4l0jiywwp5vp-tzdata-2022g/share/zoneinfo"



AmbientCapabilities=
BindPaths=/var/lib/transmission/.config/transmission-daemon
BindPaths=/DATA/D1/TM/complete
BindPaths=/DATA/D1/TM/incomplete
BindReadOnlyPaths=/nix/store
BindReadOnlyPaths=/etc
BindReadOnlyPaths=/run
BindReadOnlyPaths=/DATA/D1/TM/watch
CapabilityBoundingSet=
DeviceAllow=
ExecReload=/nix/store/lyicmql3ws929d7azr65h25b2hyakmb6-coreutils-9.1/bin/kill -HUP $MAINPID
ExecStart=/nix/store/bgja8nzs5zx715chh66qh6h0v8c7m3k0-transmission-3.00/bin/transmission-daemon -f -g /var/lib/transmission/.config/transmission-daemon 
ExecStartPre=+/nix/store/grs54fkp5dh92lj5jm5zx0p8q8504idf-transmission-prestart
Group=transmission
LockPersonality=true
MemoryDenyWriteExecute=true
MountAPIVFS=true
NoNewPrivileges=true
PrivateDevices=true
PrivateMounts=true
PrivateNetwork=false
PrivateTmp=true
PrivateUsers=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=read-only
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=strict
RemoveIPC=true
RestrictAddressFamilies=AF_UNIX
RestrictAddressFamilies=AF_INET
RestrictAddressFamilies=AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
RootDirectory=/run/transmission
RootDirectoryStartOnly=true
RuntimeDirectory=transmission
RuntimeDirectoryMode=755
StateDirectory=transmission
StateDirectory=transmission/.config/transmission-daemon
StateDirectory=transmission/.incomplete
StateDirectory=transmission/Downloads
StateDirectory=transmission/watch-dir
StateDirectoryMode=750
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@aio
SystemCallFilter=~@chown
SystemCallFilter=~@keyring
SystemCallFilter=~@memlock
SystemCallFilter=~@resources
SystemCallFilter=~@setuid
SystemCallFilter=~@timer
SystemCallFilter=quotactl
UMask=0066
User=transmission

Thanks, will see if this helps.

Btw, you can see in the unit logs that its complaining about /DATA/D2/MEDIA/:

apr 11 14:25:21 helios transmission-daemon[12609]: [2023-04-11 14:25:21.278] Couldn't create "/DATA/D2/MEDIA/": Read-only file system (/build/source/libtransmission/>

But in your configuration you always reference /DATA/D1/TM. Is it loading a leftover configuration?
I’ve had trouble in the past migrating to another storage or from docker to nixos-containers, that happens, permissions… wrong paths… try to bind that directory if it’s needed

No, that was me attempting to see if placing Transmission on the same drive which it claimed was RO made a difference (it did not).

Anyways systemd.services.transmission.serviceConfig.BindPaths = [ "/DATA" ]; seems to have solved the RO problem, as TM now results in permission denied instead. I know the cat above shows it running as transmission uid, but even when I start it as my own user it results in permission denied.

ls -l shows drwxrwxrwx 3 nixuser10512 1000 4096 and I can create files there, but Transmission seemingly cannot.

Did you manage to solve it?

Something is off here:

  services.transmission = {
    enable = true;
    package = pkgs.transmission;
    openFirewall = true;
    openRPCPort = true;
    settings = {
      home = "/DATA/D1/TM";

As you can see you have “home” in services.transmission.settings.home, but if you look at the nixos options it should be outside of the actual transmission settings, so: services.transmission.home, that explains why in your unit the ExecStart has transmission-daemon -f -g /var/lib/transmission/.config/transmission-daemon (-g is an alias por --config-dir in transmission).

Maybe try to change that, then before starting anything or doing a nixos-rebuild switch ensure that all the files are owned by the user that transmission will use, or at least belong to a group that transmission will have r/w permissions.

Unfortunately no. I removed the home section entirely so it would use the default, but that made no difference. I also made an entirely new group, ran chown -R on the entire drive and made sure the user:group combo Transmission runs under owns all of it, but it still claims permission denied.

Oh :frowning:

I have tested this in a VM and it works (with an empty /DATA volume):

users.groups.media = {gid = 1100;};
users.users.media = {
  isNormalUser = true;
  group = "media";
  uid = 1100;
};

{pkgs, ...}: {
  services.transmission = {
    enable = true;
    user = "media";
    group "media";
    openFirewall = true;
    openRPCPort = true;
    home = "/DATA/D1/TM";
    settings = {
      download-dir = "/DATA/D1/TM/complete";
      incomplete-dir = "/DATA/D1/TM/incomplete";
      watch-dir = "/DATA/D1/TM/watch";
      watch-dir-enabled = false;
      rpc-bind-address = "0.0.0.0";
      rpc-port = 9091;
      rpc-host-whitelist = "192.168.1.245";
      rpc-whitelist = "192.168.1.*";
    };
  };
}

Ensure the directories and permissions:

$ mkdir -p /DATA/D1/TM/.config/transmission-daemon
$ mkdir -p /DATA/D1/TM/{complete,incomplete,watch}
$ chown media:media -R /DATA/D1/TM
$ systemctl restart transmission.service

If it’s a new user, ensure that you can cd into all the folders, and maybe try to touch a dummy file:

$ sudo su - media
$ cd /DATA/D1/TM/.config/transmission-daemon
$ touch test.txt
$ rm test.txt

You should replace the user and group “media” with your own (check the command id) if you want to test it with your current user or create one explicitly for transmission.
This is my last try, if it doesn’t work please paste the service unit logs again and maybe someone more knowledgeable than me will help :confused:

Thanks a lot for your help @aorith. I ran your example but unfortunately Transmission still claims permission denied when a path other than the default is specified. I’ll post an update here if I ever figure it out, until then, here are the latest logs should anyone else feel like taking a stab at it.

Config:

{pkgs, ...}: {

  systemd.services.transmission.serviceConfig.BindPaths = [ "/DATA" ];

users.groups.media = {
  gid = 1100;
  };
users.users.media = {
  isNormalUser = true;
  group = "media";
  uid = 1100;
};

  services.transmission = {
    enable = true;
    user = "media";
    group = "media";
    openFirewall = true;
    openRPCPort = true;
    home = "/DATA/D1/TM";
    settings = {
      download-dir = "/DATA/D1/TM/complete";
      incomplete-dir = "/DATA/D1/TM/incomplete";
      watch-dir = "/DATA/D1/TM/watch";
      watch-dir-enabled = false;
      rpc-bind-address = "0.0.0.0";
      rpc-port = 9091;
      rpc-host-whitelist = "192.168.1.245";
      rpc-whitelist = "192.168.1.*";
    };
  };
}

Log:

[Unit]
After=network.target apparmor.service
Description=Transmission BitTorrent Service
Requires=apparmor.service

[Service]
Environment="CURL_CA_BUNDLE=/nix/store/7yrdb4cb3xqy8xlqmvdn5yalzcjz98r6-nss-cacert-3.86/etc/ssl/certs/ca-bundle.crt"
Environment="LOCALE_ARCHIVE=/nix/store/lj44nmfy9i3fhqxkawx107almiqd51a6-glibc-locales-2.35-224/lib/locale/locale-archive"
Environment="PATH=/nix/store/lyicmql3ws929d7azr65h25b2hyakmb6-coreutils-9.1/bin:/nix/store/hk8w4kray4jbc8bbpaxb0dfa95jm7syj-findutils>
Environment="TZDIR=/nix/store/w2swil1dw4fxmvkggcik4l0jiywwp5vp-tzdata-2022g/share/zoneinfo"



AmbientCapabilities=
BindPaths=/DATA/D1/TM/.config/transmission-daemon
BindPaths=/DATA/D1/TM/complete
BindPaths=/DATA/D1/TM/incomplete
BindPaths=/DATA
BindReadOnlyPaths=/nix/store
BindReadOnlyPaths=/etc
BindReadOnlyPaths=/run
CapabilityBoundingSet=
DeviceAllow=
ExecReload=/nix/store/lyicmql3ws929d7azr65h25b2hyakmb6-coreutils-9.1/bin/kill -HUP $MAINPID
ExecStart=/nix/store/bgja8nzs5zx715chh66qh6h0v8c7m3k0-transmission-3.00/bin/transmission-daemon -f -g /DATA/D1/TM/.config/transmissio>
ExecStartPre=+/nix/store/jqq1wda7y7x5sfq7gz6dcsg2rx7bp5mq-transmission-prestart
Group=media
LockPersonality=true
MemoryDenyWriteExecute=true
MountAPIVFS=true
NoNewPrivileges=true
PrivateDevices=true
PrivateMounts=true
PrivateNetwork=false
PrivateTmp=true
PrivateUsers=true
ProtectClock=true
ProtectControlGroups=true
ProtectHome=read-only
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=strict
RemoveIPC=true
RestrictAddressFamilies=AF_UNIX
RestrictAddressFamilies=AF_INET
RestrictAddressFamilies=AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
RootDirectory=/run/transmission
RootDirectoryStartOnly=true
RuntimeDirectory=transmission
RuntimeDirectoryMode=755
StateDirectory=transmission
StateDirectory=transmission/.config/transmission-daemon
StateDirectory=transmission/.incomplete
StateDirectory=transmission/Downloads
StateDirectory=transmission/watch-dir
StateDirectoryMode=750
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@aio
SystemCallFilter=~@chown
SystemCallFilter=~@keyring
SystemCallFilter=~@memlock
SystemCallFilter=~@resources
SystemCallFilter=~@setuid
SystemCallFilter=~@timer
SystemCallFilter=quotactl
UMask=0066
User=media

ls -l /DATA

drwxrwxrwx 8 media media 4096 12 apr 04.53 D1
drwxrwxrwx 4 media media 4096 12 apr 11.58 D2
drwxrwxrwx 3 media media 4096 22 mar 09.17 D3
drwxrwxrwx 3 media media 4096 30 mar 03.44 D4
drwxrwxrwx 3 media media 4096 10 nov 03.27 D5
drwxrwxrwx 2 media media 4096  9 apr 11.08 D6

Systemctl status

● transmission.service - Transmission BitTorrent Service
     Loaded: loaded (/etc/systemd/system/transmission.service; enabled; preset: enabled)
     Active: active (running) since Wed 2023-04-12 12:24:02 CEST; 9min ago
    Process: 59945 ExecStartPre=/nix/store/jqq1wda7y7x5sfq7gz6dcsg2rx7bp5mq-transmission-prestart (code=exited, status=0/SUCCESS)
   Main PID: 59949 (transmission-da)
         IP: 6.6M in, 291.1K out
         IO: 4.0K read, 48.0K written
      Tasks: 4 (limit: 9418)
     Memory: 27.6M
        CPU: 1.035s
     CGroup: /system.slice/transmission.service
             └─59949 /nix/store/bgja8nzs5zx715chh66qh6h0v8c7m3k0-transmission-3.00/bin/transmission-daemon -f -g /DATA/D1/TM/.config/transmission-daemon

apr 12 12:24:59 helios transmission-daemon[59949]: [2023-04-12 12:24:59.336] [ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv tr_fdFileCheckout failed for "/DATA/D2//[ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv.part": Permission denied (/build/source/libtransmission/inout.c:95)
apr 12 12:24:59 helios transmission-daemon[59949]: [2023-04-12 12:24:59.336] [ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv Pausing (/build/source/libtransmission/torrent.c:2022)
apr 12 12:24:59 helios transmission-daemon[59949]: [2023-04-12 12:24:59.336] Couldn't open "/DATA/D2//[ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv.part": Permission denied (/build/source/libtransmission/fdlimit.c:195)
apr 12 12:24:59 helios transmission-daemon[59949]: [2023-04-12 12:24:59.336] [ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv tr_fdFileCheckout failed for "/DATA/D2//[ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv.part": Permission denied (/build/source/libtransmission/inout.c:95)
apr 12 12:24:59 helios transmission-daemon[59949]: [2023-04-12 12:24:59.336] Saved "/DATA/D1/TM/.config/transmission-daemon/resume/56137679de262d9cfb2de8f99eaf4c107f8c0445.resume" (/build/source/libtransmission/variant.c:1221)
apr 12 12:27:41 helios transmission-daemon[59949]: [2023-04-12 12:27:41.337] [ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv IPv4 DHT announce done (/build/source/libtransmission/tr-dht.c:683)
apr 12 12:30:03 helios transmission-daemon[59949]: [2023-04-12 12:30:03.336] Couldn't open "/DATA/D2//[ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv.part": Permission denied (/build/source/libtransmission/fdlimit.c:195)
apr 12 12:30:03 helios transmission-daemon[59949]: [2023-04-12 12:30:03.336] [ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv tr_fdFileCheckout failed for "/DATA/D2//[ASW] Skip to Loafer - 02 [1080p HEVC][9B3C66C4].mkv.part": Permission denied (/build/source/libtransmission/inout.c:95)
apr 12 12:30:03 helios transmission-daemon[59949]: [2023-04-12 12:30:03.336] Error while flushing completed pieces from cache (/build/source/libtransmission/session.c:571)
apr 12 12:30:03 helios transmission-daemon[59949]: [2023-04-12 12:30:03.336] Saved "/DATA/D1/TM/.config/transmission-daemon/stats.json" (/build/source/libtransmission/variant.c:1221)