# How to Write and Run Migrations in the Omarchy Migration System

> Learn to write and run Omarchy migrations using timestamped shell scripts. Effortlessly manage configuration changes and repairs with this powerful system.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: how-to-guide
- Published: 2026-09-10

---

**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`](https://github.com/omacom/omarchy/blob/main/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:

```bash

# 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`](https://github.com/omacom/omarchy/blob/main/1785095882.sh)). The skeleton follows the pattern shown in [`migrations/1785095882.sh`](https://github.com/omacom/omarchy/blob/main/migrations/1785095882.sh), which demonstrates proper idempotence checks:

```bash
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:

```bash

# 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:

```bash

# 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:

```bash

# 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:

```bash
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.