NixOS: Windows Game Sandboxing
By Kitsune
Work notes ·NixOS Nix Linux Wine Sandboxing
I wanted to run a Windows game without making the rest of my machine part of its workspace.
That sounds straightforward until launchers, Wine drive mappings, mounted disks, and compatibility runtimes all get involved. A game needs access to its files. Its installer may need temporary storage. The launcher needs enough of the environment to start the process. None of that tells me that every mounted volume should be visible.
On NixOS, I worked on a per-game sandbox around Lutris and UMU using bubblewrap. The goal was to make filesystem access explicit and keep the setup reproducible through configuration.
Give the application a deliberate filesystem
The design used a policy file to describe the access intended for a particular game. In the setup I tested, a selected game directory appeared as the Windows D: drive, while broad access through mount locations such as /mnt and /run/media was hidden.
The distinction mattered to me. A convenient drive letter is only part of the experience. I also wanted to control what the process could reach underneath that mapping.
The policy itself needed protection too. If an application can rewrite the rules used for its next launch, those rules are not a dependable boundary.
On June 26, I confirmed that the sandbox arrangement was working as intended in my test. That was a useful milestone: the launcher and compatibility tooling could cooperate with a narrower view of the filesystem.
It was a functional result, rather than an exhaustive security audit. I still need to evaluate any access exposed for display, devices, or other integration separately from filesystem restrictions.
Try the filesystem policy
This model shows the difference between a drive mapping and the access behind it. Toggle access to other volumes, then try making the launch policy writable. The paths are examples; the controls don’t change your machine.
Choose what the game can see
| Resource | Game view | Access |
|---|---|---|
| /games/example | D: | Read/write |
| /mnt/photos | Other mounted volume | Hidden |
| /run/media/archive | Removable volume | Hidden |
| Private temporary directory | /tmp | Read/write |
| Launch policy | Wrapper configuration | Read only |
The selected game directory is writable; other mounted volumes are hidden and the policy is read only.
The bubblewrap documentation makes the same distinction about scope: the security properties depend on the arguments and resources made available to the sandbox. This example only covers filesystem visibility.
Nix makes the workaround something I can inspect
What appeals to me about putting this work into Nix is having the environment and launch behavior represented as configuration.
When a wrapper or dependency changes, I want that change to be visible. When I revisit the machine later, I want to understand why a package or an argument is there. A working shell command is useful; a setup I can reconstruct is much more useful.
There is still a runtime policy to reason about. Nix doesn’t decide which directories a game should be allowed to see. It gives me a place to express the machinery that applies those decisions consistently.
That is especially valuable when there are several programs between the desktop icon and the actual game process. I want to understand which layer creates the environment and which layer might broaden it again.
Now I can run silly games without giving them access to my whole computer! Yay!