Pinchflat not able to write videos to mediaDir

Hi,

I’m trying to use pinchflat on a libvirt VM and want it to save videos to a mounted virtiofs host file system. The storage is mounted like this:

  fileSystems = {
    "/mnt/music" = {
      device = "music";
      fsType = "virtiofs";
    };
    "/mnt/video" = {
      device = "video";
      fsType = "virtiofs";
    };
  };

I configured pinchflat to use /mnt/video/pinchflatas mediaDir:

  services.pinchflat = {
    enable = true;
    selfhosted = true;
    mediaDir = "/mnt/video/pinchflat";
  };

But when adding a source, it complains about not being able to write to the mediaDir:

Jun 05 20:40:30 media pinchflat[32920]: 2025-06-05 20:40:30.211 [info] | User scripts lifecyle file either not present or is empty. Skipping.
Jun 05 20:40:30 media pinchflat[32920]: 2025-06-05 20:40:30.215 [info] | [command_wrapper]: /nix/store/byhp729sca45p54n501ac3r3p3ax4ngh-yt-dlp-2025.5.22/bin/yt-dlp called with: https://www.youtube.com/watch?v=dFVrncgIvos --simulate --skip-download --print-to-file %(.{live_status})j /var/lib/pinchflat/tmp/01/f1/01f13e071bfeb410029fed288da52426b611035d07d52b6e13804dbafc29fb99.json --windows-filenames --quiet --cache-dir /var/lib/pinchflat/tmp/yt-dlp-cache
Jun 05 20:40:34 media pinchflat[32920]: 2025-06-05 20:40:34.581 [info] | [command_wrapper]: /nix/store/byhp729sca45p54n501ac3r3p3ax4ngh-yt-dlp-2025.5.22/bin/yt-dlp called with: https://www.youtube.com/watch?v=H6q6pYZ9Fho --no-simulate --no-progress --no-force-overwrites --parse-metadata %(upload_date>%Y-%m-%d)s:(?P<meta_date>.+) --write-subs --convert-subs srt --embed-subs --sub-langs de,en --write-thumbnail --convert-thumbnail jpg --output thumbnail:/mnt/video/pinchflat/Veritasium/%(upload_date>%Y-%m-%d)S %(title)S/%(title)S [%(id)S]-thumb.%(ext)S --embed-thumbnail --convert-thumbnail jpg --write-info-json --clean-info-json --embed-metadata --remux-video mp4 --format-sort res:1080,+codec:avc:m4a --format bestvideo*+bestaudio/best --output /mnt/video/pinchflat/Veritasium/%(upload_date>%Y-%m-%d)S %(title)S/%(title)S [%(id)S].%(ext)S --print-to-file after_move:%()j /var/lib/pinchflat/tmp/7f/16/7f160923ed156834ba75bc1e4a9d8737be1ca055018228e5cf5b087363bbcf66.json --windows-filenames --quiet --cache-dir /var/lib/pinchflat/tmp/yt-dlp-cache
Jun 05 20:40:35 media pinchflat[32920]: 2025-06-05 20:40:35.043 [info] | [command_wrapper]: /nix/store/byhp729sca45p54n501ac3r3p3ax4ngh-yt-dlp-2025.5.22/bin/yt-dlp called with: https://www.youtube.com/watch?v=dFVrncgIvos --no-simulate --no-progress --no-force-overwrites --parse-metadata %(upload_date>%Y-%m-%d)s:(?P<meta_date>.+) --write-subs --convert-subs srt --embed-subs --sub-langs de,en --write-thumbnail --convert-thumbnail jpg --output thumbnail:/mnt/video/pinchflat/Veritasium/%(upload_date>%Y-%m-%d)S %(title)S/%(title)S [%(id)S]-thumb.%(ext)S --embed-thumbnail --convert-thumbnail jpg --write-info-json --clean-info-json --embed-metadata --remux-video mp4 --format-sort res:1080,+codec:avc:m4a --format bestvideo*+bestaudio/best --output /mnt/video/pinchflat/Veritasium/%(upload_date>%Y-%m-%d)S %(title)S/%(title)S [%(id)S].%(ext)S --print-to-file after_move:%()j /var/lib/pinchflat/tmp/34/34/34349d5c25050d5765957cea568dfb5dca8e22af9b0bd28094248a414082a267.json --windows-filenames --quiet --cache-dir /var/lib/pinchflat/tmp/yt-dlp-cache
Jun 05 20:40:39 media pinchflat[32920]: 2025-06-05 20:40:39.518 [error] | [command_wrapper]: /nix/store/byhp729sca45p54n501ac3r3p3ax4ngh-yt-dlp-2025.5.22/bin/yt-dlp called with: https://www.youtube.com/watch?v=H6q6pYZ9Fho --no-simulate --no-progress --no-force-overwrites --parse-metadata %(upload_date>%Y-%m-%d)s:(?P<meta_date>.+) --write-subs --convert-subs srt --embed-subs --sub-langs de,en --write-thumbnail --convert-thumbnail jpg --output thumbnail:/mnt/video/pinchflat/Veritasium/%(upload_date>%Y-%m-%d)S %(title)S/%(title)S [%(id)S]-thumb.%(ext)S --embed-thumbnail --convert-thumbnail jpg --write-info-json --clean-info-json --embed-metadata --remux-video mp4 --format-sort res:1080,+codec:avc:m4a --format bestvideo*+bestaudio/best --output /mnt/video/pinchflat/Veritasium/%(upload_date>%Y-%m-%d)S %(title)S/%(title)S [%(id)S].%(ext)S --print-to-file after_move:%()j /var/lib/pinchflat/tmp/7f/16/7f160923ed156834ba75bc1e4a9d8737be1ca055018228e5cf5b087363bbcf66.json --windows-filenames --quiet --cache-dir /var/lib/pinchflat/tmp/yt-dlp-cache exited: 1 with: ERROR: unable to create directory [Errno 30] Read-only file system: '/mnt/video/pinchflat/Veritasium/2025-01-21 Why Is MIT Making Robot Insects?'
Jun 05 20:40:39 media pinchflat[32920]: 2025-06-05 20:40:39.518 [error] | yt-dlp download error for media item #61: "ERROR: unable to create directory [Errno 30] Read-only file system: '/mnt/video/pinchflat/Veritasium/2025-01-21 Why Is MIT Making Robot Insects?'\n"
Jun 05 20:40:39 media pinchflat[32920]: 2025-06-05 20:40:39.518 [info] | {"error":"** (Oban.PerformError) Pinchflat.Downloading.MediaDownloadWorker failed with {:error, :download_failed}","args":{"id":61},"id":134,"meta":{},"state":"failure","max_attempts":20,"queue":"media_fetching","worker":"Pinchflat.Downloading.MediaDownloadWorker","source":"oban","duration":9960800,"event":"job:exception","queue_time":75609793144,"attempt":13,"tags":["media_item","media_fetching","show_in_dashboard"]}
Jun 05 20:40:39 media pinchflat[32920]: 2025-06-05 20:40:39.525 [info] | {"args":{"id":55},"id":128,"meta":{},"system_time":1749148839525535394,"max_attempts":20,"queue":"media_fetching","worker":"Pinchflat.Downloading.MediaDownloadWorker","source":"oban","event":"job:start","attempt":13,"tags":["media_item","media_fetching","show_in_dashboard"]}

I then verified that the mount is writable:

mount
...
video on /mnt/video type virtiofs (rw,relatime)
music on /mnt/music type virtiofs (rw,relatime)

The permissions should also be fine, I tried to set them as pinchflat does when using it’s default mediaDir /var/lib/pinchflat/media:

ls -ld /var/lib/pinchflat/media 
drwxr-xr-x 3 nobody nogroup 4096 Jun  4 21:26 /var/lib/pinchflat/media

ls -ld /mnt/video/pinchflat    
drwxr-xr-x 4 nobody nogroup 4 Jun  4 21:54 /mnt/video/pinchflat

But this is really not writable by the pinchflat user:

sudo -u pinchflat mkdir /mnt/video/pinchflat/Testdir
mkdir: cannot create directory ‘/mnt/video/pinchflat/Testdir’: Permission denied

But I don’t get why the pinchflat service is able to write to /var/lib/pinchflat which is a symlink to /var/lib/private/pinchflat and IMO is only writeable by the root user. I then tried chowning /mnt/video/pinchflat to pinchflat:pinchflat but get the same errors. I hope somebody can help me with this!

Thanks in advance,
Andreas

Found out that pinchflat.service uses DynamicUser=true which is something I had to google. It lead me to Dynamic Users with systemd which states:

Service that need to write to files outside of /run/<package>, /var/lib/<package>, /var/cache/<package>, /var/log/<package>, /var/tmp, /tmp, /dev/shm are generally incompatible with this scheme. This rules out daemons that upgrade the system as one example, as that involves writing to /usr.

This would make the mediaDir option quite useless. What would be the solution? Bind-mounting the storage somewhere inside /var/lib/pinchflat? Or did I do something fundamentally wrong. Maybe @charludo can help me, please?

You can override the unit to use a plain user.

Hi, thanks for pinging me!

Yeah, I think I messed up when creating the module. I’ll see to it to open a PR the next couple days to switch away from the DynamicUser.

For a workaround in the mean time, try this: nix/vms/SRV-PINCHFLAT.nix at 50e66de23f9d38a533b5021d33ffdb1f0045499a · charludo/nix · GitHub

(I initially assumed I just was a “special case” needing to assign a group / access a cifs mount, but looks like it’s just in general not working out with DynamicUser for this application)

Hi,

Great, thank you and @bme for your help! With your config, I got it running quickly! I’m very happy that you created the module, because I found pinchflat only by reading the NixOS 25.05 release notes and it’s perfect for auto-downloading TV shows for the kids :slight_smile:

Glad to hear it :smile: I’ll ping you here when the PR is open,maybe you can review / see if it’s working for you then, if that’s alright.

Yes of course I can try and review the PR, Thanks!

@nixie_tube

1 Like