# How Omarchy Handles Migrations: A Technical Guide to Post-Upgrade Configuration Changes

> Discover how Omarchy handles migrations with self-contained Bash scripts and idempotent updates coordinated via Pacman transactions. Learn the technical details of the omarchy-migrate command.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: how-to-guide
- Published: 2026-08-18

---

**Omarchy manages migrations through self-contained Bash scripts stored in `migrations/` and executed via the `omarchy-migrate` command, using per-user state markers to ensure idempotent, incremental updates coordinated with Pacman transactions.**

Migrations in Omarchy are post-upgrade configuration adjustments that bridge the gap between package installation and user-level setup. Unlike traditional package managers that run hooks automatically, Omarchy's migration system is **per-user, opt-in, and stateful** — designed for an Arch-based environment where multiple users may need individual attention after system updates.

## Migration Discovery and State Tracking

The `omarchy-migrate` command drives the entire workflow. Its `migration_entries` function scans `$OMARCHY_PATH/migrations` (default: `/usr/share/omarchy/migrations`) for any file matching `*.sh` [source: `quattro/bin/omarchy-migrate#L35-L45`](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate#L35-L45).

State tracking happens per user through marker files:

```bash
STATE_DIR="${OMARCHY_MIGRATION_STATE:-$HOME/.local/state}/omarchy/migrations"

```

This variable [defined at lines 31-33 of `omarchy-migrate`](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate#L31-L33) ensures that **each user maintains their own migration history**. When a migration succeeds, a corresponding marker file is created in this directory, preventing re-execution.

## Checking for Pending Migrations

Before running anything, users can inspect what needs work. The `--pending` flag (also `--check`) lists migrations missing their marker files and exits `0` if any exist, `1` otherwise.

The `pending_migrations` function implements this by filtering `migration_entries` against existing markers [source: `quattro/bin/omarchy-migrate#L47-L55`](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate#L47-L55):

```bash

# List migrations waiting for the current user

omarchy-migrate --pending

```

This pattern enables safe automation — scripts can check status without side effects.

## Pacman Coordination and Execution

Migration timing matters. The `wait_for_pacman_transaction` function [lines 68-80 of `omarchy-migrate`](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate#L68-L80) polls `/var/lib/pacman/db.lck` to ensure no Pacman transaction is active before proceeding. This guarantees migrations run **after** packages are fully installed, when user sessions and DBus are stable.

The execution loop [starting at line 88](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate#L88-L97) handles each pending migration:

- Prints a colored banner identifying the migration
- Executes the script under `bash -euo pipefail` (strict error handling)
- Creates the marker file on successful exit

```bash

# Run all pending migrations for the current user

omarchy-migrate

```

## Migration Script Structure

Migration scripts are ordinary Bash files with two constraints: they must be **idempotent** and **self-contained**. Here's a representative example from the `migrations/` directory:

```bash
#!/usr/bin/env bash

# Example: remove deprecated tmux configuration

CONFIG="${HOME}/.config/tmux.conf"
sed -i '/old-feature-flag/d' "$CONFIG"

```

The script runs in the user's environment with full `$HOME` access. After success, Omarchy creates `~/.local/state/omarchy/migrations/1785189600.sh` — the filename matches the migration script, making state inspection trivial.

## Desktop Notifications for Migrations

Users who skip `omarchy update` still get reminded. The `omarchy-migrate-notify.service` systemd user service runs `omarchy-migrate --pending` on login. When pending migrations exist, it displays a toast notification whose click action launches the full migration command [described in [`docs/update-process.md`](https://github.com/basecamp/omarchy/blob/main/docs/update-process.md)](https://github.com/basecamp/omarchy/blob/quattro/docs/update-process.md#L59-L66).

The helper script `omarchy-migrate-notify` bridges the gap between background checking and user intervention [source: `quattro/bin/omarchy-migrate-notify`](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate-notify).

## Key Design Principles

Omarchy's migration system embodies several deliberate architectural choices:

- **Per-user scope** — Machine-wide repairs can coexist with personal configuration changes; each user runs migrations independently
- **Incremental application** — Missing markers mean pending work; present markers mean skip
- **Package manager coordination** — Pacman lockfile polling prevents race conditions during active upgrades
- **Minimal privilege escalation** — Scripts request sudo only when absolutely necessary, operating primarily in user space

These principles make Omarchy migrations safe for unattended execution while flexible enough for complex post-upgrade repairs.

## Summary

- **Command**: `omarchy-migrate` discovers, checks, and runs migration scripts from `/usr/share/omarchy/migrations`
- **State**: Per-user marker files in `~/.local/state/omarchy/migrations/` track completion
- **Safety**: Pacman lockfile polling in `wait_for_pacman_transaction` ensures post-upgrade execution
- **Notification**: `omarchy-migrate-notify.service` reminds users of pending work via desktop toast
- **Idempotency**: Migration scripts are expected to be safe for repeated execution

## Frequently Asked Questions

### Where are migration scripts stored in Omarchy?

Migration scripts live in `migrations/` under the Omarchy installation path, defaulting to `/usr/share/omarchy/migrations/`. The `migration_entries` function in `bin/omarchy-migrate` [scans this directory](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate#L35-L45) for `*.sh` files.

### Can I run migrations for another user?

No. The migration state directory defaults to `${XDG_STATE_HOME:-$HOME/.local/state}/omarchy/migrations`, making migrations inherently per-user. This design intentionally isolates user-level configuration changes from system-wide operations.

### What happens if a migration script fails?

The execution loop in `omarchy-migrate` runs scripts with `bash -euo pipefail`, meaning any non-zero exit stops that migration. Failed migrations do **not** create marker files, so they will be re-attempted on the next `omarchy-migrate` invocation.

### How does Omarchy know when Pacman is finished upgrading?

The `wait_for_pacman_transaction` function polls for `/var/lib/pacman/db.lck` every second until the lockfile disappears [source: lines 68-80](https://github.com/basecamp/omarchy/blob/quattro/bin/omarchy-migrate#L68-L80). This ensures migrations never run during an active package transaction.