Designing a Durable Save Backup on Steam Deck
Abstract
After spending dozens of hours in The Legend of Zelda: Breath of the Wild, I wanted stronger guarantees around save durability than simply trusting a single SSD.
The goal was not merely to back up save files. The goal was to recreate, for an emulator-based game, the confidence that Steam Cloud provides for native Steam titles: progress should remain recoverable even if the underlying device does not. So the design should handle:
- Wi-Fi disconnects
- Power loss
- Multiple save events
- Concurrent backup execution
- Partial filesystem updates
This article describes the resulting design, the guarantees it provides, and the trade-offs that were intentionally made.
Motivation
Most Steam games already benefit from Steam Cloud synchronization.
If a Steam Deck is lost, reset, or replaced, Steam can usually restore progress automatically.
However, this project started with a game that exists entirely outside that ecosystem.
My copy of The Legend of Zelda: Breath of the Wild runs through Cemu, a Wii U emulator. The save data lives entirely within Cemu's emulated filesystem and is unknown to Steam Cloud.
From Steam's perspective, the game does not exist.
This creates an interesting reliability gap:
Steam Game
↓
Steam Cloud
↓
Automatic Recovery
BOTW on Cemu
↓
Local Files Only
↓
Single Point of FailureThe save data is ultimately stored on a single SSD inside the Steam Deck.
While SSD failures are uncommon, they are not impossible. More realistic failure scenarios include:
- Accidental deletion
- Corrupted emulator configuration
- Failed storage migrations
- Broken updates
- Device loss or theft
- User error during emulator maintenance
After investing dozens of hours into a playthrough, relying on a single copy of the save directory felt unnecessarily risky.
The obvious solution was "just back up the files."
The less obvious challenge was determining what properties a good backup system should have.
I wanted something that was:
- Fully automatic
- Resource efficient
- Reliable while traveling
- Resilient to intermittent connectivity
- Easy to understand and debug
- Independent of proprietary backup software
Rather than treating the problem as file copying, I approached it as a small systems design exercise:
Can a Steam Deck continuously produce durable recovery points for emulator saves while remaining effectively invisible during normal gameplay?
The rest of this article describes the resulting architecture.
Problem Statement
The challenge appears deceptively simple:
When BOTW saves, upload the save directory to Google Drive.
However, several questions emerge immediately:
- How do we know the save operation is complete?
- What happens if the Deck loses connectivity during upload?
- How do we prevent multiple overlapping backup processes?
- How do we avoid uploading identical save states?
- What consistency guarantees can realistically be provided?
The system should be:
- Event driven
- Battery efficient
- Failure tolerant
- Easy to reason about
- Operable without external infrastructure
Requirements
Functional Requirements
- Automatically detect save activity.
- Upload save snapshots to Google Drive.
- Preserve historical versions.
- Survive Steam Deck reboots.
Non-Functional Requirements
- Near-zero idle CPU usage.
- No polling.
- No concurrent uploads.
- No duplicate snapshots.
- No advancement of backup state on failed uploads.
Design Principles
The implementation was guided by four principles.
Event Driven
The system should wake only when useful work exists.
Polling introduces unnecessary CPU wakeups and battery consumption. Linux already provides filesystem notifications through inotify, making periodic scans unnecessary.
Single Writer
Only one backup operation may execute at a time.
This dramatically simplifies reasoning about state transitions and eliminates an entire class of concurrency bugs.
State Advances Only After Success
A failed upload must never be treated as a completed backup.
The backup state is updated only after a successful upload to Google Drive.
Simplicity Over Cleverness
The objective is long-term maintainability rather than maximum sophistication.
The entire system is intentionally built from a small set of primitives:
- inotify
- flock
- SHA-256
- systemd
- rclone
A system that can be fully understood in a few minutes is easier to trust than a more complex alternative.
Architecture
sequenceDiagram
participant BOTW as BOTW/Cemu
participant Watcher as inotifywait
participant Backup as backup-botw.sh
participant Drive as Google Drive
BOTW->>Watcher: Save files written
Watcher->>Watcher: Stabilization delay
Watcher->>Backup: Launch backup
Backup->>Backup: Wait for flock lock
Backup->>Backup: Compute directory hash
alt Hash unchanged
Backup-->>Watcher: Exit
else Hash changed
Backup->>Drive: Upload snapshot
alt Upload succeeds
Backup->>Backup: Update stored hash
else Upload fails
Backup->>Backup: Preserve previous state
end
endConsistency Model
The primary design challenge is consistency.
A filesystem notification does not imply that an application has finished updating all related files.
Consider a hypothetical save sequence:
- Write save data
- Flush data
- Update metadata
- Update tracking information
- Close file descriptors
Capturing a snapshot midway through this sequence may produce a backup state that never existed from the application's perspective.
The solution is intentionally simple:
sleep 20A 20-second stabilization window is introduced between event detection and backup execution.
This trades immediacy for consistency.
Guarantee
Every uploaded snapshot is taken from a quiescent save directory.
Concurrency Model
Filesystem events frequently arrive in bursts.
A single save action may trigger multiple events:
close_write
close_write
moved_to
close_writeWithout coordination, multiple backup processes could execute simultaneously.
The system uses a file lock:
exec 9>"$LOCKFILE"
flock 9This establishes a single-writer model.
Backup processes are serialized rather than executed concurrently.
If a save event occurs while a backup is already running, the subsequent backup process waits for the lock to become available before proceeding.
Because each backup recomputes the save directory hash after acquiring the lock, every observed save event eventually results in a backup evaluation, while duplicate uploads are eliminated through content hashing.
Consequence
The system guarantees:
- No concurrent uploads
- No lost save events due to overlapping backup execution
- No duplicate uploads for identical save states
Change Detection
A common implementation would compare timestamps.
This system instead computes a deterministic hash across the save directory:
find ...
sha256sum ...This ensures:
- Metadata-only changes are ignored.
- Identical save states are never uploaded twice.
- Backup state reflects content rather than filesystem metadata.
Failure Analysis
| Failure | Outcome | Recovery |
|---|---|---|
| Wi-Fi disconnects | Upload fails | Next save retries |
| Google Drive unavailable | Upload fails | Future saves retry |
| Steam Deck reboot | Service restarts | Monitoring resumes |
| Multiple save events | Serialized by flock | Consistent state |
| Timestamp-only change | Hash unchanged | No upload |
| Upload interrupted | State not advanced | Retry later |
| Save during active backup | Backup waits for lock | Evaluated after prior backup completes |
Guarantees
The System Guarantees
- Event-driven operation
- No polling
- No concurrent uploads
- No duplicate snapshots
- No advancement of state after failed uploads
- Historical recovery points
- Automatic restart after reboot
- Every observed save event eventually results in a backup evaluation
Operational Characteristics
CPU
Idle CPU utilization is effectively zero.
The watcher blocks inside the kernel waiting for filesystem notifications.
Memory
The watcher process consumes roughly:
~1 MBBattery Impact
Because the design is event-driven, battery consumption is dominated by the upload itself rather than monitoring.
For save files measured in megabytes, the impact is negligible compared to Cemu emulation.
Conclusion
The final design intentionally favors simplicity.
The result is an eventually durable backup system that remains easy to understand, easy to debug, and resilient to common failure scenarios while running continuously on a Steam Deck.
More importantly, it restores a property that emulator-based games often lose compared to native Steam titles: confidence that progress is durable.
Steam Cloud provides that confidence for supported games.
For software operating outside that ecosystem, we sometimes have to build it ourselves.