MorgenWM reads its config from $XDG_CONFIG_HOME/morgenwm/config.toml (usually ~/.config/morgenwm/config.toml). Reference configs are provided in example/:
Copy one of them to ~/.config/morgenwm/config.toml.
All fields are optional - missing values fall back to built-in defaults.
General window manager behavior.
| Key | Type | Default | Description |
|---|---|---|---|
| window_gap | int | 12 | Gap between windows in pixels |
| border_width | int | 2 | Window border width in pixels |
| workspace_count | int | 9 | Number of workspaces (max 9) |
| overview_visible_count | int | 3 | Number of windows shown in overview mode |
| keyboard_layout | string | "us" | XKB keyboard layout (e.g. "de", "us", "fr") |
| autostart_delay | int | 0 | Seconds to wait before running autostart commands |
| mod_key | string | "Mod4" | Main modifier: Mod1 = Alt, Mod4 = Super/Win |
| windows_visible | int | 2 | Default visible count for new frames; visible_inc/visible_dec change it per frame |
| windows_visible_max | int | 4 | Ceiling visible_inc/visible_dec cycle to per frame (1..windows_visible_max) |
| wide_first_window | bool | true | First window in a frame takes full width |
| frame_border_enabled | bool | true | Show colored border around the focused frame |
| xdg_autostart | bool | false | Automatically start XDG autostart applications |
| auto_remove_empty_frames | bool | true | Remove empty frames when the last window closes |
| scroll_scope | string | "output" | Scope for scroll_left/scroll_right and the window overview: "output" = focused monitor only, "workspace" = all windows on the active workspace across monitors |
| focus_follow_pointer | bool | false | Focus the window under the pointer on hover (mouse-follow focus) |
| per_output_workspaces | bool | false | Give each monitor its own independent active workspace. When true, Mod+1-9 and the IPC workspace actions switch only the focused monitor; when false (default) all monitors switch together |
| show_welcome | bool | true | Show the welcome/quick-launch menu on startup. The menu is opened by the ~/.local/bin/welcome helper (installed by setup.sh from example/welcome.sh); the docs entry opens `glow` on the installed docs directory so you can browse them; when this is false MorgenWM exports MOGENWM_SHOW_WELCOME=0 so the helper skips itself |
| animation_frames | int | 9 | Number of animation frames for transitions |
| autostart_commands | array of strings | [] | Shell commands to run on startup (see below) |
A list of shell commands run on startup, defined inside [general]:
[general] autostart_commands = [ "pipewire", "wbg -s /path/to/wallpaper.jpg", "/home/user/.local/bin/vibepanel", ]
Commands are spawned in order via /bin/sh -c. They run in the background (fire-and-forget) - MorgenWM does not wait for them to finish.
If xdg_autostart = true, XDG autostart entries from /etc/xdg/autostart/ and ~/.config/autostart/ are also started, unless they duplicate a command already listed in autostart_commands.
MorgenWM watches the config file for changes and reloads it automatically, no restart needed. Save the file and the new settings apply within ~0.5s.
What reloads live:
What does NOT reload (applies only at startup):
If the new config fails to parse, the previous config stays in effect and an error is logged to stderr (~/morgenwm.log when launched via launch_morgenwm).
You can also trigger a reload explicitly without touching the file:
morgenwm_msg action reload-config
This is useful after fixing a parse error that left the old config in place (the file watcher only fires on mtime changes, so editing without saving again wouldn't trigger it).
Window and frame colors in RGBA (0-255).
| Key | Default | Description |
|---|---|---|
| focused | { r = 96, g = 144, b = 255, a = 255 } | Border color for the focused window |
| unfocused | { r = 160, g = 160, b = 160, a = 255 } | Border color for unfocused windows |
| focused_frame_border | { r = 203, g = 212, b = 44, a = 128 } | Border color for the focused frame |
Example:
[colors]
focused = { r = 96, g = 144, b = 255, a = 255 }
unfocused = { r = 160, g = 160, b = 160, a = 255 }
focused_frame_border = { r = 70, g = 254, b = 196, a = 80 }
Pointer bindings map mouse buttons to actions. Each binding has a button, modifiers, and an action.
[[pointer_bindings]] button = "left" modifiers = ["Mod"] action = "move" [[pointer_bindings]] button = "right" modifiers = ["Mod"] action = "resize"
button can be left, right, middle, side, or extra. "Mod" expands to whatever mod_key is set to in [general]; other valid modifier names are the same as for keybindings. Any of the actions listed under `[[keybindings]]` can be used.
The default bindings preserve classic river behavior: Mod + left mouse drag moves the window, Mod + right mouse drag resizes it.
Snap-to-grid drag. Dragging a tiled window with Mod + left drag lifts it out of the layout for the duration of the drag so it follows the pointer freely, and on release snaps it back into the tiling grid at the frame under the pointer. If you drop it over a panel or outside the usable area, it falls back to the focused frame. Dragging a window that is already floating just moves it and leaves it floating. Resizing a tiled window with Mod + right drag floats it (resize does not snap back).
Keybindings are defined as an array of tables. Each binding has a key, modifiers, and an action.
[[keybindings]] key = "Return" modifiers = ["Mod"] action = "spawn:foot"
"Mod" expands to whatever mod_key is set to in [general]. Other valid modifier names: Shift, Ctrl, Mod1 (Alt), Mod4 (Super).
| Action | Description |
|---|---|
| spawn:<command> | Spawn a program (e.g. spawn:firefox, spawn:foot) |
| close / kill | Close the focused window |
| exit / quit | Exit MorgenWM |
| toggle_fullscreen / fullscreen | Toggle fullscreen for the focused window |
| toggle_wide / wide | Toggle wide mode (focused window takes full width; clears when a sibling in the same frame is focused) |
| visible_inc / windows_visible_inc | Increase windows visible per frame (cycles 1..windows_visible_max) |
| visible_dec / windows_visible_dec | Decrease windows visible per frame (cycles 1..windows_visible_max) |
| toggle_floating | Toggle floating for the focused window |
| float | Snap a floating window back into the tiling layout |
| toggle_sticky | Toggle sticky (visible on all workspaces) |
| scroll_left / scroll_prev | Scroll the frame left |
| scroll_right / scroll_next | Scroll the frame right |
| focus_left / focus_prev | Focus the window to the left |
| focus_right / focus_next_win | Focus the window to the right |
| split_horizontal | Split the current frame horizontally |
| split_vertical | Split the current frame vertically |
| remove_frame | Remove the current frame |
| cycle_window_forward | Cycle to the next window in the frame |
| cycle_window_backward | Cycle to the previous window in the frame |
| focus_frame_left | Focus the frame to the left |
| focus_frame_right | Focus the frame to the right |
| focus_frame_up | Focus the frame above |
| focus_frame_down | Focus the frame below |
| move_window_left | Move the focused window to the left frame |
| move_window_right | Move the focused window to the right frame |
| move_window_up | Move the focused window to the frame above |
| move_window_down | Move the focused window to the frame below |
| resize_window_left | Shrink the frame from the left edge |
| resize_window_right | Shrink the frame from the right edge |
| resize_window_up | Shrink the frame from the top edge |
| resize_window_down | Shrink the frame from the bottom edge |
| toggle_overview | Toggle the window overview |
| recent_window_next | Switch to the next most-recently-used window |
| recent_window_prev | Switch to the previous most-recently-used window |
| workspace_N | Switch to workspace N (1-9) |
| move_to_workspace_N | Move the focused window to workspace N (1-9) |
| move_to_output_N | Move the focused window to output N (1-9), keeping its active workspace |
| move_to_output_left / move_to_output_right | Move the focused window to the adjacent output (Mod+Shift+Left/Right) |
Configure per-output resolution, refresh rate, and position via wlr-randr. Requires wlr-randr to be installed. Use morgenwm_msg outputs to discover output names.
[[outputs]] name = "DP-1" width = 2560 height = 1440 refresh = 240.001 position_x = 0 position_y = 0 [[outputs]] name = "DP-2" width = 2560 height = 1440 refresh = 240.001 position_x = 2560 position_y = 0
| Key | Type | Default | Description |
|---|---|---|---|
| name | string | n/a | Output connector name (e.g. DP-1, HDMI-A-1) |
| width | int | n/a | Horizontal resolution in pixels |
| height | int | n/a | Vertical resolution in pixels |
| refresh | float | n/a | Refresh rate in Hz (exact value, e.g. 240.001) |
| position_x | int | 0 | Horizontal position in the layout (pixels) |
| position_y | int | 0 | Vertical position in the layout (pixels) |
Output modes are applied 2 seconds after startup (a fixed sleep 2 prefix gives the compositor time to initialize). The refresh field must match a mode your monitor actually supports - check with wlr-randr (run without arguments to list modes).
Use morgenwm validate to check that your config file parses correctly without starting the compositor:
morgenwm validate
Exit codes: 0 = OK, 1 = parse error, 2 = parsed with warnings.
The command prints a summary of parsed values and warns about suspicious settings (e.g. workspace_count out of range, empty keybinding keys, outputs without dimensions).
This is recommended after every config edit. MorgenWM silently falls back to built-in defaults if the config fails to parse at runtime, so a syntactically broken config may appear to "work" while your settings are ignored.
| Variable | Description |
|---|---|
| MORGENWM_NO_IPC | If set, disables the IPC server entirely |
| NIRI_SOCKET | Override the IPC socket path (set automatically by MorgenWM) |
| XDG_RUNTIME_DIR | Used to locate the default socket path |
| XDG_CONFIG_HOME | Used to locate the config file |
| XKB_DEFAULT_LAYOUT | Set automatically from keyboard_layout in config |
| XDG_CURRENT_DESKTOP | Set to morgenwm if not already set (affects XDG autostart filtering) |
| MOGENWM_FOCUSED_OUTPUT | Set to the focused output name before spawning a Spawn: binding, so launchers (fuzzel, wbg, ...) can open on the right monitor |
| MOGENWM_SHOW_WELCOME | Set to 1/0 from the resolved show_welcome config before running autostart commands, so the welcome helper can skip itself without re-parsing the config |