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-migrate discovers, 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_transaction ensures post-upgrade execution
  • Notification: omarchy-migrate-notify.service reminds 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →