How Omarchy Migrations Work and How to Write New Migration Scripts

Omarchy migrations are one-time repair scripts that run after package updates to adjust user or system state that Pacman cannot modify safely, with each script stored in migrations/*.sh and tracked per-user via marker files in ~/.local/state/omarchy/migrations/.

In the basecamp/omarchy repository, migrations form a critical bridge between immutable package updates and mutable user configuration. Unlike traditional database migrations, these Bash scripts repair symlinks, update systemd units, or adjust application state that requires user-scoped changes after system-level updates complete.

What Are Omarchy Migrations?

Omarchy migrations are idempotent repair scripts designed to run after Pacman finishes updating packages. They live in the migrations/ directory at the repository root, using Unix timestamp filenames like migrations/1781158082.sh to ensure sequential execution.

The migration system addresses a specific gap in Arch Linux package management: certain configuration changes, theme updates, or user-level state modifications cannot be performed safely by Pacman during installation. Instead, the omarchy-migrate command—located at bin/omarchy-migrate—orchestrates these changes after the fact.

When and How Migrations Execute

The migration runner executes scripts through three distinct triggers, ensuring no user is left with broken configuration after an update.

Automatic Execution During Updates

When you run omarchy update, the process follows this sequence:

  1. Pacman completes package installation
  2. omarchy-migrate runs all pending migrations for the current user
  3. omarchy-hook post-update executes final cleanup

This ensures that system updates immediately trigger necessary configuration repairs before the user continues working.

Graphical Login Notifications

The systemd service omarchy-migrate-notify.service runs omarchy-migrate --pending at every graphical login. If pending migrations exist (indicated by exit code 0), the service opens a terminal notification prompting the user to run omarchy-migrate.

This mechanism guarantees that secondary user accounts—who may not have performed the original system update—still receive necessary configuration changes.

Manual Invocation

Users can trigger migrations manually at any time by running:

omarchy-migrate

The runner automatically skips any migrations already recorded in the user's state directory, making repeated execution safe.

Understanding the Migration Lifecycle

The Omarchy migration system uses a marker-file pattern to ensure robust, per-user execution tracking.

Per-User State Tracking

After a migration completes successfully, the runner creates a marker file:


~/.local/state/omarchy/migrations/<migration-filename>

For example, after running migrations/1781158082.sh, the file ~/.local/state/omarchy/migrations/1781158082.sh appears. This per-user state ensures that every account on the system runs each migration independently, preventing permission errors or cross-user configuration leaks.

Idempotency Requirements

Because migrations may execute multiple times (once per user, or manually during testing), every script must be idempotent. The script should detect whether its intended change already exists and exit without side effects if so.

According to the source code in agents/skills/migrations.md, proper idempotency prevents duplicate configuration entries, broken symlinks, or repeated package installations if a user manually re-runs the migrator.

How to Write New Migration Scripts

Creating a new migration involves scaffolding, implementing idempotent logic, and testing against isolated environments.

Generating a Migration Stub

Use the helper command to generate a properly named script:

omarchy-dev-add-migration --no-edit

This creates a file named migrations/<unix-timestamp>.sh with 0644 permissions. The timestamp naming convention ensures deterministic execution order across all installations.

Migration Script Format and Conventions

All migration scripts must follow the new migration format documented in agents/skills/migrations.md:

  • No shebang line – The runner executes scripts explicitly with bash -euo pipefail
  • Descriptive echo – Begin with echo describing the migration's purpose
  • Use $OMARCHY_PATH – Reference the repository root through this environment variable
  • Idempotent checks – Verify existing state before modifying
  • Helper commands – Use omarchy-cmd-present, omarchy-pkg-add, and other Omarchy utilities for system integration
  • No shell restart – Do not restart the Omarchy shell; omarchy update handles this automatically

Example migration (re-linking the Neovim theme):

echo "Relink Neovim theme to Omarchy current state"

theme_link="$HOME/.config/nvim/lua/plugins/theme.lua"
current_relative_target="../../../../.local/state/omarchy/current/theme/neovim.lua"

[[ -L $theme_link ]] || exit 0
ln -sfn "$current_relative_target" "$theme_link"

This script checks if the symlink exists before attempting to recreate it, demonstrating proper idempotency.

Testing Your Migration

Test migrations against a temporary home directory to prevent contamination of your working environment:

HOME=$(mktemp -d) bash -euo pipefail migrations/1781158082.sh

To re-run a migration locally after the marker file has been created, remove the marker and invoke the migrator:

rm ~/.local/state/omarchy/migrations/1781158082.sh
omarchy-migrate

Summary

  • Omarchy migrations are repair scripts stored in migrations/*.sh that run after Pacman updates to fix user-level configuration.
  • The omarchy-migrate command (in bin/omarchy-migrate) executes scripts with bash -euo pipefail and tracks completion via marker files in ~/.local/state/omarchy/migrations/.
  • Migrations trigger automatically during omarchy update, at graphical login via omarchy-migrate-notify.service, or through manual invocation.
  • Every migration must be idempotent, checking for existing state before making changes.
  • Use omarchy-dev-add-migration --no-edit to scaffold new scripts, and test them in temporary home directories before deployment.

Frequently Asked Questions

Where are Omarchy migration scripts stored?

All migration scripts live in the migrations/ directory at the repository root, using Unix timestamp filenames (e.g., migrations/1781158082.sh). This naming convention ensures chronological execution order across different systems and update sequences.

How does Omarchy ensure migrations run for every user?

The system writes marker files to ~/.local/state/omarchy/migrations/<filename> after each migration completes. Because this path resides in the user's home directory, every user account must run the migration independently. The omarchy-migrate-notify.service triggers at graphical login to catch users who missed the update window.

What makes a migration script idempotent?

An idempotent migration safely executes multiple times without cumulative side effects. Scripts should check for existing configuration (e.g., [[ -L $symlink ]] or [[ -f $file ]]) and exit early if the desired state already exists. The runner invokes each script with bash -euo pipefail, so explicit error handling and state verification are essential.

How do I test a migration script before committing?

Test against a temporary home directory using HOME=$(mktemp -d) bash -euo pipefail migrations/<filename>.sh. To re-test after the migration has already run, delete the corresponding marker file from ~/.local/state/omarchy/migrations/ and run omarchy-migrate again. This validates idempotency and ensures the script handles both initial and repeat executions correctly.

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 →