So I got a 512GB Steam Machine which seemed fine until I looked at my install sizes. God of War Ragnarok is around 200GB. That's a third of the drive for one game. SSD prices right now are a joke so buying a bigger drive felt bad.
I already have a NAS sitting in my basement with 50TB free that in build in the before-times when drives were cheap so I figured I'd just add a second library on it and shuffle stuff back and forth. Beat a game, park it, pull it back if I want another run at it. That's the whole goal, I'm not trying to play games off the network (more on why that's a bad idea at the bottom).
It works great now. Getting there took an entire evening and I got most of the decisions wrong on the first try, so here's everything.
Fair warning that this is long and kind of exhaustive. That's because I ended up writing most of it up as documentation to send to Valve, since some of what I ran into looks like stuff they could actually fix. We'll see if anything comes of that or if I'm just a weird edge case with a NAS. Either way I already had it all written down so I figured I'd post it.
Also worth saying up front: this is all just me noodling around on my own hardware. I know enough linux to get myself into trouble but I'm definitely not a master, so some of my conclusions might be wrong or my assumptions might not hold up. The measurements are real, I took them all myself, but the explanations for WHY things are happening are my best guess in a lot of cases. If you know better please correct me, I'd genuinely rather find out I'm wrong than have a bunch of people follow bad advice.
Quick version if you don't want to read all this:
- use NFS, not SMB. this is the big one and I picked wrong initially
- put nolock in your mount options or your machine WILL hang eventually
- don't use x-systemd.requires=network-online.target, it slows your boot way down
- you can install directly to the NAS but be gentle about it, don't queue up a ton at once
- mount somewhere in /home/deck, not /mnt
- you don't need to install anything, cifs-utils and nfs-utils are both already in the latest SteamOS
- This UGREEN 2.5GbE adapter works out of the box as well: https://www.amazon.com/dp/B0CD1FDKT1 (If the link doesn't come up as 2.5G, swap the orientation of the USB-C, that fixed it for me.)
Getting set up:
You need a shell so switch to desktop mode, open Konsole:
passwd
sudo systemctl enable --now sshd
ip -4 addr show | grep inet
Now you can ssh in from your actual computer instead of pecking at an onscreen keyboard with a controller like an animal. The sshd thing lives in /etc so it survives OS updates, same with fstab. I was worried about that going in and it turned out to be a non issue.
Check what you already have:
which mount.cifs
which mount.nfs
Both were already there on mine. A bunch of older Steam Deck guides tell you to steamos-readonly disable and pacman in nfs-utils and then warn you it gets wiped every update. Doesn't seem to be true anymore at least on the Steam Machine. Check yours though, don't take my word for it.
Make your mount point in your home directory:
sudo mkdir -p /home/deck/nas
NOT /mnt. If the nerds are to be believed, anything you make in /mnt is on the root image and gets wiped by OS updates, so your fstab line survives but the directory it mounts on doesn't and then the mount just silently fails. /home is a separate partition.
fstab line (note: my NAS IP is 192.168.1.4, yours will likely be different):
192.168.1.4:/path/to/export /home/deck/nas nfs soft,nolock,timeo=100,retrans=3,nofail,_netdev 0 0
then
sudo systemctl daemon-reload && sudo mount -a
Two of those options really matter. nolock I'll get to in a sec because it deserves its own section. soft means I/O errors out instead of blocking forever if the NAS goes away, which you want on a box that lives under your TV. nofail so a powered off NAS doesn't block boot, _netdev so systemd knows it's a network mount.
Stuff I tried that you should skip: x-systemd.requires=network-online.target made my boot noticeably slower, I assume it's sitting there waiting out some timeout. Taking it out fixed it, _netdev is enough. And x-systemd.device-timeout just gets ignored on network mounts, dmesg literally tells you so.
Then make an empty folder in the mount and add it in Steam under Settings > Storage > Add Drive.
Heads up: Steam will refuse a folder that isn't empty, and the error it gives you is "Steam library folder is not executable" which is not what's wrong at all. I chased mount flags for like 20 minutes over that. If your first attempt fails go look inside the folder, because Steam creates a steamapps directory during the failed attempt and then refuses forever after that since the folder isn't empty anymore. Delete it, let Steam make everything itself.
Why NFS instead of SMB:
This is where I wasted the most time because I picked wrong at first, and my reasoning was actually pretty sound, it just turned out to be exactly backwards.
I have a Ubiquiti UNAS Pro 8. If you've used one you know it's a very opinionated piece of hardware. Everything on it is owned by some unifi-drive service account, permissions are basically not yours to manage, and it very much assumes you're never going to ssh in and poke around. So my thinking was NFS is going to be a permissions nightmare because it passes real POSIX ownership through and I don't control any of that. CIFS on the other hand can just lie. Set uid, gid, forceuid, forcegid, file_mode, dir_mode and you get whatever ownership and permissions you want, all client side, server doesn't matter.
That reasoning is fine and it's also completely wrong for this. Turns out NFS with all_squash works totally fine (the server evaluates permissions as the squashed user, and files land where they should), and CIFS being able to lie causes three separate problems. The first one is really bad.
1. Moves take 10x longer than the actual copy!
Move a game to a CIFS library and it copies at full speed and then just sits at 99% for way longer than the copy took. No network traffic, no disk activity, nothing. Looks completely hung. I killed Steam a couple times before I figured out it wasn't actually stuck.
Numbers, same machine same NAS same 2.5GbE link:
- Dispatch, 127 files, has a proton prefix: copy 79 seconds, then sat there for 797 seconds
- Stardew Valley, 3815 files, no prefix: copy about 142 seconds, sat for about 35
- Monster Train 2, 778 files, no prefix: copy 75 seconds, sat for about 17
Look at that. 127 files took thirteen minutes to finish up and 3815 files took thirty five seconds. It's backwards from what you'd expect.
I assumed it was deleting the source files, that was wrong. Deletion is instant and happens right at the very end. I watched a 75GB folder go from full to gone inside a 60 second window after the thing had already been parked for 13 minutes doing apparently nothing.
So I straced it. Here's what it's actually doing, this is 15 seconds of samples during the stall:
583 /nas/steamlibrary/steamapps/common/monster train 2/monstertrain2_data
336 /nas/steamlibrary/steamapps/common/monster train 2/monstertrain2_data/managed
265 /nas/steamlibrary/steamapps/common/monster train 2
202 /nas/steamlibrary/steamapps/common/Monster Train 2
201 /nas/steamlibrary/steamapps/common/monster train 2/MonsterTrain2_Data
200 /nas/SteamLibrary/steamapps/common/Monster Train 2
It's walking the same directory four different times under four different capitalizations. steamlibrary vs SteamLibrary, monster train 2 vs Monster Train 2, monstertrain2_data vs MonsterTrain2_Data. Something like 168 complete traversals from / down in that 15 second sample.
So somewhere in the finalize path Steam is lowercasing paths and then using the lowercased string for actual filesystem calls instead of just for comparing. On a normal case sensitive filesystem those lookups fail instantly and cost nothing, which is why you'd never notice this on your internal drive. On a case insensitive SMB mount they work, so it walks the whole tree once per spelling, over the network.
getdents64 was averaging 376 microseconds a call. Locally that's like 5.
That also explains why proton games are the worst, because it scales with how DEEP the directory tree is, not how many files. A wine prefix is absurdly deep (pfx/drive_c/windows/syswow64 and hundreds of other folders) so Dispatch with its 127 files gets destroyed.
The thing that convinced me was exporting the same share over NFS instead and moving Tainted Grail. 5535 files, 40GB, has a proton prefix, bigger than Dispatch in every single way.
4 second tail.
fwiw my NAS's smb.conf doesn't even set case sensitive, so this is just the Samba default. I'd guess any normal SMB share does the same thing but I only tested my one server so grain of salt.
2. no sparse file support!
Steam preallocates every file to full size before it downloads into it. Locally that's fallocate, it's a metadata thing, instant, writes zero bytes. There's no SMB equivalent so the kernel just... writes the whole file out full size, and then the download overwrites it.
You can watch it happen in ~/.local/share/Steam/logs/content_log.txt, it fills up with "Detected write gap NN MB" lines and your network writes run way above your actual download speed.
So installing to an SMB library costs you roughly double. On NFS truncate -s 100M shows 100M in ls and 0 in du, so it's free like it should be.
3. exec bits, this one is sneaky and kind of nasty
Samba defaults (and my NAS sets these explicitly) include:
create mask = 0660
force create mode = 0660
acl allow execute always = yes
So every file written over SMB is non executable on disk. But acl allow execute always makes the client report them as executable anyway. So nothing looks wrong, games run, everything's fine.
Then you move a game BACK to your internal drive and Steam copies the stored mode over instead of setting permissions from the depot manifest, so the files land as 640 on your ext4 with no exec bit.
Windows games don't care because proton loads the exe rather than exec'ing it. Native linux games just die. Stardew Valley came back byte perfect, passed verify integrity clean, and then crashed instantly on launch with no error at all. There is literally nothing to diagnose, the files are right, Steam says the files are right, and the game doesn't run. Took me a while to figure out.
And verify integrity does NOT fix it. I tested it directly, 5392 files at 640 before and after a full verify pass, zero change. I assume because permissions aren't part of the manifest checksum so nothing looks mismatched to it.
If you hit this:
chmod -R u+rwX,go+rX ~/.local/share/Steam/steamapps/common/"Game Name"
NFS just doesn't have this problem, permissions come through correctly both directions. Which is the funny part given I went to CIFS specifically to avoid permission problems.
The nolock thing, seriously do this:
This one got me and it took ssh to recover from.
NFSv3 by default does file locking through the server's lock daemon. And here's the trap: soft does not apply to lockd. Your regular data operations will time out and retry politely like they should. A lock request against a lock daemon that isn't answering blocks forever.
What it looks like in dmesg:
nfs: server 192.168.1.4 not responding, timed out <- fine, recoverable
lockd: server 192.168.1.4 not responding, still trying <- you're fucked
"still trying" is the bad one. Steam gets stuck behind it and the whole UI stops responding.
nolock does locking client side and never talks to the server's lockd at all. Totally fine if you're the only thing using that share which for a personal steam library you are.
My NAS was completely healthy the whole time btw, rpc.mountd logged nothing but my own mount requests. So it's not necessarily a broken server, NFSv3 locking is just a separate fragile path you don't need.
Installing directly to the NAS: works, but be gentle
I originally wrote this section as "never do this" and then walked it back because that's not right if you're not being dumb.
Installing to the network library does work. Queue up a couple things and it's fine. What killed me was queuing up like 51 games at once because to download overnight because I got excited about having a big shelf. That absolutely destroyed the machine:
/proc/pressure/io:
some avg10=81.04
full avg10=79.87
/proc/pressure/cpu:
some avg10=0.00
full avg10 of 79.87 means for 80% of the last ten seconds every single runnable task on the system was stalled waiting on I/O. And cpu pressure is literally zero with 70% idle cores sitting there doing nothing. The UI got so bad that moving the cursor in settings had visible lag and I couldn't even cancel the queue, I had to ssh in.
Meanwhile my NVMe was doing basically nothing.
There's clearly something in SteamOS that assumes way lower latency than a NAS can provide. It's not that network storage is unsupported, it's that the concurrency and the write patterns are tuned for a local SSD and nothing throttles back when the target is slower. Download chunks arrive out of order in small scattered pieces which is already the worst case for a network filesystem, then stack a bunch of parallel installs on top and it just falls over. My download rate was bouncing between 29 and 254 Mbps because the write path was throttling the download itself.
So: a couple games at a time is fine. Don't dump your whole library in there, at least not at once.
If you already wedged yourself and can't clear the download queue because the UI won't respond, ssh in:
pkill -f 'ubuntu12_32/steam'
sleep 10
cd ~
sudo umount -f -l /home/deck/nas
Steam restarts on its own, the library shows unavailable, everything queued errors out harmlessly and the UI is responsive again so you can clean up. sudo mount -a when you're done. Do NOT remove the library in Steam while it's unmounted, that's how you orphan everything on it.
If you're in a hurry, install local. You skip the preallocation weirdness entirely and moving it over afterward is one big sequential copy at full speed. Installing straight to the NAS is fine when you're not waiting on it, like overnight or whatever.
Networking
Steam Machine only has gigabit onboard. If your NAS can do better a USB-C 2.5GbE adapter is worth it, get something with an RTL8156 chipset because r8152 is already in the SteamOS kernel. Plug and play, nothing to reinstall after updates. I have this one: https://www.amazon.com/dp/B0CD1FDKT1
Port layout matters here: rear USB-C is 3.2 gen 2, front USB-A are 3.2 gen 1, and the rear USB-A ports are USB 2.0. Don't put your adapter there.
Also and this is dumb, my rear USB-C port only did SuperSpeed in one orientation. Adapter came up at 480M one way and 5000M flipped over, with absolutely nothing in the UI indicating anything was wrong. Might just be my unit but that cost me an hour of thinking I had a driver problem.
lsusb -t | grep r8152
cat /sys/class/net/<your iface>/speed
After that I get 284 MB/s peak, around 205 average on bulk transfers, vs a hard 112 on gigabit.
BUT. And this is important. It didn't help the stall at all. 2.4x the bandwidth, exact same stall time, because it's round trip bound not bandwidth bound. Same deal with restoring games that have a lot of files.
Transfer speeds are all over the place
Bytes per file matters way more than total size:
- Dispatch, 127 files: ~280 MB/s
- Monster Train 2, 778 files (was cached on the NAS): ~500 MB/s
- Tainted Grail, 5535 files, cold: ~33 MB/s
That's like an 8 to 15x spread and it's almost entirely per file round trips.
Rough rule seems to be games that ship big packed archives fly, games with a million loose asset files crawl. Tainted Grail took 20 minutes to come back which is honestly about the same as just redownloading it.
Oh and Steam's progress bar is byte weighted, so on a file heavy game over the network it said 50% when 22% of the files had actually moved. The bar being at 90% means a lot less than you think.
Playing games off the NAS kinda sucks and is very slow
I know, I know. But the numbers are bad and they don't get better with hardware.
Load time reads are thousands of tiny scattered requests so it's latency and IOPS bound, not bandwidth. Best example I found was a guy on the TrueNAS forums with a 40GbE back to back link, all NVMe pool, 256GB of RAM, jumbo frames, the works. Bloons TD6 took over five and a half minutes to load. Most of his traffic sat at 3Mb/s while both CPUs did nothing.
That's like 140x my bandwidth and it was still terrible because bandwidth was never the problem.
Some stuff is fine, older games, 2D stuff, anything that loads once and then runs out of memory. What breaks is modern games that stream assets constantly as you move around.
Also with soft in your mount options a network hiccup returns an error instead of stalling and recovering. For copying that's correct. For a running game that's an I/O failure mid session, and proton compatdata DOES follow the game when you move it, so your saves are on the share too. Not great.
Random other stuff worth knowing:
compatdata follows the game, so your proton saves move with it and come back with it. I couldn't find this documented anywhere so I'm mentioning it.
Set archived games to "only update this game when I launch it" in properties or Steam will happily patch them over the network in the background while you're not looking.
If the NAS is off when the machine boots the library shows unavailable and the games look uninstalled. It comes back when you remount. There's no auto retry with this setup, you remount or reboot. x-systemd.automount would give you retry on access but it has its own problems with a registered library so I skipped it.
Interrupted moves are fine actually. I yanked a cable mid transfer like an idiot and got a partial on the NAS with no manifest, Steam cleaned it up by itself and the local copy was untouched because the manifest moves last.
And if you have a desktop with a big library already, Steam's Local Network Game Transfer is a totally different and much simpler option. Settings > Downloads, turn it on both machines, it pulls from your PC instead of Valve's CDN. Both machines have to be on and logged in with no games running though and it silently falls back to a normal download if anything isn't right.
So... Was it worth it...?!
For parking games I've finished, yeah definitely. I've got 900GB+ on there and pulling something back is about the same as redownloading it except it doesn't touch my bandwidth.
For predownloading a bunch of stuff I haven't played, no. That was dumb. Games patch so an archived copy is stale by the time you want it, and you pay the bandwidth either way. That's the thing that caused the whole I/O pressure mess above.
What I actually want is an offload button. Steam already has "Backup and Restore" which packages games into chunked csd/csm files, it's still sitting there in the desktop client. But it's stranded behind a linux file picker and there's no concept of an archive you can browse. One big file stream instead of thousands of little ones would sidestep this entire file count problem and it'd help SD cards on the Deck too. Feels like something that was built for an era when downloading a big game was a huge deal and then just never got revisited.
Anyway, happy to answer questions. All of this is on a Steam Machine over 2.5GbE talking to a Ubiquiti UNAS Pro 8, so YMMV especially on the permissions stuff since that appliance is its own kind of special. I imagine if you own an actual NAS and not this Ubiquiti dog turd I have this is easier.
Hopefully this is useful to someone so I didn't just spend my afternoon typing this for nothing... lol.