Share one Steam game library across multiple Linux users with fully isolated Proton prefixes. Uses bubblewrap to create a per-user kernel overlay on /opt/steam/steamapps/compatdata/ so game files stay shared while Proton prefixes are isolated per user, with no compatibility tool selection or per-game configuration required. Includes: - steam-shared launcher that sets up the per-user overlay and execs Steam inside a bwrap mount namespace - activate/uninstall scripts plus an add-user helper for steamshare group membership - permission watcher (steam-fix-perms.path/.service) to keep ACLs correct under pressure-vessel's restrictive mode bits - .desktop override that routes the system Steam launcher through steam-shared - Nix flake exposing activate, uninstall, and add-user packages - design doc and implementation plan covering the approach
5.0 KiB
Steam Shared Library
Share one Steam game library across multiple Linux users with fully isolated Proton prefixes.
The Problem
Steam stores Proton/Wine prefixes (compatdata) inside the game library folder. When multiple users share a library, they all write to the same prefix directory, causing:
wineserver: pfx is not owned by youPermissionError: os.chmodpressure-vessel: Permission denied
How It Works
Uses bubblewrap to create a per-user kernel overlay on /opt/steam/steamapps/compatdata/:
- Game files (
common/,shadercache/) → shared directly, readable and writable by all group members - Proton prefixes (
compatdata/) → overlaid per-user via bwrap's mount namespace
Each user gets their own overlay. Writes go to ~/.local/share/steam-shared/upper/. Reads fall through to the shared directory. Multiple users can be logged in simultaneously (GDM user switching) — each session has its own isolated mount namespace.
No compatibility tool selection. No per-game configuration. Works with any Proton variant.
Prerequisites
- Linux with a modern kernel (overlay support)
bubblewrap(pacman -S bubblewrap/apt install bubblewrap)aclpackage forsetfacl(usually pre-installed)- Steam (native package or Flatpak)
- Nix (for installation via flake)
Installation
# install (requires sudo)
nix run github:felixfoertsch/steam-shared-library
# or from a local checkout:
sudo ./scripts/activate.sh
This creates:
steamsharegroup/opt/steam/with correct permissions and ACLs/usr/local/bin/steam-sharedlauncher.desktopoverride so Steam launches through the shared launcher
Setup
# add users to the shared library group
sudo usermod -aG steamshare <username>
# user must log out and back in for group membership
# each user: open Steam → Settings → Storage → add /opt/steam
# install games to /opt/steam — they're shared for all users
# use any Proton version — prefix isolation is automatic
Steam Detection
The launcher prefers native Steam over Flatpak:
- If
/usr/bin/steamexists → native Steam - Else if Flatpak Steam is installed → Flatpak with filesystem access to
/opt/steam - Else → error
If both are installed, native wins. Having both native and Flatpak Steam is discouraged — it leads to two separate configs, library confusion, and wasted disk space. Pick one.
Uninstall
nix run github:felixfoertsch/steam-shared-library#uninstall
# or from a local checkout:
sudo ./scripts/uninstall.sh
This removes the launcher, .desktop override, and steamshare group. Game data at /opt/steam/ and per-user prefixes at ~/.local/share/steam-shared/ are preserved (remove manually if desired).
How It Works (Technical)
The Steam launcher (steam-shared) does:
- Verify prerequisites (shared dir exists, user in steamshare group, bwrap installed)
- Detect Steam installation (native or Flatpak)
- Create per-user overlay directories (
~/.local/share/steam-shared/{upper,work}) - Launch Steam inside a bwrap mount namespace with an overlay on
compatdata/
bwrap --dev-bind / / \
--overlay-src /opt/steam/steamapps/compatdata \
--overlay ~/.local/share/steam-shared/upper \
~/.local/share/steam-shared/work \
/opt/steam/steamapps/compatdata \
-- steam
Inside the namespace, any write to /opt/steam/steamapps/compatdata/ goes to the per-user upper layer. The shared lower layer is read-only from the overlay's perspective. Game files outside compatdata/ are not overlaid — they're shared normally.
Caveats
Do not nest steam-shared inside another bwrap wrapper
bwrap without the setuid bit (the default on modern distros) creates an unprivileged user namespace. User namespaces cannot preserve supplementary groups — every group except the primary one becomes nobody inside the namespace.
That has two consequences if you wrap steam-shared inside an outer bwrap (for example, a Steam lancache wrapper that bind-mounts a custom resolv.conf):
- The
steamsharegroup check insteam-sharedfails —id -nGno longer listssteamshareinside the outer namespace. - Even if you bypass the check, Steam itself cannot write to
/opt/steam/steamapps/common/. The group bit that grants access is gone inside the namespace, so new game installs fail withEACCES.
If you need a lancache or similar DNS redirect, put it at the system level (a systemd-resolved drop-in, or a local dnsmasq), not inside a second bwrap around steam-shared.
Prior Art
This project originally used a Proton compatibility tool wrapper to redirect STEAM_COMPAT_DATA_PATH. That approach was brittle:
- Required selecting the wrapper as the compatibility tool for every game
- Per-game Proton overrides bypassed the wrapper entirely
- Steam sometimes wrote to the original path before the wrapper ran
The bwrap overlay approach solves all of these by operating at the filesystem level, below Steam and Proton.