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

> Learn how to create a new migration script in Omarchy with this guide. Scaffold and implement idempotent shell commands efficiently.

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

---

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

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

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

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

```bash
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`](https://github.com/omacom/omarchy/blob/main/agents/skills/migrations.md) | Complete migration guidelines and troubleshooting |
| `migrations/*.sh` | Migration script storage (e.g., [`migrations/1781158082.sh`](https://github.com/omacom/omarchy/blob/main/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.