How Omarchy Handles Migrations: A Technical Guide to Post-Upgrade Configuration Changes
Omarchy manages migrations through self-contained Bash scripts stored in migrations/ and executed via the omarchy-migrate command, using per-user state markers to ensure idempotent, incremental updates coordinated with Pacman transactions.
Migrations in Omarchy are post-upgrade configuration adjustments that bridge the gap between package installation and user-level setup. Unlike traditional package managers that run hooks automatically, Omarchy's migration system is per-user, opt-in, and stateful — designed for an Arch-based environment where multiple users may need individual attention after system updates.
Migration Discovery and State Tracking
The omarchy-migrate command drives the entire workflow. Its migration_entries function scans $OMARCHY_PATH/migrations (default: /usr/share/omarchy/migrations) for any file matching *.sh source: quattro/bin/omarchy-migrate#L35-L45.
State tracking happens per user through marker files:
STATE_DIR="${OMARCHY_MIGRATION_STATE:-$HOME/.local/state}/omarchy/migrations"
This variable defined at lines 31-33 of omarchy-migrate ensures that each user maintains their own migration history. When a migration succeeds, a corresponding marker file is created in this directory, preventing re-execution.
Checking for Pending Migrations
Before running anything, users can inspect what needs work. The --pending flag (also --check) lists migrations missing their marker files and exits 0 if any exist, 1 otherwise.
The pending_migrations function implements this by filtering migration_entries against existing markers source: quattro/bin/omarchy-migrate#L47-L55:
# List migrations waiting for the current user
omarchy-migrate --pending
This pattern enables safe automation — scripts can check status without side effects.
Pacman Coordination and Execution
Migration timing matters. The wait_for_pacman_transaction function lines 68-80 of omarchy-migrate polls /var/lib/pacman/db.lck to ensure no Pacman transaction is active before proceeding. This guarantees migrations run after packages are fully installed, when user sessions and DBus are stable.
The execution loop starting at line 88 handles each pending migration:
- Prints a colored banner identifying the migration
- Executes the script under
bash -euo pipefail(strict error handling) - Creates the marker file on successful exit
# Run all pending migrations for the current user
omarchy-migrate
Migration Script Structure
Migration scripts are ordinary Bash files with two constraints: they must be idempotent and self-contained. Here's a representative example from the migrations/ directory:
#!/usr/bin/env bash
# Example: remove deprecated tmux configuration
CONFIG="${HOME}/.config/tmux.conf"
sed -i '/old-feature-flag/d' "$CONFIG"
The script runs in the user's environment with full $HOME access. After success, Omarchy creates ~/.local/state/omarchy/migrations/1785189600.sh — the filename matches the migration script, making state inspection trivial.
Desktop Notifications for Migrations
Users who skip omarchy update still get reminded. The omarchy-migrate-notify.service systemd user service runs omarchy-migrate --pending on login. When pending migrations exist, it displays a toast notification whose click action launches the full migration command [described in docs/update-process.md](https://github.com/basecamp/omarchy/blob/quattro/docs/update-process.md#L59-L66).
The helper script omarchy-migrate-notify bridges the gap between background checking and user intervention source: quattro/bin/omarchy-migrate-notify.
Key Design Principles
Omarchy's migration system embodies several deliberate architectural choices:
- Per-user scope — Machine-wide repairs can coexist with personal configuration changes; each user runs migrations independently
- Incremental application — Missing markers mean pending work; present markers mean skip
- Package manager coordination — Pacman lockfile polling prevents race conditions during active upgrades
- Minimal privilege escalation — Scripts request sudo only when absolutely necessary, operating primarily in user space
These principles make Omarchy migrations safe for unattended execution while flexible enough for complex post-upgrade repairs.
Summary
- Command:
omarchy-migratediscovers, checks, and runs migration scripts from/usr/share/omarchy/migrations - State: Per-user marker files in
~/.local/state/omarchy/migrations/track completion - Safety: Pacman lockfile polling in
wait_for_pacman_transactionensures post-upgrade execution - Notification:
omarchy-migrate-notify.servicereminds users of pending work via desktop toast - Idempotency: Migration scripts are expected to be safe for repeated execution
Frequently Asked Questions
Where are migration scripts stored in Omarchy?
Migration scripts live in migrations/ under the Omarchy installation path, defaulting to /usr/share/omarchy/migrations/. The migration_entries function in bin/omarchy-migrate scans this directory for *.sh files.
Can I run migrations for another user?
No. The migration state directory defaults to ${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/migrations, making migrations inherently per-user. This design intentionally isolates user-level configuration changes from system-wide operations.
What happens if a migration script fails?
The execution loop in omarchy-migrate runs scripts with bash -euo pipefail, meaning any non-zero exit stops that migration. Failed migrations do not create marker files, so they will be re-attempted on the next omarchy-migrate invocation.
How does Omarchy know when Pacman is finished upgrading?
The wait_for_pacman_transaction function polls for /var/lib/pacman/db.lck every second until the lockfile disappears source: lines 68-80. This ensures migrations never run during an active package transaction.
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 →