How to Create a New Migration Script in Omarchy: A Complete Guide

Use omarchy-dev-add-migration --no-edit to scaffold a new migration script in the migrations/ directory, then implement idempotent shell commands that check for existing state before making changes.

Omarchy's migration system provides a robust mechanism for applying one-time configuration changes and repairs that cannot be expressed as ordinary package updates. Migration scripts in Omarchy are idempotent, per-user, and self-contained—ensuring safe, repeatable execution across all user accounts on a system. This guide walks through creating, implementing, and testing migration scripts based on the actual source code in the omacom/omarchy repository.

Understanding Omarchy Migrations

Before writing your first migration, you need to understand how the system executes and tracks these scripts.

What Migrations Are For

Migrations perform one-time repairs or configuration changes. Unlike package updates, which are stateless and version-controlled externally, migrations handle transitional states—for example, migrating user configuration files to a new format or adding required default settings that packages cannot provide.

Per-User Execution Model

The migration system is deliberately per-user rather than system-wide. Each migration's completion marker is stored in:


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

This design ensures every logged-in user runs every pending migration independently. Even if one user applies a machine-wide fix, other users still execute the same migration to update their personal configurations.

Migrations run as the current Omarchy user, not as root. Privileged operations must invoke helper commands like omarchy-pkg-add or prompt for elevation—the migration runner itself never performs privileged actions directly.

When Migrations Execute

Migrations run in three contexts:

  1. During omarchy update — After package upgrades complete, omarchy-migrate runs all pending migrations for the current user
  2. At graphical login — The omarchy-migrate-notify.service systemd unit triggers omarchy-migrate --pending; if migrations are pending, a notification opens a terminal to run omarchy-migrate
  3. Manual invocation — Users can run omarchy-migrate at any time; completed migrations are automatically skipped

Creating a New Migration Script

Omarchy provides a dedicated helper command to scaffold migration files with the correct naming convention and permissions.

Step 1: Generate the Migration File

Run the developer helper to create a new migration:

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

This command generates an empty file under migrations/ using the current Unix timestamp as the filename (for example, migrations/1781158082.sh). The --no-edit flag skips opening an editor immediately; omit it if you prefer to edit right away.

Step 2: Implement the Migration

Edit the generated file to implement your changes. Follow these mandatory requirements:

Requirement Specification
Permissions 0644 (readable, not executable)—the runner invokes with bash -euo pipefail
Shebang None—the runner supplies the interpreter
Header Leading echo describing the migration's purpose
Idempotency Guard all state-changing commands with existence checks

Here is a template migration script:

echo "Ensuring required environment variable is set in ~/.bashrc"

# Idempotent check: skip if already present

if grep -q "OMARCHY_FEATURE_ENABLED=1" ~/.bashrc; then
    echo "Already configured, skipping"
    exit 0
fi

# Apply the change

echo 'export OMARCHY_FEATURE_ENABLED=1' >> ~/.bashrc
echo "Configuration added successfully"

Step 3: Use Helper Commands

For package-related actions, use Omarchy's wrapper commands rather than direct pacman calls:

  • omarchy-cmd-present <command> — Check if a command exists
  • omarchy-cmd-missing <command> — Check if a command is absent
  • omarchy-pkg-add <package> — Install a package with proper privilege handling
  • omarchy-pkg-drop <package> — Remove a package

These helpers ensure consistent behavior and proper elevation handling.

Testing Your Migration

Never test migrations directly in your live environment. Use isolated testing and controlled re-execution.

Test in Temporary Environment

Run the script against a temporary home directory to verify it does not depend on your specific user configuration:

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

This command simulates a fresh user environment and catches assumptions about existing files or settings.

Re-Run Locally for Development

To test a migration repeatedly during development, remove its completion marker and invoke the migrator:

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

The migration will execute again because its marker is absent.

Critical Implementation Rules

Follow these rules to ensure your migrations integrate correctly with the Omarchy system:

  • Do not restart the Omarchy shell — The omarchy update command handles restarts after all migrations complete
  • Make every operation idempotent — Check for existing state before making changes; migrations may run multiple times if markers are cleared
  • Keep permissions at 0644 — Executable permissions are unnecessary and discouraged
  • Avoid privileged operations in the script — Delegate to helper commands that handle elevation properly

Key Files and Commands Reference

Path Purpose
agents/skills/migrations.md Complete migration guidelines and troubleshooting
migrations/*.sh Migration script storage (e.g., migrations/1781158082.sh)
bin/omarchy-dev-add-migration Scaffold new migration files
bin/omarchy-migrate Execute pending migrations
bin/omarchy-migrate-notify Login-time pending migration checker
default/systemd/user/omarchy-migrate-notify.service Systemd unit for graphical login notifications

Summary

  • Create migration scripts with omarchy-dev-add-migration --no-edit
  • Name files using Unix timestamps in the migrations/ directory
  • Implement idempotent shell code with no shebang and proper header echo
  • Test in temporary HOME directories and re-run by deleting completion markers
  • Execute automatically during updates, at login via systemd, or manually with omarchy-migrate

Frequently Asked Questions

What happens if two users run the same migration?

Each user maintains independent completion markers in ~/.local/state/omarchy/migrations/. Both users execute the same migration script independently, which is the intended behavior—this ensures per-user configurations are properly updated even when machine-wide changes are already complete.

Can I use sudo directly in a migration script?

No. Migrations run as the unprivileged Omarchy user. For package installations or other privileged operations, use helper commands like omarchy-pkg-add or omarchy-pkg-drop. These commands handle privilege elevation safely and consistently.

Why can't I make my migration script executable?

The migration runner explicitly invokes scripts with bash -euo pipefail, so executable permissions are unnecessary. The 0644 permission requirement prevents accidental direct execution and ensures consistent execution through the managed runner.

How do I check if my migration will run on a specific system?

Run omarchy-migrate --pending to list migrations that have not yet been applied to the current user. This command checks for the presence of completion markers in ~/.local/state/omarchy/migrations/ without executing any scripts.

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 →