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:
- Pacman completes package installation
omarchy-migrateruns all pending migrations for the current useromarchy-hook post-updateexecutes 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
echodescribing 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 updatehandles 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/*.shthat run after Pacman updates to fix user-level configuration. - The
omarchy-migratecommand (inbin/omarchy-migrate) executes scripts withbash -euo pipefailand tracks completion via marker files in~/.local/state/omarchy/migrations/. - Migrations trigger automatically during
omarchy update, at graphical login viaomarchy-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-editto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →