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 $USER.clock file in ~/.config/omarchy/plugins. Let's 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.
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
# codex might not be present at first until you do `omarchy update`
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.2 --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.12.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.27.2 --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
Add fonts
Place your font files in ~/.local/share/fonts.
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
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
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",
"tab_size": 2,
"tabs": {
"file_icons": true,
},
"telemetry": {
"diagnostics": true,
"metrics": false,
"anthropic_retention": false
},
"theme": {
"mode": "dark",
"light": "Catppuccin Latte",
"dark": "Catppuccin Mocha",
},
//"ui_font_size": 14,
}
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:
-- when working with scales, you need to adjust the positioning of the second monitor accordingly
-- for a scale of 1.5 3840x2160 -> 2560x1440
-- if you want your second monitor to the right, vertical and centered you'd need to:
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-560",
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
-- for a scale of 1.2 3840x2160 -> 3200x1800
-- if you want your second monitor to the right, vertical and centered you'd need to:
hl.monitor({
output = "HDMI-A-2",
mode = "3840x2160@60",
position = "0x0",
scale = 1.2,
})
hl.monitor({
output = "HDMI-A-1",
mode = "3840x2160@60",
position = "3200x-700",
scale = 1.2,
transform = 3,
})
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
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 stuff
AI tools
Install OpenCode, Pi, and agent-browser with npm:
npm install -g --allow-scripts=@opencode/cli @opencode/cli
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
npm install -g agent-browser
# your might need to 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
// }
// }
}
Opencode 2.0 starts a service whenever you run the TUI client, to see the status or stop it afterwards:
opencode service status
opencode service stop
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
Agent skills
The way I set up agent skills is to clone the repo in a dev dir, for example for the Impeccable skill:
# the directory that will contain all the skills I want to install
mkdir -p ~/development/agent-skills
cd ~/development/agent-skills
# create a directory for the author (some skills are named the same, this serves as a namespace)
mkdir pbakaus
cd pbakaus
# clone the skill repository under the author
git clone https://github.com/pbakaus/impeccable.git
Then to have the skill available to agents:
cd ~/.agents/skills
ln -s ~/development/agent-skills/pbakaus/impeccable/.pi/skills/impeccable/
# you can link to something else in case you're using some other harness, for example:
# ln -s ~/development/agent-skills/pbakaus/impeccable/.opencode/skills/impeccable/
# ln -s ~/development/agent-skills/pbakaus/impeccable/.claude/skills/impeccable/
# ln -s ~/development/agent-skills/pbakaus/impeccable/.codex/skills/impeccable/
With this setup you can update skills with a git pull.
Some good baseline skills:
- https://github.com/pbakaus/impeccable
- https://github.com/mattpocock/skills
- https://github.com/obra/superpowers
Misc
Running out of space
If mysteriously running out of space, it might be a faulty snapshot process.
A good starting point is to check the list of existing snapshots (shouldn't be there more than 5 or so, at some point I had more than 1000):
sudo snapper -c root list
# sudo snapper list
If you find a large number of snapshots and need to delete them, you can use something like:
sudo snapper -c root delete 32-105
Other commands to try:
findmnt -t btrfs
sudo btrfs subvolume list -t /
sudo btrfs filesystem usage -T /
sudo snapper list-configs
sudo snapper -c root get-config
# sudo snapper get-config
systemctl status snapper-cleanup.timer
systemctl status snapper-cleanup.service
Disable IPv6
To temporarily disable IPv6:
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1