# How Hyprland Configuration is Organized in Omarchy: A Layered Approach Explained

> Discover Omarchy's layered Hyprland configuration: default bootstrap files, theme overrides, and user settings. Keep customizations safe and organized with this robust approach.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: internals
- Published: 2026-08-18

---

**Omarchy organizes Hyprland configuration through a three-layer priority system: default bootstrap files → theme-specific overrides → user personal settings, with `~/.config/hypr/hyprland.lua` always loading last.** This architecture keeps customizations safe from upstream changes while allowing themes to inject their own window rules and keybindings.

The Omarchy desktop environment treats Hyprland as a **composable Lua system** rather than a static config file. Understanding this layered organization helps you know exactly where to place modifications without breaking the refresh mechanism or losing changes on updates.

## The Base Layer: Default Configuration Files

Every Hyprland setup in Omarchy starts from [`config/hypr/hyprland.lua`](https://github.com/basecamp/omarchy/blob/main/config/hypr/hyprland.lua). This file serves as the **canonical source** that the `omarchy-refresh-config` command copies into your home directory.

```bash

# Regenerate the official Hyprland config at ~/.config/hypr/hyprland.lua

omarchy-refresh-config hypr/hyprland.lua

```

The default config immediately loads [`default/hypr/bootstrap.lua`](https://github.com/basecamp/omarchy/blob/main/default/hypr/bootstrap.lua), which performs two critical tasks:

- Initializes Omarchy's Lua helper functions (the `o` table for bindings, window rules, etc.)
- Prepends theme discovery paths to `package.path` so themes can override core modules

This bootstrap mechanism is verified in the test suite at [[`test/shell.d/config-test.sh`](https://github.com/basecamp/omarchy/blob/main/test/shell.d/config-test.sh)](https://github.com/basecamp/omarchy/blob/quattro/test/shell.d/config-test.sh#L186), ensuring the sequence remains stable across releases.

## The Theme Layer: Template Rendering and Theme Files

Omarchy's theming system generates Hyprland configurations dynamically. The process involves two distinct mechanisms:

**Template Generation**

The file `default/themed/hyprland.lua.tpl` contains the blueprint for default Hyprland settings. During a refresh, this template renders into [`default/hypr/hyprland.lua`](https://github.com/basecamp/omarchy/blob/main/default/hypr/hyprland.lua) with theme-specific values substituted.

**Theme-Specific Overrides**

Individual themes ship their own [`hyprland.lua`](https://github.com/basecamp/omarchy/blob/main/hyprland.lua) files (for example, [`themes/kanagawa/hyprland.lua`](https://github.com/basecamp/omarchy/blob/main/themes/kanagawa/hyprland.lua)). Because the bootstrap prepends theme directories to `package.path`, a theme's module **wins over** the generic template when both define the same bindings or rules.

The priority works like this:

1. Bootstrap establishes the Lua environment
2. Default template provides base configuration
3. Theme file replaces or extends specific settings
4. User config executes last with final authority

## The User Layer: Personal Configuration at `~/.config/hypr/hyprland.lua`

After all system and theme files load, Omarchy `require`s your personal configuration. This file location is documented in [[`manual/31-dotfiles.md`](https://github.com/basecamp/omarchy/blob/main/manual/31-dotfiles.md)](https://github.com/basecamp/omarchy/blob/quattro/manual/31-dotfiles.md#L11) as the **stable interface** for customizations.

A minimal user config loads the Omarchy helpers then adds personal modifications:

```lua
-- ~/.config/hypr/hyprland.lua
require("default.hypr.omarchy")        -- load core helpers and defaults

-- Add custom keybinding
o.bind("SUPER+Enter", "alacritty")

-- Override window rule from theme
o.window("special:my_scratchpad", { size = "80% 80%" })

```

You can also require theme modules explicitly if you want to combine multiple theme features:

```lua
-- ~/.config/hypr/hyprland.lua
require("default.hypr.omarchy")
require("themes.kanagawa")             -- pull in Kanagawa color scheme and rules

```

## Helper Modules Under `default/hypr/`

Omarchy ships a library of reusable Lua modules in the `default/hypr/` directory. The main entry point [`default/hypr/omarchy.lua`](https://github.com/basecamp/omarchy/blob/main/default/hypr/omarchy.lua) aggregates these helpers:

- [`windows.lua`](https://github.com/basecamp/omarchy/blob/main/windows.lua) — window rule construction
- [`bindings.lua`](https://github.com/basecamp/omarchy/blob/main/bindings.lua) — keybinding definitions
- [`monitors.lua`](https://github.com/basecamp/omarchy/blob/main/monitors.lua) — display configuration utilities

These are **optional but recommended**—they provide type-safe wrappers around raw Hyprland directives and maintain compatibility with Omarchy's refresh system.

## Refresh and Migration Safety

The `omarchy-refresh-config` CLI tool handles config regeneration. Migration scripts in the `migrations/` directory ensure upgrades don't break existing setups. For example, [[`migrations/1781063758.sh`](https://github.com/basecamp/omarchy/blob/main/migrations/1781063758.sh)](https://github.com/basecamp/omarchy/blob/quattro/migrations/1781063758.sh#L3) verifies the bootstrap line is present and strips legacy path modifications that would interfere with the current layering.

This means you can safely run:

```bash
omarchy-refresh-config hypr/hyprland.lua

```

Without losing your `~/.config/hypr/hyprland.lua` personalizations—the command only regenerates the base file, and your user config loads afterward by design.

## Summary

- **Default layer**: [`config/hypr/hyprland.lua`](https://github.com/basecamp/omarchy/blob/main/config/hypr/hyprland.lua) copied by `omarchy-refresh-config`, bootstraps via [`default/hypr/bootstrap.lua`](https://github.com/basecamp/omarchy/blob/main/default/hypr/bootstrap.lua)
- **Theme layer**: Template at `default/themed/hyprland.lua.tpl` renders defaults; individual themes inject overrides via `package.path` priority
- **User layer**: `~/.config/hypr/hyprland.lua` loads last with guaranteed final authority
- **Helper modules**: [`default/hypr/omarchy.lua`](https://github.com/basecamp/omarchy/blob/main/default/hypr/omarchy.lua) and submodules provide type-safe configuration primitives
- **Safety mechanism**: Refresh and migration scripts preserve the three-layer order across updates

## Frequently Asked Questions

### Where should I put my custom Hyprland keybindings in Omarchy?

Add them to `~/.config/hypr/hyprland.lua`. This file is guaranteed to load after all default and theme configurations, so your bindings will override or extend earlier definitions. Never edit the file generated by `omarchy-refresh-config` directly—your changes would be lost on the next refresh.

### What happens to my Hyprland config when I run `omarchy-refresh-config`?

The command regenerates the base config from [`config/hypr/hyprland.lua`](https://github.com/basecamp/omarchy/blob/main/config/hypr/hyprland.lua) into your home directory. Your personal `~/.config/hypr/hyprland.lua` remains untouched because it exists in a separate location and is merely `require`d at the end of the generated file. Migration scripts automatically fix any bootstrap line issues.

### Can I use multiple Omarchy themes with different Hyprland settings simultaneously?

Yes—your user config can `require()` theme modules explicitly. For example, `require("themes.kanagawa")` after loading `default.hypr.omarchy` pulls in that theme's window rules and bindings. Only one theme's generated template applies during refresh, but manual requires let you mix theme fragments.

### What is the `o` object in Hyprland Lua configs?

It's Omarchy's helper table initialized by [`default/hypr/bootstrap.lua`](https://github.com/basecamp/omarchy/blob/main/default/hypr/bootstrap.lua). The `o` object provides methods like `o.bind()` for keybindings and `o.window()` for window rules, offering validation and cleaner syntax than raw Hyprland configuration directives.