Omarchy 4.0: The Distro Is Mostly a Migration Runner

· 20 min read

I opened the Omarchy repository expecting to find a theme collection with an installer attached. Version 4.0 — "Quattro" — had just landed on 14 August 2026, the release notes led with a desktop shell rewritten in Quickshell, and the screenshots looked exactly like every tiling-window-manager setup that has ever been posted to r/unixporn. Beautiful, opinionated, Arch, DHH. I assumed I knew the shape of it.

Then I noticed the repo has a migrations/ directory. Eighty shell scripts named after Unix timestamps, with a documented model for how and when they run, a per-user completion ledger, and a hard rule that every one of them must be idempotent.

That is not a ricing decision. That is someone who has been woken up at 3am by a schema change.

Omarchy

The interesting claim in Omarchy is not that Arch can be pretty. It is that a desktop Linux install is mutable state co-owned by two parties who never talk to each other — the distribution, which keeps shipping new opinions, and you, who keeps editing the files those opinions live in — and that this is a data-management problem rather than a packaging problem. Almost nobody treats it that way. Omarchy does, using the exact playbook you would use on a production database: a schema owned by the vendor, data owned by the user, migrations to move between versions, and a transaction with a rollback around every change.

That framing is worth more than the Quickshell rewrite, and it is the part that transfers to machines that will never run Arch.

The problem nobody names

Ship someone a configured desktop and you have created a fork. Their ~/.config starts as your file and ends as theirs, and from that moment you have three bad options.

You can own it — overwrite on every update, and lose the user's changes. You can abandon it — write it once at install and never touch it again, which is what most distributions do, and which means a machine installed in 2024 slowly becomes a museum of the defaults that shipped that year. Or you can declare it away: NixOS's answer, where configuration is a function of a config file and the machine is rebuilt rather than modified. That last one genuinely solves the problem, at the price of making every small tweak a build.

Omarchy takes a fourth path, and it is the one that most software actually lives on: keep the state mutable, and manage its evolution explicitly. Two layers plus a migration runner.

The layering is a load order. /usr/share/omarchy belongs to the package, ~/.config belongs to you, and — because Hyprland now takes Lua — your file calls theirs. The shipped config/hypr/hyprland.lua is, essentially, in full:

-- Omarchy's bootstrap keeps path setup out of this user config.
dofile((os.getenv("OMARCHY_PATH") or "/usr/share/omarchy") .. "/default/hypr/bootstrap.lua")

-- Load Omarchy defaults.
require("default.hypr.omarchy")

-- Put your personal overrides in these files. They're loaded after Omarchy's
-- defaults so package updates can improve the defaults without rewriting your
-- ~/.config/hypr files.
require("hypr.monitors")
require("hypr.input")
require("hypr.bindings")
require("hypr.looknfeel")
require("hypr.autostart")

Config as a program rather than a document. The consequences are pleasant: opting out is a flag, not a fork (omarchy_default_bindings = false drops every default binding; omarchy_preinstalled_bindings = false drops only the ones that launch bundled apps, keeping the window-management core), and there is room for a small DSL on top, so a window rule reads o.window("qemu", { workspace = "5" }) instead of a windowrulev2 string you have to get the regex right in.

The theory has one visible crack, and the manual points at it rather than hiding it: ~/.config/omarchy/shell.json, which configures the bar, is canonical once you write it. "There's no deep merge, so new default widgets in future Omarchy releases won't appear on your bar automatically." Touch your bar, and your bar stops receiving the future. It is a defensible call — a merged layout is a layout nobody designed — but it is precisely the place where "your file calls ours" degrades back into a fork, and it is worth knowing before you drag a widget.

Migrations are the actual idea

Layering handles config that a package can rewrite. It does nothing for the other category: state that needs repairing rather than replacing. A systemd user unit that has to be re-enabled. A preference file whose format changed under an app. A symlink that pointed somewhere that no longer exists. Packages are declarative; these are one-time fixes to a machine's history, and pacman has no vocabulary for them.

Omarchy's agents/skills/migrations.md states it flatly:

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 design is small, and every part of it reads like scar tissue. Migrations are migrations/<unix-timestamp>.sh, run through omarchy-migrate — which first waits for any in-flight pacman transaction to finish. They run as the user, not root, because most of what they repair lives in $HOME. Completion is tracked per user, in ~/.local/state/omarchy/migrations/, so on a two-account laptop the second person still gets every fix. Which is exactly why the guide makes idempotency mandatory: a machine-wide repair already applied by the first user must be detected and skipped by the second. And because an update can happen without a login, omarchy-migrate-notify.service starts with the graphical session and tells you when migrations are pending.

Nobody writes "completion state is per-user, therefore migrations must be idempotent" from first principles. You write it after a bug report from a family machine.

This is Rails' schema migrations pointed at $HOME, and I mean that as the compliment it sounds like. It is also, as far as I can tell, the mechanism that makes the rest of Omarchy's promises survivable: you can only claim "one command updates everything" for two years running if you have somewhere to put the fixes that packaging can't express.

Updating is a transaction, and the rollback is honest about its limits

Given migrations, the rest follows. omarchy update snapshots the root filesystem, updates packages from Omarchy's own Arch mirror plus the AUR, runs omarchy-migrate, then fires omarchy-hook post-update. Running pacman or yay yourself is blocked — deliberately, to keep snapshots and migrations from being bypassed.

That blocking is the interesting bit. It is the moment an opinionated distro stops being a preference and starts being a contract: you cannot have a coherent update story and also let the user step outside it. Arch users will hate this, which is fine, because they are not the audience. The release channels make the same bet from the other side — stable deliberately lags official releases by about a month so that somebody else finds the incompatibilities, with edge, rc and dev available to whoever wants to be that somebody.

Rollback is a boot-menu entry: snapshots require the Limine bootloader, you pick one by date and version at boot, then run omarchy-snapshot restore. And then the manual says the thing I did not expect it to say:

This will restore your root filesystem, but not your /home.

~/.config is untouched by a rollback too, which means rolling back to yesterday's application versions can leave them reading config files written by tomorrow's. So the snapshot is not a backup and not a full rollback; it is an undo for a broken update, and nothing more. Stating that in the manual, in one sentence, next to the feature, is worth more than the feature. Most vendors would have let you find out.

The consolidation instinct, applied twice

The Omarchy menu — one keystroke to every system action

Now the headline feature, which reads differently once you see the pattern.

A normal Wayland desktop is a federation of small daemons: Waybar for the bar, Mako for notifications, Walker for the launcher, something else for the lock screen, each with its own config dialect and its own idea of what your accent colour is. Omarchy 4.0 collapses all of it into one long-running Quickshell process in which the bar, menu, notifications, OSD, panels and lock screen are plugins. The shell/README.md gives three reasons, and they are the correct three: shared services exist once instead of once per process, summoning a panel is IPC into something already running rather than a cold start, and third-party plugins load from disk without patching Omarchy.

It is the same instinct as the update pipeline — one owner, one coherent story, fewer seams — applied to the UI instead of to state. The migration PR ran to hundreds of commits across 216 files and merged to a quattro branch rather than dev, which is at least honest signalling about the blast radius. DHH's claim is under 300 MB of runtime memory once shared libraries are accounted for; I have not measured it, and "once you account for shared libraries" is doing some work in that sentence, but one Qt process amortising what five processes duplicated is directionally obvious.

The tell that the consolidation went further than the release notes: on the quattro branch, config/hypr/ no longer contains hyprlock.conf or hypridle.conf. Locking and idle are the shell's job now.

The cost of a single coherent shell is the thing I flagged earlier — the moment you customise it, you own that layout forever. Coherence and mergeability are in tension, and Omarchy picks coherence every time. You can see the choice repeating.

What Hyprland gives you, and what it doesn't

It is worth being precise about the base, because "Omarchy is Hyprland with themes" is the summary I keep seeing and it is wrong in a specific way.

Hyprland is a compositor. It arranges windows, handles input, draws borders and animations, and exposes an IPC socket. It is not a desktop. Out of the box it has no bar, no notifications, no launcher, no clipboard history, no screenshot flow, no audio or network panel, no power menu, and no idea what your wallpaper is. The Hyprland ecosystem fills some of that in — hypridle, hyprlock, hyprpaper, hyprpicker — and the community fills the rest by picking from Waybar, Walker, wofi, Mako, grim/slurp, and gluing the result together with keybindings. That assembly is the hobby. It is genuinely fun, and you can do a good one in a weekend.

What you cannot do in a weekend is the second half: keeping the assembly alive. And upstream has just demonstrated why, in the most direct way possible. Hyprland 0.55 deprecated hyprlang in favour of Lua — the entire configuration language. Not a renamed option; the format. Anybody running a hand-rolled setup now owns that migration personally, along with every option rename in the releases before it, in every one of the six or seven separate programs their desktop is made of.

Omarchy's answer to a config-language deprecation is a directory that already exists. Its own .conf files became .lua files in 4.0, the two-layer loader means the defaults half of that move was the package's problem rather than yours, and anything left over that a package cannot express is a timestamped repair script. Same compositor, same ecosystem, same beautiful screenshots — the difference is entirely in who absorbs upstream's churn.

So: Hyprland gives you the mechanism. Omarchy gives you the assembly and a maintenance contract for it. If you enjoy the assembly, Omarchy has nothing you want and you should keep your rice. If you want the result on a work machine in three years, the assembly was never the hard part.

Against GNOME and KDE

The stranger comparison is upward, because Omarchy 4.0 has quietly converged on the architecture of the desktops it is nominally a rebellion against.

GNOME Shell is one long-running process — compositor and shell UI together, with extensions as JavaScript loaded in-process. KDE Plasma splits it (KWin compositing, plasmashell as a QML shell) but the shell is still one process hosting applets. Omarchy started from the opposite end, a pile of independent daemons over a compositor, and in 4.0 consolidated to… one long-running process hosting the bar, panels, notifications and lock screen as plugins. Two directions, one destination. That destination is a real design conclusion, not a coincidence: shared state and instant panels want a single owner, and everyone who ships a desktop eventually finds that out.

Configuration is where they genuinely differ, though not in the way distro partisans usually claim. Layering is not new — GNOME has system dconf databases and lock lists under /etc/dconf, KDE has the KConfig cascade from /etc/xdg into ~/.config, complete with an [$i] marker to make a key immutable. Both are real vendor/user layering. But look at what they are for: they exist so an administrator can impose and freeze policy on a fleet, key by key, in a format meant for a settings GUI to write. GNOME goes further and keeps the user's values in a binary dconf database — a store you cannot sanely put in git, diff, or hand-edit, which is why "dotfiles" in the GNOME world means exporting and reimporting a dump.

Omarchy's layering is aimed at the other problem entirely: not freezing the user's config but continuing to improve it after they have edited it. That is why the layers are code and load order rather than keys and precedence, and it is why the interesting piece is downstream of the layering — the migration runner. Neither GNOME nor KDE has one for user state, and you can feel the absence every time a GNOME upgrade leaves you with a broken extension. There is no repair script to run, because there is no concept of a repair script; there is a compatibility field in a manifest and an extension that is now off.

To be fair about what the big desktops have that Omarchy does not: a graphical settings surface that is the primary interface rather than a fallback, serious accessibility work, decades of hardware and locale coverage, sessions that survive Secure Boot and enterprise imaging, and the enormous advantage of being what already came with the machine. Omarchy asks you to reinstall your operating system, disable Secure Boot and TPM, and type a LUKS passphrase on a wired keyboard. That is not a rival to GNOME on a corporate ThinkPad. It is a different bargain for a different person.

The cleanest way I can phrase the inversion: GNOME and KDE are desktops that a distribution ships; Omarchy is a distribution that a desktop ships. Owning both halves is what makes a migration runner possible at all — nobody can repair your ~/.config reliably unless they also control what put it there.

…and then 425 shell scripts, which is the opposite instinct

The theme switcher

Here is where the architecture stops being tidy, and I think it is right not to be.

bin/ holds 425 executables. All named omarchy-<prefix>-<thing>. All bash. The prefix is the taxonomy — hw- detects hardware, capture- grabs screenshots and recordings, install- adds optional software, setup- runs wizards, refresh- copies a default config into ~/.config, plus restart-, toggle-, theme-, pkg-, launch-, update- — and bin/omarchy is a router that turns that naming convention into a browsable CLI, with a GROUP_DESCRIPTIONS table mapping prefixes to groups and a # omarchy:hidden=true marker for commands that should route but not advertise.

No framework, no plugin API, no abstraction to speak of: a naming convention and a case statement. And because the same router drives the graphical menu, every menu item has a command-line address — omarchy menu summon style.theme jumps straight to theme selection, omarchy menu toggle system, omarchy menu close. That property is why a shipped desktop can be scripted at all.

Then there is my favourite listing in the repository:

omarchy-hw-asus-expertbook-b9406     omarchy-hw-intel-ptl
omarchy-hw-asus-rog                  omarchy-hw-intel-sof
omarchy-hw-asus-zenbook-ux5406aa     omarchy-hw-nvidia-gsp
omarchy-hw-dell-xps-haptic-touchpad  omarchy-hw-nvidia-without-gsp
omarchy-hw-dell-xps-oled             omarchy-hw-surface
omarchy-hw-framework16               omarchy-hw-touchpad
omarchy-hw-hybrid-gpu                omarchy-hw-touchscreen

These return exit codes, for use in conditionals. There is no clever generalisation here and there shouldn't be, because there is nothing general about them: this is the tacit knowledge that normally lives in a 2023 forum thread about one specific laptop, checked into git under a filename you can grep. When a distribution "just works" on the machine you happen to own, this is what that feeling is physically made of — someone wrote your model number down.

Theming works the same way, one level up. A theme is a directory whose essential file is colors.toml, and from that palette Omarchy generates config for Foot, Alacritty, Ghostty, Kitty, btop, Chromium, Hyprland, Neovim, Helix, VS Code, Obsidian and every surface of the shell. Unsupported app? Drop an [appname].tpl into ~/.config/omarchy/themed/ and reference {{ accent }} or {{ color0 }}{{ color15 }}. Twenty-two themes ship. Theming is a compiler with a palette as its input, which is the only reason twenty applications that have never heard of each other end up agreeing on a colour.

How the AI actually integrates

Choosing a default coding agent

Most "AI-powered OS" claims resolve, on inspection, to a chat window with a shortcut. This one is structured, and it is structured in three distinct layers that are worth separating, because they are of very different quality.

Layer one: agents as first-class applications. Nine coding-agent CLIs — Claude, Codex, Copilot, Crush, Gemini, Grok, omp, OpenCode, Pi — installed lazily through mise on first use, so the ISO carries launchers rather than nine Node trees. You designate one with omarchy default agent <name>; Super + Shift + Ctrl + A opens it. The details are where you see actual desktop-integration thinking: the agent window gets a fixed --app-id=org.omarchy.agent rather than the per-binary default, so window rules and themes can target agents as a category regardless of which vendor you picked, and the launcher quietly changes directory out of $HOME into ~/Work first, because — per the comment in the script — "agents refuse to remember trust for $HOME." Someone used this daily before shipping it. There is also a bar widget polling omarchy-agent-usage-claude, -codex, -fireworks every 15 minutes, so your token burn sits next to the battery indicator. That is a fairly precise snapshot of what 2026 development feels like.

Layer two: the desktop as a tool surface. This is the part I find genuinely clever, and it is not an AI feature at all — it is the CLI-first design paying an unexpected dividend. Because every menu entry has a command-line address (omarchy menu summon style.theme, omarchy theme set <name>, omarchy bar move omarchy.clock --section center --index 0), an agent asked to "make my bar show the weather on the left" has a real API to work against instead of a screenshot and a dream. Omarchy then closes the loop by shipping an Agent Skill describing its own conventions, symlinked into the agent directories. Its rules are exactly the invariants this whole post has been about:

For end-user customization tasks, NEVER modify anything in /usr/share/omarchy/ — but READING is safe and encouraged. This directory is owned by the omarchy package. Any local changes will be overwritten on the next omarchy update.

The vendor/user boundary, taught to the machine's other operator. If you accept that agents will be editing dotfiles on real machines — and they will — then documenting which half of the filesystem is yours to write is not a gimmick, it is the minimum viable safety rail. It also, revealingly, is the same document a new human contributor would need.

Layer three: the OS handing work to the agent. systemd-coredump is wired to omarchy-agent-crash, which is a small, unglamorous script: take a PID, look up the crash time from coredumpctl, assemble a prompt containing process name, PID, binary, signal and timestamp, point the agent at the diagnose-crash skill — and, nicely, tell it to just read the skill file directly if its harness has no skill mechanism. Then exec omarchy-agent --prompt. A "Process crashed" notification is a clickable handoff to whichever agent you happen to prefer.

Away from coding agents, the AI story is smaller and more sensible than the marketing would allow: dictation via Voxtype behind a hold-to-talk F9 (a 150 MB base English model, configured in ~/.config/voxtype/config.toml), and OCR that is plain old Tesseract on a region select, straight to the clipboard. No model in that path at all, and no worse for it.

Now the part that deserves scrutiny. Every agent launched from the hotkey or the menu starts auto-approved. From bin/omarchy-agent, verbatim:

# Agents launched from the keybinding or menu run unattended, so each one starts
# with its own spelling of "don't stop to ask". Pi has no approval prompts.

In practice: claude --permission-mode auto, gemini --yolo, copilot --allow-all, grok --permission-mode bypassPermissions, codex --approve-for-me, crush --yolo. On a machine that also offers a 15-minute passwordless-sudo toggle in its menu. I understand the reasoning — an agent that stops to ask is useless from a keybinding, and the whole distribution is built on removing friction between an intent and its execution — but this is the sharpest version of Omarchy's recurring trade, and unlike the snapshot caveat it is not called out in the manual. It is a decision worth making consciously rather than discovering from a shell script.

Where does that leave the "AI-native OS" claim? Mostly earned, but not for the reason the feature list suggests. The agent picker is the least interesting part; anyone can bundle nine CLIs. The reason agents work well here is that Omarchy made the desktop addressable — every action a command, every command discoverable, every convention written down — years before it bundled anything. That work was done for scripting and it happens to be exactly what an agent needs. The lesson generalises past Linux: the best thing you can do to make a system AI-operable is to give it a good CLI and honest docs.

What it costs

The constraints are real and, to the project's credit, documented rather than discovered. Secure Boot and TPM must be off, LUKS is on by default, and since the passphrase is typed before anything USB-agnostic is running, a Bluetooth keyboard cannot unlock the machine. Snapshots skip /home and ~/.config. Customising shell.json forks your bar. pacman is off the table. Unattended installs read a cidata drive whose disk_encryption block holds a plaintext passphrase, making that ISO a credential — and encrypted installs still need a human at first boot anyway. Agents launched from the hotkey run auto-approved, which the manual does not mention. The network posture, by contrast, is tight by default (everything inbound blocked except port 53317 for LocalSend, SSH off until you enable it), which I like. And support is a manual plus a Discord; for anything unusual — Asahi on Apple Silicon, a Steam Deck, a VM — you are in #omarchy-on-other.

Notice that most of those are the same trade made repeatedly: coherence over configurability, one owner over merged ownership. Whether Omarchy is for you reduces almost entirely to whether you want that trade, and the manual is unusually good at letting you answer that before you burn an ISO.

What I'd steal

Strip the wallpapers and there are four ideas here that have nothing to do with Arch:

Ship migrations with your configuration. Timestamped, idempotent, with per-recipient completion state. Anyone who distributes config to machines they don't control eventually needs this, and most of us reinvent it badly and late.

Make the user's file load the vendor's file, with named flags for opting out, instead of merging two documents and hoping.

Give every GUI action a CLI address. Scripting becomes free, and agent-operability comes along with it for nothing.

Write the quirks down as code. omarchy-hw-dell-xps-oled is a more useful artefact than any wiki page about that laptop will ever be.

Write down which half of the filesystem you own, in a document an agent will read. Omarchy's skill file says "never modify /usr/share/omarchy" to a coding agent for the same reason its manual says it to a human, and both audiences are about to start editing your config.

The Quickshell rewrite is what made the release notes, and it deserved to. But in two years the load-bearing part of this project will still be a directory of timestamped shell scripts and a rule that says they must be safe to run twice.


Read against the Omarchy manual and the quattro branch as of 18 August 2026. Counts are from that tree; the memory figure is DHH's, not mine. Screenshots are from the Omarchy 4.0 release (MIT).