- MIT LICENSE - CHANGELOG.md (CalVer, first entry 2026.04.15) - CODE_OF_CONDUCT, CONTRIBUTING, SECURITY, SUPPORT policies - docs/release.md covering preflight, tagging, rollback - GitHub Actions CI running shell syntax, shellcheck, desktop-file-validate, script tests, and nix flake check - tests/ harness with activate-path and preflight checks; preflight test stubs `id` via PATH so it cannot launch real Steam on a developer machine where /opt/steam already exists
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.