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:
omarchy update– The main update command automatically invokesomarchy-migrateafter package installation completes.omarchy-migrate-notify.service– A systemd user service started at graphical login that checks for pending migrations and displays a notification if any exist.- Manual execution – Users can run
omarchy-migratedirectly 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/*.shwith0644permissions and no shebang. - The runner executes scripts with
bash -euo pipefailin 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-migrateor check status withomarchy-migrate --pending. - Scaffold new migrations using
omarchy-dev-add-migration --no-editto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →