First, a caveat: fan noise spiking at odd hours is often just DSM doing its own housework (scheduled tasks run by default in the middle of the night — indexing photos, deduplication, etc.). Check Control Panel → Task Scheduler before assuming malware; if the noisy jobs are legit DSM ones, that's your answer.
If you still want to verify the box is clean, since Synology DSM is basically a locked-down Linux under the hood:
-
Check what's actually listening and running. SSH in (enable it in Control Panel → Terminal) and run
sudo netstat -tulpnandps aux. Anything unfamiliar with no package owner is a red flag.synowebapi/synologyprefixed processes are normal. -
Check persistence points:
crontab -land/etc/crontab, plus~/.ssh/authorized_keysfor all users — malware loves adding a key. -
Log review: DSM's Log Center will show auth failures. A wall of failed SSH logins from random IPs just means the NAS is internet-exposed — which is a fixable problem in itself (move SSH to a non-standard port or better, keep it LAN-only and use Tailscale/WireGuard for remote access).
-
Full scan: Synology has a built-in malware scanner (Security Advisor /
synoscan), but it's fairly shallow. A stronger option is running ClamAV on a separate machine pointed at a read-only mount of the shares —clamscan -r --bell -i /mnt/nas— which catches infected files (Windows-targeting stuff mostly) even if the NAS itself isn't compromised. -
If you find something you can't explain: the safest move on a NAS is a config backup, a full DSM reinstall, and data restore — chasing a rootkit on an appliance is not worth it.
One hardening tip regardless of outcome: disable admin SSH + port-forwarding to the NAS entirely, and don't expose DSM to the internet directly (use quickconnect or a VPN tunnel instead).
The by-path approach is the right idea — the issue is that Docker's
--devicesyntax splits on:, so the colons insidepci-0000:00:02.0-cardconfuse the parser (container_path gets mangled). Two things to try:Re-check the exact by-path filenames. They must match what's actually in
/dev/dri/by-path/on your host — e.g. on Intel iGPU it's usuallypci-0000:00:02.0-cardandpci-0000:00:02.0-render, but on some boards the PCI address differs. Runls -l /dev/dri/by-path/and confirm the symlink targets exist.Drop card entirely — you likely only need the render node. Modern VAAPI/QSV transcoding in Jellyfin works fine with just the render device, no
card0needed:This is also the most robust fix for your reboot problem: the render node via by-path doesn't shuffle between reboots, so your compose file stops breaking.
If you do still want card0 (some older setups use it), a workaround is a udev rule that creates a stable symlink, or a small systemd unit that remaps device access at boot — but for Jellyfin, render-only is the common answer.