Files
arthur-os/docs/migrations.md
T
2026-06-04 18:38:25 -04:00

7.0 KiB

Omarchy migrations

Omarchy migrations are one-time repair scripts for existing installs. They are used when a package update needs to change state that pacman cannot safely own by itself.

The two migration scopes

Omarchy migrations are split by execution context:

migrations/system/*.sh
migrations/user/*.sh

System migrations

System migrations run as root and must be noninteractive.

Use system migrations for machine-wide state, for example:

  • /etc drop-ins or legacy config cleanup
  • /boot / bootloader cleanup
  • systemd system units or service state
  • hardware quirks
  • package-owned layout transitions

Note that it's not necessary to write a migration for any file that will be package-owned.

State lives in:

/var/lib/omarchy/migrations/system/<migration filename>

User migrations

User migrations run as the current graphical/login user and may be interactive.

Use user migrations for per-user or session-aware state, for example:

  • ~/.config changes
  • ~/.local state
  • user systemd units
  • browser/editor preferences
  • DBus/session-dependent updates
  • prompts that need to happen in a visible terminal

State lives in:

~/.local/state/omarchy/migrations/user/<migration filename>

A user migration is pending for a given user only when the script exists under /usr/share/omarchy/migrations/user/ and that user's matching state file is missing.

There is no global root-owned “user migrations required” queue.

When migrations run

During omarchy update

omarchy update is the normal update path. It runs package updates, then:

omarchy-migrate
omarchy-hook post-update

omarchy-migrate:

  1. waits for any active pacman transaction to finish
  2. runs pending system migrations
  3. runs pending user migrations for the current user

System migrations usually already ran during the pacman transaction via the omarchy package post_upgrade(), but the second check is intentional and idempotent.

During direct pacman updates

Raw sudo pacman -Syu is guarded. Users should normally run:

omarchy update

If a user explicitly bypasses the guard, the omarchy package still runs system migrations from post_upgrade():

omarchy-migrate-system

User migrations are never run by pacman. Instead, the user session watches the packaged user migration directory and runs a notifier. The notifier checks:

omarchy-migrate --pending user

If that user has pending user migrations, it shows a notification that opens a terminal for:

omarchy-migrate

The notifier never runs user migrations silently in the background.

Manually

Users can safely run:

omarchy-migrate

at any time. Already-completed migrations are skipped.

Inspecting pending migrations

Use:

omarchy-migrate --pending
omarchy-migrate --pending system
omarchy-migrate --pending user

Exit behavior:

  • 0 — one or more matching migrations are pending
  • non-zero — no matching migrations are pending

Output is scope-prefixed:

system/100-system.sh
user/200-user.sh

The lower-level runners also support --pending and print filenames without the scope prefix:

omarchy-migrate-system --pending
omarchy-migrate-user --pending

Prefer omarchy-migrate --pending ... in user-facing tools and watchers.

Writing migrations

Create a migration with:

omarchy-dev-add-migration system --no-edit
omarchy-dev-add-migration user --no-edit

Choose the scope based on the state being changed, not on convenience.

File format

Migrations are plain shell files:

  • filename: unix timestamp, generated by omarchy-dev-add-migration
  • mode: 0644
  • no shebang
  • start with an echo describing the migration
  • use $OMARCHY_PATH for Omarchy-owned files
  • should be idempotent
  • should be as simple as possible to accomplish the task

Example system migration:

echo "Remove legacy Limine config"

rm -f /boot/EFI/limine/limine.conf

Example user migration:

echo "Refresh user app launchers"

omarchy-refresh-applications

Execution model

Migration runners execute scripts with strict shell settings:

bash -euo pipefail <migration>

$OMARCHY_PATH is exported into the migration process.

A migration is marked complete only after the script exits successfully. If it fails, the marker is not written and the migration will be retried later.

Because failed migrations can be retried after doing partial work, migrations must be idempotent. Prefer operations that are safe to run more than once:

mkdir -p ~/.config/example
rm -f ~/.config/example/legacy.conf
install -Dm644 "$OMARCHY_PATH/default/example.conf" ~/.config/example/example.conf
systemctl --user enable --now some-unit.service || true

Use || true only when a failure is genuinely acceptable.

What belongs in a migration?

Good migration uses:

  • deleting or moving legacy files that block the packaged layout
  • marking or enabling new service state
  • migrating old config paths to new config paths
  • repairing known bad state from an earlier Omarchy release
  • one-time compatibility for existing installs

Bad migration uses:

  • fresh-install setup that belongs in install/ or /etc/skel
  • package installation that belongs in package dependencies or package lists
  • recurring maintenance
  • arbitrary cleanup that should be a user command
  • pre-4 upgrade work that belongs in omarchy-upgrade-to-4
  • hidden user/session work from pacman

Fresh installs and new users

Fresh users created from the packaged layout should not have to run historical user migrations. The package seeds user migration markers into /etc/skel so new users start with shipped user migrations marked complete.

The ISO/finalization path also marks shipped user migrations complete for the freshly-created install user when running:

omarchy-finalize-user --first-install

Existing users keep their own migration state and run only migrations whose state files are missing.

Troubleshooting

See what is pending

omarchy-migrate --pending

Retry failed migrations

Fix the underlying problem, then run:

omarchy-migrate

A failed migration is not marked complete, so it will retry.

Re-run a completed migration manually

Remove its marker, then run the migration command again:

rm ~/.local/state/omarchy/migrations/user/<migration>.sh
omarchy-migrate

For system migrations:

sudo rm /var/lib/omarchy/migrations/system/<migration>.sh
omarchy-migrate

Be careful: migrations should be idempotent, but manually removing markers is an advanced troubleshooting step.

Agent checklist

Before adding a migration:

  1. Is this a one-time repair for existing installs?
  2. Is it system state or user/session state?
  3. Can it run safely more than once?
  4. Will it fail loudly if the important work fails?
  5. Does it avoid hidden interactive work from pacman?
  6. Does it belong in omarchy-upgrade-to-4 instead?
  7. Did you add or update tests if behavior changed?

If the answer to any of these is unclear, stop and ask before adding the migration.