Paul Borrego

CachyOS Hyprland Setup

Configuration changes applied to a fresh CachyOS install running Hyprland + HyDE.
Started: 2026-05-15


The one big gotcha: where to put user overrides

HyDE's config-registry advertises files in ~/.config/hypr/ (e.g. keybindings.conf, windowrules.conf, monitors.conf) as user-editable, but ~/.local/share/hypr/hyprland.conf does not source most of them — they are effectively dead. Editing them and reloading does nothing.

The reliable override file is ~/.config/hypr/workflows/default.conf. It is sourced (via ~/.local/share/hypr/hyprland.conf:61) after defaults.conf, so later-wins applies. Hyprland's own comments call workflows "configuration overrides everything." Almost every Hyprland change in this doc lives there.

Waybar has the same shape: ~/.config/waybar/config.jsonc is regenerated by HyDE's waybar.py, but layouts in ~/.config/waybar/layouts/ are first-class and never overwritten — that's the durable place to customize.


Hyprland — ~/.config/hypr/workflows/default.conf

All of the following were appended to this single file.

Keybindings

ActionBind
Terminal ($TERMINAL)Super+Return
Rofi launcherSuper+Space
Toggle floatingSuper+T
Toggle fullscreenSuper+F

Note: HyDE actually sources ~/.local/share/hyde/keybindings.conf (not the ~/.config/hypr/keybindings.conf advertised by the registry). To make binds stick, both files were edited; the ~/.local/share/hyde/ copy may be overwritten on HyDE update and need re-applying. Long-term fix: investigate making HyDE source the user copy last.

Window opacity

Focused 100% / unfocused 90% globally; terminals keep HyDE's original look.

decoration {
    active_opacity = 1.0
    inactive_opacity = 0.9
}
# Terminals: active 0.9 / inactive 0.75 / fullscreen 1.0
windowrule = opacity 0.9 $& 0.75 $& 1, match:class ^(kitty)$

Add other terminal classes (Alacritty|foot|org.wezfurlong.wezterm) to that regex if installed later.

Monitors

Goal: cursor crosses monitors at equal vertical levels; text on the 1440p panel matches the physical size of the 1080p panels. All three monitors are ~27".

Fix: scale the 1440p panel by 4/3 so its logical resolution becomes 1920×1080, matching the side panels.

monitor = HDMI-A-1, 1920x1080@60, 0x0,    1
monitor = DP-1,     2560x1440@60, 1920x0, 1.333333
monitor = DP-2,     1920x1080@60, 3840x0, 1

Follow-ups not applied: bump refresh rates (DP-1 → 240, DP-2 → 165, HDMI-A-1 → 75). Fractional scaling can cause blurry text in some XWayland apps — if it bites, try scale 1.25 instead.

Workspace-to-monitor assignment

10 workspaces split 3 / 4 / 3 across the displays.

workspace = 1, monitor:HDMI-A-1
workspace = 2, monitor:HDMI-A-1
workspace = 3, monitor:HDMI-A-1
workspace = 4, monitor:DP-1
workspace = 5, monitor:DP-1
workspace = 6, monitor:DP-1
workspace = 10, monitor:DP-1
workspace = 7, monitor:DP-2
workspace = 8, monitor:DP-2
workspace = 9, monitor:DP-2

Keyboard — swap CapsLock and Escape (Hyprland layer)

input:kb_options = caps:swapescape

Apply all of the above with hyprctl reload.


Keyboard — system layer (SDDM, X11 apps, other DEs)

Hyprland manages its own keymap, so a second layer is needed for the display manager and X11 apps:

sudo localectl set-x11-keymap us pc105 "" caps:swapescape

Writes /etc/X11/xorg.conf.d/00-keyboard.conf. Verify with localectl status (should show X11 Options: caps:swapescape).

Not covered: TTY / Linux console. If needed there, either write a custom vconsole keymap or install keyd (which would also replace both layers above with one config).


Idle / lock (~/.config/hypr/hypridle.conf)

This file is the one actually sourced — no HyDE shadow. New timings (preserve the cascade dim → lock → screen-off → suspend):

StageTimeout
Dim to 1%600 s (10 min)
loginctl lock-session1800 s (30 min)
DPMS off2100 s (35 min)
systemctl suspend3600 s (60 min)

Hypridle doesn't reload on SIGHUP — restart it. Use systemctl --user restart hypridle.service if a user unit exists; otherwise pkill hypridle; hypridle &. Verify exactly one process with pgrep -a hypridle.


Kitty (~/.config/kitty/kitty.conf)

Override file that includes HyDE's hyde.conf at the top — later settings win, and this file is not touched by HyDE updates.

window_padding_width  4         # was 25
copy_on_select        yes       # highlight = copy to clipboard

Reload an existing window with Ctrl+Shift+F5, or open a new one.


Waybar

Goal: keep layout 12 as base, but put clock on left, per-monitor workspaces in center (HDMI-A-1: 1–3, DP-1: 4–6+10, DP-2: 7–9), and move the idle_inhibitor to the right pill.

Durable approach: user layout file

Editing ~/.config/waybar/config.jsonc doesn't survive — HyDE's waybar.py regenerates it from layout files on --update, theme switches, and watcher restarts.

The fix: put a complete layout at ~/.config/waybar/layouts/12-custom.jsonc. waybar.py:46-51 searches ~/.config/waybar/layouts/ first, before HyDE's ~/.local/share/waybar/layouts/. Files placed here are never overwritten.

Critical detail: define hyprland/workspaces inline at the top level of the layout JSON, not via the include of HyDE's module file. Top-level config keys take precedence over included ones, so HyDE refreshing the included module file can't shadow our settings.

"hyprland/workspaces": {
    "all-outputs": false,
    "active-only": false,
    "on-click": "activate",
    "disable-scroll": false,
    "on-scroll-up": "hyprctl dispatch workspace -1",
    "on-scroll-down": "hyprctl dispatch workspace +1",
    "persistent-workspaces": {
        "HDMI-A-1": [1, 2, 3],
        "DP-1": [4, 5, 6, 10],
        "DP-2": [7, 8, 9]
    }
}

Why both persistent-workspaces keys matter: all-outputs: false makes each bar show only its own monitor's workspaces; persistent-workspaces keeps the numbers visible as static buttons even when empty.

Module placement changes in the same layout file:

  • group/pill#left.modules[clock, wlr/taskbar, mpris]
  • group/pill#center.modules[hyprland/workspaces]
  • group/pill#right2.modules → original list + idle_inhibitor inserted

Apply:

hyde-shell waybar --set 12-custom

Verify: each monitor's bar shows only its workspace numbers; pgrep -a waybar returns one process.

Font bump — ~/.config/waybar/user-style.css

style.css is HyDE-managed but imports user-style.css last, so this overrides cleanly.

* { font-size: 12px; }   /* was 10px */

Waybar's reload_style_on_change: true picks it up on save. Use a more targeted selector (#clock, #workspaces button) if you only want to resize specific modules.

Caveat — hyde-shell waybar --next/--prev

These cycle through every discoverable layout including 12-custom. To pin permanently, just don't invoke them.


Fastfetch — CachyOS logo

System ships /usr/share/icons/cachyos.svg but fastfetch's kitty graphics protocol needs raster. Convert once:

mkdir -p ~/.config/fastfetch/logo
rsvg-convert -h 256 /usr/share/icons/cachyos.svg \
    -o ~/.config/fastfetch/logo/cachyos.png

Edit ~/.config/fastfetch/config.jsonc:

"logo": {
    "source": "~/.config/fastfetch/logo/cachyos.png",
    "type": "kitty",
    "height": 18
}

Renders only in a real kitty session (not in tmux/zellij that strip the kitty graphics protocol; falls back to ASCII otherwise). The PNG lives under $HOME so it survives package updates that rewrite the system SVG.


Disk mounts — /etc/fstab

Procedure (reusable)

  1. lsblk -f -o NAME,FSTYPE,LABEL,UUID,SIZE,MOUNTPOINT to enumerate drives. Use UUIDs, not /dev/sdX.
  2. sudo mkdir -p /mnt/<name> for each mount point.
  3. Back up fstab: sudo cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d).
  4. Append UUID=… /mnt/<name> <fs> defaults,noatime,nofail 0 2.
    • nofail is critical — without it, a missing drive drops boot into emergency mode.
  5. sudo systemctl daemon-reload && sudo mount -a && mount | grep /mnt/.
  6. Optional: sudo chown -R $USER:$USER /mnt/<name> for data drives you own.

For occasionally-disconnected drives use x-systemd.automount,noauto (lazy-mount on first access). Encrypted drives need /etc/crypttab set up first.

Drives chosen (2026-05-17)

DeviceUUIDFSMount
/dev/sdb1204b6728-1751-4fa3-b918-1349eb4e6172ext4/mnt/psi
/dev/sda19aa68d02-8fba-435d-a5e7-34836a6c0473ext4/mnt/omega
/dev/nvme0n1p1e34bdaae-8596-4e24-a643-35d2df1695f6ext4/mnt/alpha
sudo cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d) && sudo tee -a /etc/fstab <<'FSTAB'

# User data drives (added 2026-05-17)
UUID=204b6728-1751-4fa3-b918-1349eb4e6172 /mnt/psi    ext4 defaults,noatime,nofail 0 2
UUID=9aa68d02-8fba-435d-a5e7-34836a6c0473 /mnt/omega  ext4 defaults,noatime,nofail 0 2
UUID=e34bdaae-8596-4e24-a643-35d2df1695f6 /mnt/alpha  ext4 defaults,noatime,nofail 0 2
FSTAB
sudo systemctl daemon-reload && sudo mount -a && mount | grep /mnt/

Status: ⏳ Pending — user has not yet executed the command above.