# Understanding the Omarchy Migration Structure: A Per-User Shell-Based Framework

> Explore the Omarchy migration structure, a per-user shell-based framework. Learn how timestamped scripts and marker files ensure safe, idempotent environment updates in omarchy.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: architecture
- Published: 2026-09-13

---

**Omarchy's migration system uses timestamp-prefixed shell scripts stored in `migrations/` that execute in ascending order per user, tracking completion via zero-byte marker files in `~/.local/state/omarchy/migrations/` to ensure idempotent, safe environment evolution across updates.**

The `omacom/omarchy` project implements a robust, file-based migration framework designed to evolve user environments safely without requiring privileged access. Unlike traditional database migration systems, Omarchy's approach treats shell scripts as versioned artifacts that run per-user, ensuring consistent configuration states across package updates and system changes.

## Migration Directory Layout and Naming Conventions

All migration scripts live in the repository under the `migrations/` directory and follow a strict naming convention that determines execution order.

### Source Location and Installation

The source files reside in `migrations/**/*.sh` within the repository. At runtime, these scripts install to `/usr/share/omarchy/migrations/`, making them available system-wide while keeping the execution model user-centric. This separation allows administrators to ship updates via the repository while maintaining per-user state tracking.

### Timestamp-Based Ordering

Scripts must use a Unix-timestamp prefix to define global execution order. For example, [`migrations/1784960000.sh`](https://github.com/omacom/omarchy/blob/main/migrations/1784960000.sh) runs before [`migrations/1784960001.sh`](https://github.com/omacom/omarchy/blob/main/migrations/1784960001.sh). The `omarchy-migrate` command scans the installed directory and sorts scripts by filename to determine the sequence, ensuring migrations run strictly in ascending chronological order regardless of installation timing.

## How the omarchy-migrate Command Executes

The core migration logic resides in `bin/omarchy-migrate`, which orchestrates discovery, execution, and state management automatically.

### Execution Flow

The command performs three critical steps:

1. **Discovery**: Scans `/usr/share/omarchy/migrations/` for all `*.sh` files
2. **Comparison**: Checks each script's basename against the user's state directory at `~/.local/state/omarchy/migrations/`
3. **Execution**: Runs pending scripts with `bash -euo pipefail` to enable strict error handling

This process triggers automatically after package updates and on user login via the `omarchy-migrate-notify.service` systemd unit, ensuring environments stay current without manual intervention.

### Marker File Creation

Upon successful completion, the system creates a zero-byte marker file in the state directory matching the script's basename. For example, completing [`1784960000.sh`](https://github.com/omacom/omarchy/blob/main/1784960000.sh) creates `~/.local/state/omarchy/migrations/1784960000.sh`. These markers persist across sessions, preventing re-execution of already-applied migrations while allowing new users to run the full sequence on first login.

## Per-User State Tracking and Idempotency

The migration model explicitly requires every script to be idempotent, as documented in [`agents/skills/migrations.md`](https://github.com/omacom/omarchy/blob/main/agents/skills/migrations.md). This design ensures safety when re-running partially completed sequences or when users manually trigger migrations multiple times.

### Writing Idempotent Scripts

Each migration must verify state before modifying the system. The recommended pattern checks for the existence of the marker file or the desired end state before executing privileged operations:

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

# Guard against re-running

[[ -e "$HOME/.local/state/omarchy/migrations/$(basename "$0")" ]] && exit 0

# Example: Install package only if missing

if ! pacman -Qs lsp-plugins-lv2 > /dev/null; then
  omarchy-pkg-add lsp-plugins-lv2
fi

```

### Privilege Delegation

Migrations run as the regular user account, never as root. Any operation requiring elevated privileges must delegate to helper commands such as `omarchy-pkg-add`, `sudo`, or `omarchy-cmd-present`. This constraint prevents accidental system-wide damage while allowing necessary configuration changes through controlled interfaces.

## Error Handling and Execution Safety

The `omarchy-migrate` command implements strict failure semantics to prevent partial migration states.

### Queue Stopping on Failure

If any migration exits with a non-zero status, the entire queue halts immediately. According to the implementation in `bin/omarchy-migrate` at line 129, the process aborts without marking subsequent migrations as completed. This guarantees that a broken migration never leaves the system in a partially-migrated state where later scripts depend on earlier, failed changes.

### Strict Shell Options

All migrations execute with `bash -euo pipefail`, which causes immediate termination on:
- Non-zero exit codes (`-e`)
- Unset variables (`-u`)
- Pipeline failures (`-o pipefail`)

These options enforce defensive programming practices, catching errors early before they can corrupt user configuration.

## Creating Custom Migrations

Developers and advanced users can extend Omarchy by adding new scripts to the `migrations/` directory following the established conventions.

### Template Structure

Save new migrations with the current timestamp to ensure proper ordering:

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

# Template for migrations/$(date +%s).sh

# All migrations must be idempotent.

# Check if already applied

state_dir="$HOME/.local/state/omarchy/migrations"
[[ -e "$state_dir/$(basename "$0")" ]] && exit 0

# Perform user-land configuration

conf="/etc/vconsole.conf"
if [[ -f "$conf" && "$(grep -E '^KEYMAP=' "$conf")" != 'KEYMAP=us' ]]; then
  sudo sed -i 's/^KEYMAP=.*/KEYMAP=us/' "$conf"
fi

# The migration runner creates the marker file automatically on success

```

### Testing Migrations Locally

Use the `omarchy-migrate` command with flags to preview and test changes:

```bash

# Show pending migrations without executing

omarchy-migrate --pending

# Execute all pending migrations

omarchy-migrate

```

## Summary

- **Timestamp Naming**: Use Unix timestamps (e.g., [`1784960000.sh`](https://github.com/omacom/omarchy/blob/main/1784960000.sh)) in `migrations/` to define execution order
- **Per-User State**: Marker files in `~/.local/state/omarchy/migrations/` track completion per account
- **Automatic Execution**: The `omarchy-migrate` command runs via systemd service after updates and login
- **Idempotency Required**: Scripts must verify state before changing; privileged work delegates to helpers
- **Strict Failure Handling**: Non-zero exits abort the queue immediately, preventing partial states

## Frequently Asked Questions

### How do I create a new migration in Omarchy?

Create a shell script in the `migrations/` directory with a Unix timestamp prefix (e.g., `migrations/$(date +%s).sh`). The script must be idempotent, check for existing marker files, and delegate privileged operations to commands like `omarchy-pkg-add` or `sudo`. Follow the guidelines in [`agents/skills/migrations.md`](https://github.com/omacom/omarchy/blob/main/agents/skills/migrations.md) to ensure compatibility with the execution framework.

### Where does Omarchy store migration state?

Omarchy stores state in the user-specific directory `~/.local/state/omarchy/migrations/`. Each completed migration creates a zero-byte marker file matching the script's basename (e.g., [`1784960000.sh`](https://github.com/omacom/omarchy/blob/main/1784960000.sh)). The `omarchy-migrate` command compares these markers against scripts in `/usr/share/omarchy/migrations/` to determine which migrations remain pending.

### What happens if a migration fails?

If a migration exits with a non-zero status, `omarchy-migrate` immediately aborts the entire queue and does not mark subsequent migrations as completed. This prevents the system from entering a partially-migrated state where later scripts might depend on failed earlier operations. Fix the failing script and re-run `omarchy-migrate` to resume from the failure point.

### Do migrations run as root or regular user?

Migrations run as the regular user account, not root. This security model requires that any privileged system changes use delegation commands such as `omarchy-pkg-add`, `sudo`, or `omarchy-cmd-present` rather than direct root execution. The design ensures that user-specific configurations remain isolated while still allowing necessary system modifications through controlled interfaces.