Reflections on trust, restore safety, threading, and building a desktop orchestration layer around a CLI engine.
Backups Are Easy. Restores Are Not.
Taking backups is usually simple.
Restoring them — confidently, correctly, and without hesitation — is where anxiety begins.
Over time, I realized that most tools are designed around creating backups. Very few are designed around restoring safely. And psychologically, restore confidence is what really matters.
This realization is what led me from a small Bash script wrapped in KDialog to a full multithreaded Qt/C++ desktop application built around restic.
Phase 1: The Script That Solved One Problem
The first version of this project was nothing more than:
- A Bash script
- Hardcoded include paths
- restic CLI calls
- KDialog progress dialogs
It removed repetitive typing. That was enough — for a while.
But as I started using restic to manage configuration state (Docker stacks, system configs, reverse proxies, etc.), new requirements emerged:
- Multiple structured backup definitions
- Clean separation between “what to back up” and “how to execute”
- Safer restore workflows
- Snapshot comparison
- Elevated execution handling
- Persistence beyond flat config files
The script had reached its natural limits.
The Philosophy: Orchestrator, Not Engine
One rule shaped everything that followed:
Restic remains the engine. The application must remain an orchestrator.
I did not want:
- A custom backup format
- A metadata layer that breaks CLI compatibility
- Tight coupling to restic internals
- A forked engine
Repositories remain 100% standard restic repositories.
You can always use the CLI directly.
This separation is intentional. It preserves transparency and trust.
Why Qt/C++?
The move to Qt Widgets + C++ was pragmatic:
- Cross-platform (Linux + Windows)
- Strong threading primitives
- Clean process management (QProcess)
- Mature event system
- Deterministic memory ownership
This was not about building something flashy.
It was about building something predictable.
Backup software should feel boring — in the best possible way.
Threading and Process Isolation
One non-negotiable requirement:
The UI must never block during backup or restore operations.
Architecture:
UI Thread
→ Operation Controller
→ Worker Object (QObject-based)
→ QThread
→ QProcess (restic invocation)
Key principles:
- Restic runs as an external process.
- Output streams are parsed asynchronously.
- No nested event loops.
- No UI updates from worker threads.
- Strict signal-slot boundaries.
This preserves responsiveness and reduces risk of undefined state during long operations.
The application does not embed restic. It orchestrates it.
Profiles as “State Definitions”
The biggest conceptual shift from the Bash prototype was introducing structured profiles.
A profile defines:
- Repository path
- Multiple include directories
- Selective file includes
- Glob-based exclude patterns
- Pre/Post scripts
- Elevation requirements
- Default restore behavior
Instead of thinking in terms of “run backup command”, I started thinking in terms of:
Define the state of a machine.
For example:
Profile: docker-host-01
Includes:
- /etc/docker
- /srv/docker-compose
- /etc/nginx
- specific .env files
Excludes:
- /logs/
- /cache/
- transient runtime directories
This allows treating configuration as versioned infrastructure.
Restore Is a First-Class Citizen
Most CLI usage patterns focus on restic backup.
I chose to design the UI around restore semantics.
Two restore modes emerged:
Mirror Mode
The target directory becomes identical to the snapshot.
Useful for:
- Rebuilding a machine
- Migrating to a clean VM
- Rehydrating configuration state
Update Mode
Only overwrite files that exist in the snapshot.
Useful for:
- Partial correction
- Non-destructive repair
- Controlled restoration
Internally, restore operations are staged and validated before final application.
Restore must feel deliberate — not destructive.
Snapshot Diff and File-Level Diff
Restic already tracks content efficiently.
But understanding what changed is equally important.
The application can:
- Compare two snapshots
- Identify added/modified/deleted files
- Extract specific file versions
- Perform file-level diff analysis
This turns backups into something closer to version history.
For configuration-heavy environments, this visibility matters.
Elevated Execution and Hooks
System paths often require root/admin access.
The application supports:
- Explicit elevated execution
- Controlled privilege escalation
- Pre/Post scripts per profile
Examples:
- Stop Docker before backup
- Dump a database before snapshot
- Restart services after restore
These are not advanced features — they are practical realities in homelab and system environments.
Automation without context can be dangerous.
Controlled hooks provide flexibility without losing structure.
Persistence and Portability
SQLite is used for:
- Profile definitions
- Operation history
- Application settings
Import/Export allows moving profile definitions between machines.
The repository remains external and portable.
The application’s state is separate from backup data.
Again — boundaries matter.
Build System and Determinism
The project uses CMake with clear target separation:
- Core execution module
- Worker module
- UI module
- Database layer
Shared library outputs are controlled explicitly.
No versioned SONAME chains where unnecessary.
Deployment artifacts are predictable.
Backup tooling should not surprise the user — including at build time.
A Calm Conclusion
This project evolved gradually:
Bash script → structured orchestration → multi-threaded Qt desktop application.
The most important insight was not about threading or CMake.
It was this:
Backup software is about confidence.
Confidence comes from:
- Predictable behavior
- Clear boundaries
- Restore safety
- Transparency
- Compatibility
Restic remains the engine.
The desktop layer exists to improve workflow — not to obscure it.
If there is one architectural lesson here, it is this:
When wrapping a CLI tool with a GUI, preserve the engine’s integrity — and design around the human moment of restore.
Project Reference
I packaged this into a desktop application called EasyBackupManager (EBM).
If you're curious to explore it, see screenshots, or try the Windows/Linux builds which is completely free, you can find it here:
The repositories remain fully standard restic repositories and are always accessible via the CLI.
Top comments (0)