System configuration
DNS
Select Setup -> Network -> DNS -> Cloudflare from the Omarchy menu.
Confirm the active DNS configuration with:
resolvectl status
Shell aliases and PATH
Add these aliases and environment variables:
# CUSTOM
# non-omarchy alias for real ls
# alias la="ls -lahF"
# omarchy alias for replaced-by-eza ls
alias la="ls -lah --classify=auto"
export PATH=~/bin:$PATH
export ERL_AFLAGS="-kernel shell_history enabled"
Reset UFW
To discard the current UFW rules and enable a fresh firewall configuration:
sudo ufw reset
sudo ufw enable
Extend desktop idle timeouts
The default idle timeouts are too short for my desktop. I replace them with the following one-hour screensaver and lock sequence:
{
// ...
"idle": {
"screensaver": 3600,
"lock": 3601
}
// ...
}
Omarchy bar
Add detail to the clock, and show the active network interface:
{
// ...
"bar": {
// ...
"layout": {
// ...
"center": {
// ...
{
"id": "omarchy.clock",
"format": "☀️ dddd 'w'ww 📅 yyyy / MM / dd 🕑 HH:mm:ss",
// ...
},
// ...
}
}
}
}
To make the clock update every second:
omarchy plugin clone omarchy.clock
This will create a file in ~/.config/omarchy/plugins with your username in it. Change the precision in it from
SystemClock.Minutes to SystemClock.Seconds:
// ...
SystemClock {
// ...
precision: SystemClock.Seconds
}
Restart the shell to apply the changes immediately:
omarchy restart shell
Enable fstrim for an encrypted SSD
Run a manual trim check:
sudo fstrim / -v
If it returns fstrim: /: the discard operation is not supported, add :allow-discards immediately after some-id:root:
KERNEL_CMDLINE[default]="cryptdevice=PARTUUID=some-large-uuid:root:allow-discards ...
Regenerate the Limine configuration after saving the file:
sudo limine-update
Then enable the periodic trim timer:
sudo systemctl enable --now fstrim.timer
Restart the system to apply the kernel command-line change.
Set up subvolumes for Podman containers and AI sandboxes
Use this ONLY when the target directories do not exist.
Some Podman commands, including podman info, may initialize rootless storage.
Create the container subvolume before running them.
If ~/.local/share/containers already exists, do not remove it.
-
Inspect the installed Btrfs and Snapper layout without starting Podman:
Shell command findmnt -t btrfs sudo btrfs subvolume list -t / sudo btrfs filesystem usage -T / sudo snapper list-configs sudo snapper -c root get-config -
If you intend to use Podman's default rootless storage location, create its subvolume:
Shell command mkdir -p "$HOME/.local/share" sudo btrfs subvolume create "$HOME/.local/share/containers" sudo chown --reference="$HOME" "$HOME/.local/share/containers" -
If your workflow generates disposable AI sandboxes under
~/.local/share/ai-sandboxes, create that subvolume too:Shell command mkdir -p "$HOME/.local/share" sudo btrfs subvolume create "$HOME/.local/share/ai-sandboxes" sudo chown --reference="$HOME" "$HOME/.local/share/ai-sandboxes" -
Verify the container boundary and current filesystem state:
Shell command sudo btrfs subvolume show "$HOME/.local/share/containers" sudo btrfs filesystem usage -T / sudo snapper -c root list
Software
Restore your backup
Remount the encrypted partition , then restore the files from your full-disk-encrypted backup drive.
Remove software I do not use
The following command removes packages that are not part of my workflow. Review the list before running it, especially on a system you have already configured:
sudo pacman -Rns xournalpp docker docker-buildx docker-compose ufw-docker lazydocker
mise remove codex
Remove each unused web app through:
Omarchy menu -> Remove -> Web App
I also like to clear any entries in ~/.local/bin, and only add the ones
I explicitly install:
ls -la ~/.local/bin
rm ~/.local/bin/*
Update the system
sudo pacman -Syy
omarchy update
Install desktop and command-line software
This is the software I typically need on a workstation:
sudo pacman -S nethogs transmission-gtk nmap \
darktable gimp inkscape krita godot \
blender calibre obs-studio shotcut \
colordiff python-gobject python-xdg yt-dlp \
qgis python-gdal python-owslib python-psycopg2 python-pygments \
ansible inotify-tools jq \
simple-scan audacity base-devel \
libvips openslide firefox iotop \
vlc vlc-plugins-base vlc-plugins-extra \
podman pass
yt-dlp
If the normal extractor cannot download a video, try the generic extractor:
yt-dlp --force-generic-extractor URL
# or install python-curl_cffi
Install Erlang, Elixir, Node.js, and Go with mise
List the language runtimes already installed:
mise ls
Choose and install an Erlang version:
mise ls-remote erlang
mise use erlang@29.1.1 --global
Choose an Elixir build that matches the installed Erlang/OTP version, then install the Hex, Rebar, Phoenix, and Nerves tooling:
mise ls-remote elixir
mise use elixir@1.20.4-otp-29 --global
mix local.hex
mix local.rebar
mix archive.install hex phx_new
mix archive.install hex nerves_bootstrap
Choose and install a Node.js version, then install the npm and pnpm tools:
mise ls-remote node
mise use node@26.10.0 --global
npm i -g npm
#pnpm setup
# npm i -g pnpm
# pnpm self-update
Set a minimum package release age so newly published versions are not installed immediately:
# npm in days
npm config set min-release-age 2 --location=global
# pnpm in minutes
#pnpm config set minimumReleaseAge 2880
Choose and install a Go version:
mise ls-remote go
mise use go@1.26.4 --global
Install PostgreSQL
sudo pacman -S postgresql postgresql-libs postgis
sudo -i -u postgres
initdb --locale $LANG -E UTF8 -D '/var/lib/postgres/data/'
exit
Enable PostgreSQL now and on future system starts:
sudo systemctl enable --now postgresql.service
Workstation fixes and desktop settings
Prevent Bluetooth audio lag after inactivity
Disable WirePlumber's node suspension:
wireplumber.profiles = {
main = {
hooks.node.suspend = disabled
}
}
Then restart the audio services:
systemctl --user restart wireplumber.service pipewire.service
Make Shift+Enter work in tmux and Alacritty
If Alacritty does not forward Shift+Enter inside a tmux session - for example, when adding a new line to a Pi prompt - add this binding:
[keyboard]
bindings = [
#...
{ key = "Return", mods = "Shift", chars = "\u001b[13;2u" }
]
Install GPU utilities
For NVIDIA:
sudo pacman -S nvtop nvidia-utils
For AMD:
sudo pacman -S vulkan-radeon
Code editor
Zed
Install -> Editor -> Zed
Extensions:
- Catppuccin
- Catppuccin Icons
- Dockerfile
- Elixir
- Emmet
- Git Firefly
- HTML
- Rainbow CSV
- SCSS & SASS
- SQL
- TOML
- Vue
- Zig
If Ctrl+Enter opens the assistant panel instead of inserting a cursor on the next line, add these bindings:
[
//...
{
"context": "Editor && mode == full",
"bindings": {
"ctrl-enter": "editor::NewlineBelow",
"ctrl-shift-enter": "editor::NewlineAbove",
},
},
]
My Zed settings:
{
"autosave": "on_window_change",
"buffer_font_size": 12,
"buffer_line_height": "comfortable",
"cursor_blink": true,
"cursor_shape": "bar",
"file_finder": { "modal_max_width": "large" },
"file_types": {
"HTML": ["*.svg"],
},
"icon_theme": "Catppuccin Mocha",
"indent_guides": {
"coloring": "indent_aware",
},
"languages": {
"CSS": {
"language_servers": [
"tailwindcss-intellisense-css",
"!vscode-css-language-server",
"...",
],
},
"Elixir": {
"language_servers": [
"expert",
"tailwindcss-language-server",
"emmet-language-server",
"!elixir-ls",
"!next-ls",
"!lexical",
"...",
],
},
"HEEx": {
"language_servers": [
"expert",
"tailwindcss-language-server",
"emmet-language-server",
"!elixir-ls",
"!next-ls",
"!lexical",
"...",
],
},
},
"prettier": {
// "plugins": ["prettier-plugin-tailwindcss"],
"semi": false,
"singleQuote": false,
},
// "lsp": {
// "vscode-css-language-server": {
// "settings": {
// "css": {
// "lint": {
// "unknownAtRules": "ignore",
// },
// },
// },
// },
// },
"show_whitespaces": "all",
"soft_wrap": "editor_width",
"ui_font_size": 14,
"tab_size": 2,
"tabs": {
"file_icons": true,
},
"theme": {
"mode": "dark",
"light": "Catppuccin Latte",
"dark": "Catppuccin Mocha",
},
}
Configure laptop monitor
I like to keep all ten workspaces visible:
-- custom scale for 2.8k laptop display
local omarchy_gdk_scale = 1.8
local omarchy_monitor_scale = 1.8
hl.env("GDK_SCALE", tostring(omarchy_gdk_scale))
hl.monitor({ output = "", mode = "preferred", position = "auto", scale = omarchy_monitor_scale })
for i = 1, 10 do
hl.workspace_rule({
workspace = tostring(i),
persistent = true,
})
end
Configure two desktop monitors
List the connected monitors and note their output names:
hyprctl monitors
My configuration assigns workspaces 1-5 to the landscape display and 6-10 to the rotated display:
hl.monitor({
output = "HDMI-A-2",
mode = "3840x2160@60",
position = "0x0",
scale = 1.5,
})
hl.monitor({
output = "HDMI-A-1",
mode = "3840x2160@60",
position = "2560x-720",
scale = 1.5,
transform = 3,
})
for i = 1, 5 do
hl.workspace_rule({
workspace = tostring(i),
monitor = "HDMI-A-2",
persistent = true,
})
end
for i = 6, 10 do
hl.workspace_rule({
workspace = tostring(i),
monitor = "HDMI-A-1",
persistent = true,
})
end
Add fonts
Place user-specific font files in ~/.local/share/fonts.
Add laptop touchpad gestures
Add three-finger workspace and full-screen gestures:
hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace"})
hl.config({
gestures = {
workspace_swipe_invert = false,
},
})
hl.gesture({ fingers = 3, direction = "up", action = "fullscreen" })
Reduce window gaps
Set smaller inner gaps and remove outer gaps:
hl.config({
general = {
gaps_in = 2,
gaps_out = 0,
border_size = 1,
},
})
Hyprland reloads these files automatically after each save. To check the active configuration for errors do:
hyprctl reload
hyprctl configerrors
AI tools
Install OpenCode, Pi, and agent-browser with npm:
npm install -g --allow-scripts=opencode-ai opencode-ai
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
npm install -g agent-browser
# reshim to pickup new executables
mise reshim
I have this set up for Pi:
# CLI Tools
* You have access to the `agent-browser` CLI tool. You can use it as a "browser automation" tool.
# Special instructions
* When done and about to hand-off to the user terminate all outstanding processes started during your turn.
* When working on Phoenix apps, use Tailwind CSS utility classes instead of BEM style opaque classes.
Set OpenCode to notify before updating and keep optional MCP server configurations ready:
{
// ...
"autoupdate": "notify",
"default_agent": "plan",
// "mcp": {
// "chrome-devtools": {
// "type": "local",
// "command": ["npx", "chrome-devtools-mcp@latest", "--executablePath=/usr/bin/chromium", "--isolated=true"],
// "enabled": false
// },
// "context7": {
// "type": "local",
// "command": ["npx", "-y", "@upstash/context7-mcp@latest"]
// },
// "playwright": {
// "type": "local",
// "command": ["npx", "@playwright/mcp@latest", "--executable-path=/usr/bin/chromium"],
// "enabled": false
// }
// }
}
Fabric
Install Fabric from its repository, then run its setup flow:
# Install Fabric directly from the repo
go install github.com/danielmiessler/fabric/cmd/fabric@latest
# Configure your directories and keys
fabric --setup
AI sandbox
Need this code in phoenix apps if using the sandbox script:
if System.get_env("AI_SANDBOX") == "true" do
config :my_app, MyAppWeb.Endpoint, http: [ip: {0, 0, 0, 0}]
config :my_app, MyApp.Repo, hostname: "postgres"
end