Inside My Arch + Hyprland Setup: Building a Fully Reproducible Desktop Environment
·5260 words·25 mins
Author
David Cajio
I do not treat my Linux desktop like a toy anymore.
That does not mean I do not care how it looks. I absolutely do. I want my desktop to look sharp, modern, dark, readable, and intentional. I want my terminal, status bar, launcher, lock screen, and editor to feel like they belong to the same system.
But the real goal is bigger than aesthetics.
My Arch + Hyprland setup is built around one idea:
My desktop should be reproducible infrastructure.
If I wipe my laptop tomorrow, I should not have to spend a weekend trying to remember which packages I installed, which config files I edited, which environment variables fixed screen sharing, or which script made my monitors behave correctly.
I should be able to redeploy my workstation.
That mindset changes everything.
Instead of a random pile of dotfiles, my desktop becomes something closer to a small platform:
version-controlled configuration
modular Hyprland files
wallpaper-driven theming
repeatable package installation
machine-specific monitor templates
shared keybindings across systems
explicit handling for Wayland quirks
scripts for the small things I do constantly
This is the same way I think about infrastructure at work. If a process matters and I will repeat it more than once, it should eventually become documented, automated, or reproducible.
I did not choose Arch and Hyprland because it is the easiest path. I chose it because I wanted control without dragging around a full desktop environment that makes assumptions for me.
The main goals were:
Full rebuildability
Keeping multiple machines in sync
Readable wallpaper-based theming
Aesthetic consistency
Low bloat
Wayland-native tooling
A workflow that feels fast all day
The rebuildability piece is the most important.
I use more than one machine. My desktop and laptop need to feel like the same environment. I do not want one machine to have a different launcher, different keybinds, different fonts, missing scripts, or a slightly broken terminal theme.
That kind of drift is annoying in cloud infrastructure.
It is just as annoying on a workstation.
The second big goal was theming. I like changing wallpapers, but I do not want to manually retheme my entire desktop every time I do it. The color system should follow the wallpaper automatically while still keeping text readable.
That is where tools like pywal, swww, and matugen fit into the workflow.
The wallpaper is not just decoration. It becomes an input into the theme pipeline.
That matters because Wayland behavior can vary depending on whether I am on Intel, AMD, or NVIDIA hardware. Some of my machines are smoother than others. Some need different environment variables. Some behave differently with external monitors. Some make screen sharing more painful.
So the config cannot assume every machine is identical.
That is one of the biggest reasons I separate shared configuration from machine-specific configuration.
# ~/.config/hypr/hyprland.conf## Main Hyprland entrypoint.# Keep this file boring. Real configuration lives in modules/.source=~/.config/hypr/modules/environment.confsource=~/.config/hypr/modules/theme.confsource=~/.config/hypr/modules/monitors.confsource=~/.config/hypr/modules/input.confsource=~/.config/hypr/modules/keybinds.confsource=~/.config/hypr/modules/window-rules.confsource=~/.config/hypr/modules/animations.confsource=~/.config/hypr/modules/autostart.conf
That is the whole point.
The root config should explain the system at a glance. It should not contain every detail.
# ~/.config/hypr/modules/environment.conf## Session-level environment variables.# Keep hardware-specific values templated through chezmoi where possible.env=XDG_CURRENT_DESKTOP,Hyprlandenv=XDG_SESSION_TYPE,waylandenv=XDG_SESSION_DESKTOP,Hyprland# Toolkit behaviorenv=QT_QPA_PLATFORM,wayland;xcbenv=QT_WAYLAND_DISABLE_WINDOWDECORATION,1env=GDK_BACKEND,wayland,x11env=SDL_VIDEODRIVER,waylandenv=CLUTTER_BACKEND,wayland# Firefox / Mozilla Wayland supportenv=MOZ_ENABLE_WAYLAND,1# Electron / Chromium-based appsenv=ELECTRON_OZONE_PLATFORM_HINT,auto
For NVIDIA-specific machines, I would keep the values separate or generated through a template:
1
2
3
4
5
6
# NVIDIA-specific values.# Do not apply globally to every machine unless every machine is NVIDIA.env=LIBVA_DRIVER_NAME,nvidiaenv=__GLX_VENDOR_LIBRARY_NAME,nvidiaenv=NVD_BACKEND,direct
I avoid dumping NVIDIA settings into the shared config unless they are genuinely safe for every machine.
That is the broader rule:
Shared config should be boring. Machine-specific config should be explicit.
A direct Hyprland config for that machine might look like this:
1
2
3
4
5
6
7
# ~/.config/hypr/modules/monitors.conf## Generated or selected per-machine through chezmoi.monitor=eDP-1,1920x1080@144,4520x0,1.5monitor=HDMI-A-1,1920x1080@60,0x0,1,transform,3monitor=HDMI-A-2,3440x1440@60,1080x0,1
This works, but it should not be globally shared across every system.
The better pattern is to treat monitor layout as machine-specific infrastructure.
In the dotfiles repo, I would structure it like this:
External monitor behavior is one of those things that can work perfectly for weeks and then randomly come up wrong after a reboot, dock change, driver update, or sleep/wake cycle.
So I like having an explicit monitor enforcement script.
The best status bar is the one that gives me context without demanding attention.
Terminal Workflow: Ghostty, Kitty, Zsh, and Starship#
Most of my real work happens in the terminal, so the terminal setup matters.
I use Zsh with Starship, and I currently have both Ghostty and Kitty available. Ghostty is my primary terminal right now, but I like having Kitty installed as a fallback.
The terminal needs to be:
fast
readable
consistent with the desktop theme
good with Nerd Fonts
predictable across machines
Starship is useful because it gives me context without a giant prompt.
Screen sharing is one of the biggest practical issues with a Hyprland workstation.
On Wayland, applications do not just get unrestricted access to the screen. That is good for security, but it means screen sharing depends on the correct portal and PipeWire stack.
systemctl --user status pipewire
systemctl --user status wireplumber
systemctl --user status xdg-desktop-portal
systemctl --user status xdg-desktop-portal-hyprland
If the services are running but screen sharing is still acting weird:
NVIDIA can work well, but it still needs special care.
I avoid mixing NVIDIA-specific assumptions into the shared config. If a setting only applies to NVIDIA, it belongs in a machine-specific file or template.
This matters even more when syncing dotfiles between machines with different GPUs.
Arch and Hyprland are not the easiest way to run a Linux desktop.
That is fine.
I am not optimizing for easiest. I am optimizing for control, speed, reproducibility, and a desktop that feels like mine.
The difference between a fragile rice and a real workstation setup is discipline.
A fragile rice looks good in screenshots.
A real workstation survives rebuilds.
That is what I want from my desktop. I want the wallpaper, colors, terminal, status bar, keybinds, monitors, scripts, and applications to work together as one reproducible system.
If I wipe the machine, I should not feel like I lost my setup.
I should feel like I am redeploying it.
My Linux desktop is not just a collection of dotfiles.