Skip to content

Omarchy tweaks

My post-install Omarchy checklist for DNS, packages, language runtimes, editor settings, displays, Hyprland and AI stuff.

9 min read

System configuration

DNS

Select Setup -> Network -> DNS -> Cloudflare from the Omarchy menu.

Confirm the active DNS configuration with:

Shell command
resolvectl status

Shell aliases and PATH

Add these aliases and environment variables:

~/.bashrc
# 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:

Shell command
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:

~/.config/omarchy/shell.json
{
  // ...
  "idle": {
    "screensaver": 3600,
    "lock": 3601
  }
  // ...
}

Omarchy bar

Add detail to the clock, and show the active network interface:

~/.config/omarchy/shell.json
{
  // ...
  "bar": {
    // ...
    "layout": {
      // ...
      "center": {
        // ...
        {
          "id": "omarchy.clock",
          "format": "☀️ dddd  'w'ww  📅 yyyy / MM / dd  🕑 HH:mm:ss",
          // ...
        },
        // ...
      }
    }
  }
}

To make the clock update every second:

Bash
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:

~/.config/omarchy/plugins/lobotuerto.clock/BarWidget.qml
// ...
SystemClock {
  // ...
  precision: SystemClock.Seconds
}

Restart the shell to apply the changes immediately:

Shell command
omarchy restart shell

Enable fstrim for an encrypted SSD

Run a manual trim check:

Shell command
sudo fstrim / -v

If it returns fstrim: /: the discard operation is not supported, add :allow-discards immediately after some-id:root:

/etc/default/limine
KERNEL_CMDLINE[default]="cryptdevice=PARTUUID=some-large-uuid:root:allow-discards ...

Regenerate the Limine configuration after saving the file:

Shell command
sudo limine-update

Then enable the periodic trim timer:

Shell command
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.

  1. 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
  2. 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"
  3. 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"
  4. 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:

Shell command
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:

Shell command
ls -la ~/.local/bin
rm ~/.local/bin/*

Update the system

Shell command
sudo pacman -Syy
omarchy update

Install desktop and command-line software

This is the software I typically need on a workstation:

Shell command
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:

Shell command
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:

Shell command
mise ls

Choose and install an Erlang version:

Shell command
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:

Shell command
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:

Shell command
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:

Shell command
# 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:

Shell command
mise ls-remote go
mise use go@1.26.4 --global

Install PostgreSQL

Shell command
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:

Shell command
sudo systemctl enable --now postgresql.service

Workstation fixes and desktop settings

Prevent Bluetooth audio lag after inactivity

Disable WirePlumber's node suspension:

~/.config/wireplumber/wireplumber.conf.d/bluetooth-disable-suspension.conf
wireplumber.profiles = {
  main = {
    hooks.node.suspend = disabled
  }
}

Then restart the audio services:

Shell command
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:

~/.config/alacritty/alacritty.toml
[keyboard]
bindings = [
#...
{ key = "Return", mods = "Shift", chars = "\u001b[13;2u" }
]

Install GPU utilities

For NVIDIA:

Shell command
sudo pacman -S nvtop nvidia-utils

For AMD:

Shell command
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:

~/.config/zed/keymap.json
[
  //...
  {
    "context": "Editor && mode == full",
    "bindings": {
      "ctrl-enter": "editor::NewlineBelow",
      "ctrl-shift-enter": "editor::NewlineAbove",
    },
  },
]

My Zed settings:

~/.config/zed/settings.json
{
  "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:

~/.config/hypr/monitors.lua
-- 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:

Shell command
hyprctl monitors

My configuration assigns workspaces 1-5 to the landscape display and 6-10 to the rotated display:

~/.config/hypr/monitors.lua
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:

~/.config/hypr/input.lua
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:

~/.config/hypr/looknfeel.lua
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:

Shell command
hyprctl reload
hyprctl configerrors

AI tools

Install OpenCode, Pi, and agent-browser with npm:

Shell command
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:

~/.pi/agent/APPEND_SYSTEM.md
# 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:

~/.config/opencode/opencode.json
{
  // ...
  "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:

Shell command
# 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:

config/runtime.exs
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

More posts connected by shared tags.