Skip to main content

proton-home-sync

Merge every Steam Proton prefix's home directory into a single shared home, then replace each prefix's home with a symlink to it. Backup tools that don't cooperate well with symlinks (most of them) can target one real directory and get every game's saves.

~/.steam/steam/steamapps/compatdata/
├── 11111/pfx/drive_c/users/steamuser  ──►  ~/proton-shared-home/steamuser/
├── 22222/pfx/drive_c/users/steamuser  ──►  ~/proton-shared-home/steamuser/
└── 44444/pfx/drive_c/users/steamuser  ──►  ~/proton-shared-home/steamuser/
                                              └── (real directory — the only backup target)

The arrows go prefix → shared home. The shared home is one real directory on disk. Once a prefix is linked, it stays linked — there is no deletion tracking to worry about. When Steam creates a brand-new prefix, the daemon picks it up, merges its initial Wine state into the shared home, and turns the prefix's home into a symlink.

Why this shape

  • Most backup tools either de-duplicate symlinks to nothing or chase pointers back into your Steam library. With a real shared home, the tool walks one tree and sees real files.
  • No deletion tracking. New prefixes just add their initial files to the shared home; after the symlink flip they write straight into it.
  • One backup target: ~/proton-shared-home/steamuser/ (or wherever you point --central-home).

The cost is that all games now share one Windows user namespace, so two games that use the same path under %USERPROFILE% would collide. In practice most games namespace themselves via %APPDATA%\<Studio>\<Game>\, Documents/My Games/<Game>/, or Saved Games/<Game>/ and don't trip on each other.

Install

pipx install .                                # from a cloned checkout
# or directly from GitHub:
pipx install git+https://github.com/vality/proton-home-sync
# or
pip install --user .
# plus, for daemon mode:
pip install 'proton-home-sync[daemon]'

Requires Python ≥ 3.9 and the vdf package (pulled in automatically).

Usage

# First run — merges every existing prefix into the shared home and links them.
proton-home-sync sync

# Use a custom shared home location.
proton-home-sync sync --central-home ~/backups/proton-shared-home

# See what would happen.
proton-home-sync sync --dry-run

# Show every prefix and its link state (✓ linked, · real dir, ↗ manual symlink).
proton-home-sync list

# Health summary — central home size + per-prefix link state.
proton-home-sync status

# Tear down the shared home (does NOT unlink the prefixes).
proton-home-sync clean

# Long-running watcher — auto-migrates new prefixes as games install.
proton-home-sync daemon

Conflict policy

When a prefix's home contains a file that's already in the shared home, the default is --on-conflict skip — the shared copy wins, the prefix's copy is left behind (and effectively discarded because the prefix's home is then replaced with a symlink). Pass --on-conflict overwrite to make the prefix's copy win instead; this is useful when re-migrating after a manual rollback.

Hooking up a backup tool

# restic — encrypted, incremental
restic backup ~/proton-shared-home/steamuser

# borg — encrypted, deduplicated
borg create ~/backup::saves-$(date +%F) ~/proton-shared-home/steamuser

# rclone — to a cloud target
rclone sync ~/proton-shared-home/steamuser remote:proton-saves

# rsync — to a USB drive
rsync -a --delete ~/proton-shared-home/steamuser/ /mnt/usb/saves/

That's the entire backup story — one real directory, one command.

Daemon mode

proton-home-sync daemon runs forever, watching each Steam library's compatdata/ for new prefixes. When one shows up (or Steam re-creates an existing one), the daemon migrates it.

pip install 'proton-home-sync[daemon]'
proton-home-sync daemon

Useful flags:

# React faster to filesystem events.
proton-home-sync daemon --debounce-ms 500

# Less frequent full rescans (default every 5 minutes).
proton-home-sync daemon --rescan-seconds 900

The daemon logs to stderr. Run it under your service supervisor of choice.

systemd --user unit

# ~/.config/systemd/user/proton-home-sync.service
[Unit]
Description=Merge new Proton prefix home directories into the shared home
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/proton-home-sync daemon
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target
systemctl --user enable --now proton-home-sync.service

Configuration

Defaults live in proton_home_sync/config.py:

Setting Default Override
Steam roots ~/.steam/steam, Flatpak variants --steam-root
Shared home ~/proton-shared-home/steamuser --central-home
Wine user steamuser --username
Conflict policy skip (shared home wins) --on-conflict overwrite

Safety properties

  • Idempotent. Re-running sync is a no-op once everything is linked.
  • Atomic flip. When migrating, the prefix's real directory is renamed aside first, the symlink is created, and only then is the backup deleted. If anything fails midway, the prefix is restored.
  • Manual symlinks are respected. A prefix whose home already symlinks somewhere else is left alone with a warning.
  • clean is paranoid. Refuses ~, /, symlinks, or non-directories.

Development

CI status

python -m venv .venv
source .venv/bin/activate
pip install -e '.[daemon,test]'
pytest

The test suite builds a fake Steam install on disk (see tests/conftest.py) — two libraries, three valid prefixes plus one broken one — and exercises every code path.

Project layout

proton-home-sync/
├── pyproject.toml
├── README.md
├── proton_home_sync/
│   ├── __init__.py
│   ├── __main__.py
│   ├── cli.py            # argparse + subcommand dispatch
│   ├── config.py         # default paths and tunables
│   ├── daemon.py         # long-running watcher (uses watchfiles)
│   ├── merge.py          # merge-then-symlink migration logic
│   ├── models.py         # ProtonPrefix, MigrationResult, MigrationReport
│   ├── prefix.py         # scan libraries for prefixes
│   ├── steam.py          # locate Steam root + libraryfolders.vdf parser
│   ├── utils.py          # human_path, helpers
│   └── linker.py         # deprecation shim → re-exports merge.py
└── tests/
    ├── conftest.py       # fake_steam fixture (two libraries, 3 valid prefixes)
    ├── test_steam.py
    ├── test_prefix.py
    ├── test_merge.py
    ├── test_linker.py    # deprecation marker
    ├── test_cli.py
    └── test_daemon.py

License

MIT.

Release files for proton-home-sync 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for proton-home-sync 0.1.1
File Size Uploaded
proton_home_sync-0.1.1.tar.gz 41.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for proton-home-sync 0.1.1
File Interpreter ABI Platform
proton_home_sync-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 70.1 kB

Release files / proton_home_sync-0.1.1.tar.gz

Download URL proton_home_sync-0.1.1.tar.gz
Size 41.6 kB
Tags Source
SHA-256 checksum
How to use checksums
5c12f05bb4a23d27a9b545e42fba4e039ca906ae152ebd7be333e9bd190f76e9
BLAKE2b-256 checksum
How to use checksums
c32b7f3d05fe9808e6b7019595c49069067aa84232b3b264d0bfc7f0b46b526d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release files / proton_home_sync-0.1.1-py3-none-any.whl

Download URL proton_home_sync-0.1.1-py3-none-any.whl
Size 28.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3735daf2db85e98561a000762c65710d32a3a0bf8d0b0c6eec6583279368eb32
BLAKE2b-256 checksum
How to use checksums
d08548d865acd533062ddac141f6dff9d6bf78b972200bc3b7ed1b4b077dc99c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.6

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page