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
| Action | Bind |
|---|---|
Terminal ($TERMINAL) | Super+Return |
| Rofi launcher | Super+Space |
| Toggle floating | Super+T |
| Toggle fullscreen | Super+F |
Note: HyDE actually sources
~/.local/share/hyde/keybindings.conf(not the~/.config/hypr/keybindings.confadvertised 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):
| Stage | Timeout |
|---|---|
| Dim to 1% | 600 s (10 min) |
loginctl lock-session | 1800 s (30 min) |
| DPMS off | 2100 s (35 min) |
systemctl suspend | 3600 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_inhibitorinserted
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)
lsblk -f -o NAME,FSTYPE,LABEL,UUID,SIZE,MOUNTPOINTto enumerate drives. Use UUIDs, not/dev/sdX.sudo mkdir -p /mnt/<name>for each mount point.- Back up fstab:
sudo cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d). - Append
UUID=… /mnt/<name> <fs> defaults,noatime,nofail 0 2.nofailis critical — without it, a missing drive drops boot into emergency mode.
sudo systemctl daemon-reload && sudo mount -a && mount | grep /mnt/.- 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)
| Device | UUID | FS | Mount |
|---|---|---|---|
/dev/sdb1 | 204b6728-1751-4fa3-b918-1349eb4e6172 | ext4 | /mnt/psi |
/dev/sda1 | 9aa68d02-8fba-435d-a5e7-34836a6c0473 | ext4 | /mnt/omega |
/dev/nvme0n1p1 | e34bdaae-8596-4e24-a643-35d2df1695f6 | ext4 | /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.