A complete guide to building an energy-efficient NAS for Jellyfin using Alpine Linux with automatic suspend/wake functionality.
This guide shows you how to repurpose old hardware into a low-power NAS that:
- Runs Alpine Linux (minimal RAM usage)
- Shares media via NFSv4 to your Jellyfin server
- Automatically suspends when idle to save power
- Wakes up instantly when Jellyfin needs access
- Supports JBOD storage with mergerfs
Perfect for reducing electricity costs while keeping your media accessible 24/7.
- Initial Setup
- Wake-on-LAN Configuration
- Storage: Mergerfs JBOD (Optional)
- NFS Server Setup
- Jellyfin Client Configuration
- Automatic Suspend on Idle
- Automatic Wake on Access
- Testing
- Troubleshooting
Download Alpine Linux Standard from alpinelinux.org.
Manual installation:
- Boot from USB/CD
- Login as
root(no password) - Run
setup-alpine - Follow prompts for keyboard, network, timezone, disk
Automated installation (advanced): Use alpine-answers to create unattended installation media.
For mergerfs and newer packages, enable the testing repository:
# Edit repositories file
vi /etc/apk/repositoriesAdd this line:
http://dl-cdn.alpinelinux.org/alpine/edge/testing
Your final /etc/apk/repositories should look like:
http://dl-cdn.alpinelinux.org/alpine/v3.23/main
http://dl-cdn.alpinelinux.org/alpine/v3.23/community
http://dl-cdn.alpinelinux.org/alpine/edge/testing
Update package index:
apk updateEnable your NAS to wake from suspend via network magic packets.
apk add ethtoolEdit /etc/network/interfaces:
vi /etc/network/interfacesAdd the post-up command:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
post-up /usr/sbin/ethtool -s eth0 wol g
Note: Consider using a static IP or DHCP reservation for reliability.
Reboot and check:
reboot
# After reboot:
ethtool eth0 | grep Wake-onExpected output:
Supports Wake-on: pumbg
Wake-on: g
The g indicates wake-on-magic-packet is enabled.
Skip this section if you're using a single drive or hardware RAID.
Mergerfs pools multiple drives into a single mount point (JBOD - Just a Bunch Of Disks) without RAID overhead, perfect for media storage where redundancy isn't critical.
# Load module now
modprobe fuse
# Make permanent
echo "fuse" >> /etc/modules
# Verify
lsmod | grep fuseapk add mergerfsImportant: You don't need to install the fuse package. Mergerfs includes its own mount helper.
Install util-linux to get the blkid command:
apk add util-linuxGet your disk UUIDs:
blkidEdit /etc/fstab:
vi /etc/fstabAdd your individual disks and mergerfs pool:
# Individual disk mounts
/dev/disk/by-uuid/YOUR-UUID-1 /mnt/disk1 auto nosuid,nodev,nofail 0 0
/dev/disk/by-uuid/YOUR-UUID-2 /mnt/disk2 auto nosuid,nodev,nofail 0 0
/dev/disk/by-uuid/YOUR-UUID-3 /mnt/disk3 auto nosuid,nodev,nofail 0 0
# Mergerfs pool
/mnt/disk* /mnt/storage mergerfs defaults,nonempty,allow_other,use_ino,cache.files=off,moveonenospc=true,category.create=mfs,dropcacheonclose=true,minfreespace=250G,fsname=mergerfs 0 0
Mount everything:
mount -adf -h /mnt/storage
ps ax | grep mergerfs# Load now
modprobe nfsd
# Make permanent
echo "nfsd" >> /etc/modules
# Verify
lsmod | grep nfsdapk add nfs-utilsCreate your NFS export directory:
mkdir -p /mnt/storage/jellyfin
chown nobody:nogroup /mnt/storage/jellyfinEdit /etc/exports:
vi /etc/exportsAdd your exports. NFSv4, unlike v3, requires a root export, and any shares must be children of that location. Just make the root /mnt/storage, and the exported share /mnt/storage/jellyfin. Any clients will reference our share as /jellyfin. The star '*' makes this insecure, any host can write/edit media but that's fine for me.
/mnt/storage *(rw,sync,fsid=0,crossmnt,no_subtree_check)
/mnt/storage/jellyfin *(rw,sync,all_squash,anonuid=65534,anongid=65534,no_subtree_check,fsid=101)
Export options explained:
rw- Read/write accesssync- Synchronous writesfsid=0- NFSv4 root exportall_squash- Map all users to anonymousanonuid/anongid=65534- Use nobody:nogroupno_subtree_check- Performance improvement
rc-update add nfs default
rc-service nfs startshowmount -e localhostExpected output:
Export list for localhost:
/mnt/storage/jellyfin *
/mnt/storage *
Check detailed export options:
exportfs -vExpected output:
/mnt/storage <world>(sync,wdelay,hide,crossmnt,no_subtree_check,fsid=0,sec=sys,rw,secure,root_squash,no_all_squash)
/mnt/storage/jellyfin
<world>(sync,wdelay,hide,no_subtree_check,fsid=101,sec=sys,rw,secure,root_squash,all_squash)
Configure your Jellyfin server (or any Alpine Linux client) to mount the NFS share.
Note: For autofs to work properly, you'll need nfs-utils installed for the mount.nfs helper. While Alpine's kernel has native NFSv4 support, autofs relies on the userspace mount helper.
apk add nfs-utils autofsautofs automatically mounts NFS shares on access and unmounts after idle timeout. This is critical for the suspend/wake system - when unmounted, no traffic goes to the NAS, allowing it to suspend.
Create the autofs configuration file /etc/autofs.conf:
[ autofs ]
master_map_name = /etc/autofs/auto.master
timeout = 30
browse_mode = yesNote: The timeout = 30 setting is for testing only - it unmounts after 30 seconds idle. Once you've verified everything works, increase this to a more practical value like timeout = 300 (5 minutes) or timeout = 3600 (1 hour). Most users watch content for extended periods before switching off.
Create /etc/autofs/auto.master:
# autofs master map - indirect mount
# Mounts under /mnt using auto.nfs map file
/mnt /etc/autofs/auto.nfs --timeout=30Create /etc/autofs/auto.nfs:
# autofs indirect map for NFS share
# Format: <key> <options> <server>:<remote-path>
# This creates /mnt/thirstynas when accessed
thirstynas -rw,vers=4.2,rsize=1048576,wsize=1048576,proto=tcp,hard,timeo=10,retrans=10 thirstynas:/jellyfinMount options explained:
hard- Never give up retrying (critical for Jellyfin)timeo=10- Initial timeout 1 second (in deciseconds)retrans=10- Retry 10 times at 1s before exponential backoff beginsrsize/wsize- Large buffer sizes for better performance
Note: When the filesystem is unmounted (after idle timeout), no traffic reaches the NAS, allowing it to suspend. On next access, outgoing traffic triggers the wake monitor.
Enable and start autofs:
rc-update add autofs default
rc-service autofs startTest that autofs mounts on demand:
# This should automatically mount the share
ls /mnt/thirstynas
# Create/delete test file
touch /mnt/thirstynas/test.txt
rm /mnt/thirstynas/test.txt
# Check mount status
mount | grep thirstynas
# Wait 30+ seconds, then check again - should be unmounted
sleep 35
mount | grep thirstynasConfigure Jellyfin: In Jellyfin's dashboard, add /mnt/thirstynas as a media library location.
Install the NFS idle suspend daemon on your NAS server.
# Transfer the installer to your NAS
# Then run:
sh nfs-idle-suspend-installer.sh
# Enable and start
rc-update add nfs-idle-suspend default
rc-service nfs-idle-suspend start- Monitors
/proc/net/rpc/nfsdfor NFS activity - Checks every 5 seconds
- Suspends after 30 seconds of inactivity
- Suspends to RAM:
echo mem > /sys/power/state
Edit /usr/local/bin/nfs-idle-suspend.sh to adjust:
CHECK_INTERVAL=5- Polling frequencyIDLE_THRESHOLD=30- Seconds before suspend
Test suspend/wake manually first:
# Suspend (fans should stop)
echo mem > /sys/power/state
# Wake by pressing keyboard key or sending WOL from another machine:
# On Alpine client:
apk add net-tools
ether-wake AA:BB:CC:DD:EE:FF
# On macOS:
brew install wakeonlan
wakeonlan AA:BB:CC:DD:EE:FFReplace AA:BB:CC:DD:EE:FF with your NAS MAC address (get it with ip link on the NAS).
Install the wakeup monitor on your Jellyfin client.
With a hard NFS mount and automatic suspend, you need a way to wake the NAS when Jellyfin tries to access it. Simply using autofs isn't enough due to edge cases where:
- ARP cache expires while NAS is asleep
- Mount exists but server is suspended
- No remount triggers = no wake-up
The solution: Monitor outgoing network traffic to the NAS and send WOL packets when activity is detected.
apk add tcpdump net-tools# Transfer the installer to your Jellyfin server
sh nas-wake-installer.shEdit /usr/local/bin/nas-wake-monitor.sh:
vi /usr/local/bin/nas-wake-monitor.shSet these values:
NAS_HOST="thirstynas" # Your NAS hostname or IP
NAS_MAC="AA:BB:CC:DD:EE:FF" # NAS MAC address
INTERFACE="eth0" # Network interface to monitor
ACTIVITY_FILE="/tmp/nas-activity" # Timestamp file for NAS heartbeat
ACTIVITY_THRESHOLD=2 # Seconds - skip wake if NAS responded recentlyNote: LOCAL_MAC is auto-detected from the network interface. No manual configuration needed.
Get NAS MAC address:
# On NAS:
ip link show eth0rc-update add nas-wake-monitor default
rc-service nas-wake-monitor startThe monitor uses a two-process architecture for intelligent wake-on-LAN:
Background Process (Incoming Traffic Monitor):
- Continuously monitors NAS→client traffic using tcpdump
- Samples incoming packets every 0.1 seconds
- Updates
/tmp/nas-activitytimestamp file when packets detected - Runs as background job managed by main process
Main Process (Outgoing Traffic Monitor):
- Watches for client→NAS traffic (ARP requests, NFS calls, etc.)
- Checks activity file timestamp before sending WOL
- Sends WOL only if: NAS hasn't responded in 2+ seconds (configurable)
- Skips WOL if: Recent incoming traffic indicates NAS is awake
Benefits:
- No unnecessary WOL packets when NAS is actively serving files
- No port polling or connection attempts (lower network/NAS load)
- Instant detection of NAS activity via passive monitoring
- Eliminates mount/unmount state issues
During active NFS streaming, the activity file stays fresh (milliseconds old), preventing unnecessary wake attempts. When the NAS suspends, incoming traffic stops, and the next outgoing request triggers a WOL.
-
Verify NAS suspends:
# On NAS, watch logs: tail -f /var/log/messages | grep nfs-idle-suspend # Wait 30 seconds with no activity # NAS should suspend (fans stop)
-
Test automatic wake:
# On Jellyfin client: ls /mnt/thirstynas # Should see wakeup logs: tail -f /var/log/messages | grep nas-wake # NAS should wake and respond within ~10-20 seconds
-
Test Jellyfin playback:
- Start playing media in Jellyfin
- NAS should wake and stay awake during playback
- After playback ends, NAS should suspend after 30 seconds
On NAS:
rc-service nfs status
rc-service nfs-idle-suspend statusOn Jellyfin client:
rc-service nas-wake-monitor status
mount | grep nfsCheck if service is running:
rc-service nfs-idle-suspend status
tail /var/log/messages | grep nfs-idle-suspendVerify no active connections:
netstat -tn | grep :2049Test manual suspend:
echo mem > /sys/power/stateVerify WOL is enabled:
ethtool eth0 | grep Wake-onTest WOL manually from client:
ether-wake AA:BB:CC:DD:EE:FFCheck wakeup monitor logs:
tail -f /var/log/messages | grep nas-wake-monitorVerify packet filter is working:
tcpdump -i eth0 -c 5 "dst host thirstynas"
# Try accessing NFS share from another terminalHard mount behavior: With hard mounts, the client will freeze indefinitely waiting for the server. This is intentional - Jellyfin cannot function without the NAS.
Check wakeup monitor is running:
rc-service nas-wake-monitor statusForce unmount if needed:
umount -f /mnt/thirstynasIf your NAS IP changes after suspend, use a static IP or DHCP reservation:
Static IP configuration in /etc/network/interfaces:
auto eth0
iface eth0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1
post-up /usr/sbin/ethtool -s eth0 wol g
Or configure DHCP reservation on your router based on the NAS MAC address.
Check FUSE module:
lsmod | grep fuseCheck mount helper:
ls -l /usr/sbin/mount.mergerfsVerify mounts:
df -h
mount | grep mergerfsTypical power consumption:
- Idle (awake): 40-60W
- Suspended: 1-2W (R5 AM4 system, 5 HDDs)
- Potential savings: (~£100/year)
Suspend effectiveness depends on your usage pattern. Best results with occasional media access rather than continuous streaming.
makeGenerates:
nfs-idle-suspend-installer.sh(for NAS)nas-wake-installer.sh(for Jellyfin client)
MIT License - See LICENSE file
Thanks to the Jellyfin community for this amazing bit of software.
- Maintenance: Power off Jellyfin when doing NAS maintenance to avoid issues with scheduled tasks
- Dedicated use: This setup assumes the NAS is dedicated to Jellyfin. For multi-purpose NAS, consider different suspend strategies
- Network segment: Both machines should be on the same subnet for optimal WOL reliability
- Alpine simplicity: No systemd, no heavy Python daemons - just efficient shell scripts using ~1-2MB RAM
Enjoy your low-power Jellyfin NAS!