# How Omarchy Migrations Run Per‑User: Complete Technical Guide

> Discover how Omarchy migrations run per-user by storing markers in user home directories and executing scripts under the invoking user's UID for granular privilege escalation.

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

---

**Omarchy migrations run per‑user by storing completion markers in each user's home directory (`~/.local/state/omarchy/migrations`) and executing scripts under the invoking user's UID, allowing granular privilege escalation only when needed.**

When you upgrade Omarchy, the system must apply one‑time fixes, policy updates, and configuration changes without requiring manual intervention. The migration system achieves this by running scripts as the user who invokes the upgrade, tracking completion state locally, and deferring privileged operations to explicit `sudo` calls within each script.

## How the Per‑User Migration System Works

The `omarchy-migrate` command in `bin/omarchy-migrate` implements a stateful, user‑scoped execution model. Rather than applying migrations system‑wide, it treats each user as an independent migration target.

### Discovery and State Tracking

`omarchy-migrate` scans `$OMARCHY_PATH/migrations/*.sh` to find all available migration scripts. It compares these against completion markers stored in:

```bash
"$HOME/.local/state/omarchy/migrations/"

```

Each marker file matches its corresponding script name exactly (e.g., [`1788619462.sh`](https://github.com/omacom/omarchy/blob/main/1788619462.sh)). This design ensures migrations run **once per user** regardless of how many times the command executes.

### Execution Model

Scripts execute with `bash -euo pipefail`, enforcing strict error handling. The runner sources each pending script under the current user's UID, meaning:

- **Unprivileged operations** proceed without elevation
- **Privileged operations** require explicit `sudo` calls within the script
- **Failure handling** aborts immediately on non‑zero exit, leaving the migration unmarked for retry

This contrasts with system‑wide migration systems that typically require root throughout or complex privilege separation logic.

### Notification Integration

After processing completes—whether successfully or with pending migrations remaining—`omarchy-migrate` triggers `omarchy-migrate-notify.service`. This systemd user service displays a desktop alert such as **"Pending Omarchy Migrations"** defined in `default/systemd/user/omarchy-migrate-notify.service`.

## Running Migrations Manually

### Apply All Pending Migrations

```bash
$ omarchy-migrate

```

This discovers unmarked scripts, executes them sequentially, and writes completion markers for successful runs.

### Preview Pending Migrations

```bash
$ omarchy-migrate --pending

# → 1788619462.sh

# → 1788596255.sh

```

This lists scripts awaiting execution without modifying state.

### Debug a Specific Migration

```bash

# Execute directly (omarchy-migrate manages markers on successful completion)

$ bash "$OMARCHY_PATH/migrations/1788619462.sh"

```

Direct execution bypasses the runner's state management—you must handle markers manually in this mode.

## Key Files in the Migration System

| Path | Purpose |
|------|---------|
| `bin/omarchy-migrate` | Main discovery, execution, and state management runner |
| `migrations/*.sh` | Individual migration scripts (timestamped filenames like [`1788619462.sh`](https://github.com/omacom/omarchy/blob/main/1788619462.sh)) |
| `default/systemd/user/omarchy-migrate-notify.service` | User service for desktop notifications post-migration |
| [`test/shell.d/migrate-scope-test.sh`](https://github.com/omacom/omarchy/blob/main/test/shell.d/migrate-scope-test.sh) | Validates per‑user isolation and state separation |
| [`test/shell.d/migrate-wrapper-test.sh`](https://github.com/omacom/omarchy/blob/main/test/shell.d/migrate-wrapper-test.sh) | Tests runner functionality: pending listings, notifications, exit codes |
| [`docs/update-process.md`](https://github.com/omacom/omarchy/blob/main/docs/update-process.md) | Documents migration integration within upgrade flows |

## Integration with Upgrade Commands

Migration execution occurs automatically as a final step in upgrade workflows:

- `omarchy-upgrade-to-quattro`
- `omarchy-update`
- Other package management wrappers

This sequencing ensures new code is present before migrations attempt to modify configuration or apply fixes that depend on updated assets.

## Per‑User Design Benefits

The state directory approach in `~/.local/state/omarchy/migrations/` provides concrete protections:

- **Isolation**: Each user's migration progress is independent; one user's failure or delay doesn't block others
- **Retry safety**: Failed migrations remain unmarked, enabling partial recovery without re‑executing successes
- **Least privilege**: Scripts run unprivileged by default, escalating only for operations explicitly requiring `sudo`

The test suite in [`test/shell.d/migrate-scope-test.sh`](https://github.com/omacom/omarchy/blob/main/test/shell.d/migrate-scope-test.sh) verifies this isolation by confirming that completion markers from one user don't satisfy migration requirements for another.

## Summary

- **Omarchy migrations run per‑user** via state markers in `~/.local/state/omarchy/migrations/`
- **`omarchy-migrate`** discovers, executes, and records migrations from `migrations/*.sh`
- **Execution uses `bash -euo pipefail`** under the invoking user's UID with explicit privilege escalation
- **Failed migrations retry automatically** on next invocation; successes never re‑execute
- **Desktop notifications** trigger via `omarchy-migrate-notify.service` after each run
- **Upgrade commands** call migrations automatically as their final step

## Frequently Asked Questions

### Where does Omarchy store per‑user migration state?

Omarchy writes completion markers to `"$HOME/.local/state/omarchy/migrations/"` as files matching their corresponding script names. This user‑writable directory ensures cross‑user isolation without requiring system‑level coordination.

### What happens if a migration script fails?

The runner aborts immediately on non‑zero exit due to `set -e`. No marker file is written, so the next `omarchy-migrate` invocation will retry that specific script while skipping already‑successful migrations.

### Can I run migrations for another user?

No—`omarchy-migrate` operates exclusively under the current user's UID and writes state to that user's home directory. Administrators must either run migrations as each target user or design system‑level automation that invokes the command within user contexts.

### How do I know if migrations are pending after an upgrade?

Run `omarchy-migrate --pending` to list unexecuted scripts, or check for the desktop notification triggered by `omarchy-migrate-notify.service`. The notification appears automatically when the upgrade process invokes `omarchy-migrate` at completion.