i3 Desktop Part 1: A Curated Session

The first useful decision in this i3 setup was to stop treating autostart as a dumping ground.

On a normal desktop environment, a lot of background behaviour is started for me. Some of that is useful. Some of it is tray furniture, duplicated settings daemons, or services that made sense in GNOME but are not needed in a small i3 session.

For this setup, startup is deliberately curated.

The i3 config runs .local/bin/i3-autostart with exec_always. That script is small, but it gives the session a clear shape. It exports the desktop identity as i3, sets the default browser to qutebrowser, the terminal to kitty, the editor to nvim, and the visual editor to code. Then it pushes that environment into D-Bus activation so applications launched later see the same values.

That matters more than it sounds.

Without it, GUI apps can inherit a different idea of the session from whatever login manager or parent process started them. A terminal might know the right browser, but a notification action or file opener might not. The autostart script gives the session a single set of defaults.

Applying the desktop shape

After the environment is set, i3-autostart calls i3-apply-theme.

That script sets the GTK theme, icon theme, cursor theme, fonts, Qt platform theme, and cursor settings. It updates gsettings, merges X resources with xrdb, and starts xsettingsd so GTK applications behave consistently outside GNOME.

The theme is not applied by wishing every app would read the same file. It is applied through the mechanisms those apps already understand.

The i3 config then starts the desktop pieces that belong to this session:

  • monitor layout with i3-monitor-layout auto --quiet
  • keyboard layout with i3-keyboard-layout apply --quiet
  • wallpaper with i3-wallpaper
  • dunst for notifications
  • CopyQ through i3-start-copyq
  • picom through i3-start-picom
  • xss-lock through i3-start-xss-lock

The order is simple: set the environment, apply the system look, then start the services that make the desktop usable.

Why not just start everything

The temptation with dotfiles is to add one more autostart line every time something is annoying.

That works for a while. Then the session starts to feel haunted. A tray icon appears and you cannot remember which package owns it. A notification daemon competes with another one. A compositor starts twice. A theme only applies to half the apps. A service keeps running because it was useful for one experiment six months ago.

The better rule is: start only what this desktop intentionally uses.

Network and Bluetooth are good examples. I do not start tray applets for them. They are controlled through rofi menus backed by nmcli and bluetoothctl. If I need the full GUI editor or manager, the menu can open it. The daily path stays clean.

CopyQ is another example. I want clipboard history, but I do not want the tray icon. The startup wrapper makes sure the server is running and configures it to hide the tray and main window.

Session recovery

The i3 config uses exec_always for startup pieces because reloading i3 should repair the desktop.

If I change the config and reload, the theme is reapplied, monitor and keyboard layout are checked, the wallpaper comes back, CopyQ and picom are restarted safely, and xss-lock is reset. The helper scripts are written to tolerate being run again.

That turns i3 reload into a recovery tool. I do not need to log out just because a service got into a weird state.

There is still a tradeoff. Anything started with exec_always needs to avoid duplicating itself. That is why wrappers like i3-start-picom, i3-start-copyq, and i3-start-xss-lock exist. They can stop or check existing processes before starting the new one.

The desktop as a small system

This is the part where i3 starts to feel less like a config file and more like a desktop.

The window manager is still doing window manager work. The autostart script is doing session work. The theme script is doing appearance work. The status bar has its own scripts. Menus wrap the underlying command-line tools.

Keeping those concerns separate makes the setup easier to change. If I want to adjust lock behaviour, I edit the lock script. If I want to change startup defaults, I edit autostart. If I want to add a new control centre entry, I do not have to touch monitor layout or notifications.

That separation is why the setup has grown without becoming impossible to understand.

The desktop starts with a small promise: only start the things I actually use, and make each part obvious enough that future me can fix it.