How Omarchy Migrations Run Per‑User: Complete Technical Guide
Omarchy migrations run per‑user by storing completion markers in each user's home directory (~/.local/state/omarchy/migrations) and executing scripts under the invoking user's UID, allowing granular privilege escalation only when needed.
When you upgrade Omarchy, the system must apply one‑time fixes, policy updates, and configuration changes without requiring manual intervention. The migration system achieves this by running scripts as the user who invokes the upgrade, tracking completion state locally, and deferring privileged operations to explicit sudo calls within each script.
How the Per‑User Migration System Works
The omarchy-migrate command in bin/omarchy-migrate implements a stateful, user‑scoped execution model. Rather than applying migrations system‑wide, it treats each user as an independent migration target.
Discovery and State Tracking
omarchy-migrate scans $OMARCHY_PATH/migrations/*.sh to find all available migration scripts. It compares these against completion markers stored in:
"$HOME/.local/state/omarchy/migrations/"
Each marker file matches its corresponding script name exactly (e.g., 1788619462.sh). This design ensures migrations run once per user regardless of how many times the command executes.
Execution Model
Scripts execute with bash -euo pipefail, enforcing strict error handling. The runner sources each pending script under the current user's UID, meaning:
- Unprivileged operations proceed without elevation
- Privileged operations require explicit
sudocalls within the script - Failure handling aborts immediately on non‑zero exit, leaving the migration unmarked for retry
This contrasts with system‑wide migration systems that typically require root throughout or complex privilege separation logic.
Notification Integration
After processing completes—whether successfully or with pending migrations remaining—omarchy-migrate triggers omarchy-migrate-notify.service. This systemd user service displays a desktop alert such as "Pending Omarchy Migrations" defined in default/systemd/user/omarchy-migrate-notify.service.
Running Migrations Manually
Apply All Pending Migrations
$ omarchy-migrate
This discovers unmarked scripts, executes them sequentially, and writes completion markers for successful runs.
Preview Pending Migrations
$ omarchy-migrate --pending
# → 1788619462.sh
# → 1788596255.sh
This lists scripts awaiting execution without modifying state.
Debug a Specific Migration
# Execute directly (omarchy-migrate manages markers on successful completion)
$ bash "$OMARCHY_PATH/migrations/1788619462.sh"
Direct execution bypasses the runner's state management—you must handle markers manually in this mode.
Key Files in the Migration System
| Path | Purpose |
|---|---|
bin/omarchy-migrate |
Main discovery, execution, and state management runner |
migrations/*.sh |
Individual migration scripts (timestamped filenames like 1788619462.sh) |
default/systemd/user/omarchy-migrate-notify.service |
User service for desktop notifications post-migration |
test/shell.d/migrate-scope-test.sh |
Validates per‑user isolation and state separation |
test/shell.d/migrate-wrapper-test.sh |
Tests runner functionality: pending listings, notifications, exit codes |
docs/update-process.md |
Documents migration integration within upgrade flows |
Integration with Upgrade Commands
Migration execution occurs automatically as a final step in upgrade workflows:
omarchy-upgrade-to-quattroomarchy-update- Other package management wrappers
This sequencing ensures new code is present before migrations attempt to modify configuration or apply fixes that depend on updated assets.
Per‑User Design Benefits
The state directory approach in ~/.local/state/omarchy/migrations/ provides concrete protections:
- Isolation: Each user's migration progress is independent; one user's failure or delay doesn't block others
- Retry safety: Failed migrations remain unmarked, enabling partial recovery without re‑executing successes
- Least privilege: Scripts run unprivileged by default, escalating only for operations explicitly requiring
sudo
The test suite in test/shell.d/migrate-scope-test.sh verifies this isolation by confirming that completion markers from one user don't satisfy migration requirements for another.
Summary
- Omarchy migrations run per‑user via state markers in
~/.local/state/omarchy/migrations/ omarchy-migratediscovers, executes, and records migrations frommigrations/*.sh- Execution uses
bash -euo pipefailunder the invoking user's UID with explicit privilege escalation - Failed migrations retry automatically on next invocation; successes never re‑execute
- Desktop notifications trigger via
omarchy-migrate-notify.serviceafter each run - Upgrade commands call migrations automatically as their final step
Frequently Asked Questions
Where does Omarchy store per‑user migration state?
Omarchy writes completion markers to "$HOME/.local/state/omarchy/migrations/" as files matching their corresponding script names. This user‑writable directory ensures cross‑user isolation without requiring system‑level coordination.
What happens if a migration script fails?
The runner aborts immediately on non‑zero exit due to set -e. No marker file is written, so the next omarchy-migrate invocation will retry that specific script while skipping already‑successful migrations.
Can I run migrations for another user?
No—omarchy-migrate operates exclusively under the current user's UID and writes state to that user's home directory. Administrators must either run migrations as each target user or design system‑level automation that invokes the command within user contexts.
How do I know if migrations are pending after an upgrade?
Run omarchy-migrate --pending to list unexecuted scripts, or check for the desktop notification triggered by omarchy-migrate-notify.service. The notification appears automatically when the upgrade process invokes omarchy-migrate at completion.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →