How to Write and Run Migrations in the Omarchy Migration System

Omarchy applies one-time configuration changes and repairs through timestamp-ordered shell scripts in the migrations/ directory, executing them per-user via omarchy-migrate and tracking completion in ~/.local/state/omarchy/migrations/.

Omarchy is an opinionated Arch Linux distribution that manages system state through declarative configuration and automated update workflows. When you need to apply user-specific repairs or configuration changes that pacman cannot handle, you write and run migrations in the Omarchy migration system to ensure consistent state across all user accounts on a machine.

Where Migration Scripts Live

All migration scripts reside in the migrations/ directory at the repository root, using the pattern migrations/*.sh. According to the authoring guide in agents/skills/migrations.md, the system scans this directory to discover pending changes.

Omarchy executes migrations on a per-user basis, recording completion markers in ~/.local/state/omarchy/migrations/<migration-filename>. Because each user maintains their own state directory, every account on a machine receives the opportunity to run pending migrations independently, even if another user triggered the system update.

How Migrations Are Triggered

The Omarchy migration system runs through three distinct mechanisms defined in the source:

  1. omarchy update – The main update command automatically invokes omarchy-migrate after package installation completes.
  2. omarchy-migrate-notify.service – A systemd user service started at graphical login that checks for pending migrations and displays a notification if any exist.
  3. Manual execution – Users can run omarchy-migrate directly from the terminal at any time.

Scripts process in timestamp order (based on filename), and a failing migration halts the queue to prevent cascading errors.

Migration Script Requirements

When you write migrations for the Omarchy migration system, you must adhere to strict conventions to ensure safe, repeatable execution:

Property Requirement
Permissions 0644 (read-only; the runner executes via bash -euo pipefail)
Shebang None – the runner supplies the interpreter with strict error handling
Idempotence Must detect existing state and exit early; otherwise it runs repeatedly for each user
Ordering Processed in timestamp order; failures block subsequent migrations
Helpers Use Omarchy helpers like omarchy-cmd-present and omarchy-pkg-add for common tasks

The strict bash -euo pipefail enforcement means scripts exit immediately on errors, undefined variables, or pipeline failures, preventing partial state corruption.

Creating a New Migration Script

Use the provided scaffolding tool to generate a properly formatted migration skeleton:


# Create a new migration without opening an editor

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

This generates a file in migrations/ with a timestamp prefix (e.g., 1785095882.sh). The skeleton follows the pattern shown in migrations/1785095882.sh, which demonstrates proper idempotence checks:

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        # already fixed → no-op

ln -sfn "$current_relative_target" "$theme_link"

Always validate existing state before making changes. Since the system runs migrations for every user, assume the target environment might already contain the fix.

Running Migrations Manually

While omarchy update and graphical login services handle most execution scenarios, developers and power users can interact directly with the migration system:


# List pending migrations (exits 0 if any are pending)

omarchy-migrate --pending

# Execute all pending migrations for the current user

omarchy-migrate

The --pending flag is particularly useful for CI/CD pipelines or dotfiles management scripts that need to detect whether state changes are required before proceeding.

Testing and Debugging Migrations

Test migrations locally against a temporary home directory to avoid polluting your actual configuration:


# Run a specific migration in isolation

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

If a migration fails during development and you need to re-run it after fixing the code, remove the completion marker before invoking the migration again:


# Remove the state marker for a specific migration

rm ~/.local/state/omarchy/migrations/1785095882.sh

# Re-run the migration

omarchy-migrate

This manual cleanup is necessary because the system tracks completion via empty marker files in the user's state directory, not by hashing script contents.

Summary

  • Store migration scripts in migrations/*.sh with 0644 permissions and no shebang.
  • The runner executes scripts with bash -euo pipefail in timestamp order, halting on the first failure.
  • Write idempotent scripts that check for existing state and exit cleanly if no action is needed.
  • Track completion per-user in ~/.local/state/omarchy/migrations/; every account runs pending migrations independently.
  • Invoke manually with omarchy-migrate or check status with omarchy-migrate --pending.
  • Scaffold new migrations using omarchy-dev-add-migration --no-edit to ensure proper formatting.

Frequently Asked Questions

What happens if a migration script fails?

When a migration exits with a non-zero status, the omarchy-migrate command halts immediately and does not process subsequent migrations in the queue. This prevents dependent changes from running against a broken state. You must fix the failing script, clear its completion marker from ~/.local/state/omarchy/migrations/, and re-run omarchy-migrate to continue.

Can I run migrations for another user account?

No. The Omarchy migration system is explicitly designed to run as the current user, writing completion markers to that user's ~/.local/state/ directory. There is no built-in mechanism to execute migrations on behalf of another account; each user must run omarchy-migrate individually, typically triggered automatically at their next graphical login via omarchy-migrate-notify.service.

How do I check which migrations are pending without running them?

Use the --pending flag to list outstanding migrations without executing them:

omarchy-migrate --pending

This command returns exit code 0 if migrations are pending and exit code 1 if the user is up to date, making it suitable for scripting and conditional logic in shell profiles.

Do I need to make migration scripts executable with chmod?

No. Migration scripts must remain at 0644 permissions (read-only for owner, read-only for group, read for others). The omarchy-migrate binary invokes the scripts explicitly via bash -euo pipefail <filename>, so the execute bit is neither required nor desired. Setting executable permissions may actually violate Omarchy's security model for the migrations directory.

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 →