Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

omarchy-systemd-widget

Know the moment a systemd unit fails — from your Omarchy bar.
Invisible while everything is fine. One click to restart, read the log, or clear the state.

Install · Using it · Configuration · Standalone · How it works


A backup timer that quietly stopped running two weeks ago is the classic Linux failure: nothing tells you, because nothing is broken — a unit just sits in the failed state until the day you need what it was supposed to produce. On a graphical session there is no equivalent of the systemctl --failed you'd never think to type.

This widget is that check, running for you. It stays off the bar entirely while everything is healthy, and shows up the moment something isn't.

Features

  • Both managers — system units and your own user units, which is where syncthing, pipewire and your personal timers live
  • Quiet by default — takes no bar space at all until something fails
  • One notification per failure — the first time each unit breaks, not once a minute forever, and never a storm about units already failed at startup
  • Act from the panelEnter restarts, l opens the journal in a floating terminal, x clears the failed state without restarting, c clears them all
  • Native authentication — a system unit restart goes through plain systemctl, so systemd asks polkit and Omarchy's own agent puts up the prompt; no second password path, no pkexec wrapper
  • Right action for timer jobs — a oneshot job that failed once and will run again on schedule wants its state cleared, not restarted; both are one key
  • Ignore list — silence the units you have already decided not to care about, with prefix* matching
  • Keyboard-navigable panelj/k to move, r to re-check, Esc to close
  • Cheap — one short-lived process a minute, covering both scopes and the overall manager state in a single call
  • Theme-native — built from Omarchy's own bar and panel components
  • Reusable coreUnitsCore.qml has no Omarchy dependency and works in any Quickshell config

Install

omarchy plugin add https://github.com/tpatzelt/omarchy-systemd-widget.git --enable --yes

That clones into ~/.config/omarchy/plugins/tpatzelt.systemd/ and adds the widget to the right-hand bar section. Move it wherever you like:

omarchy bar move tpatzelt.systemd --section right

Plugins run as unsandboxed code inside omarchy-shell. Omarchy deliberately installs them disabled unless you pass --enable, so you can read the source first. It's two short QML files.

Nothing on the bar after installing is the expected result — it means no unit has failed. Set hideWhenHealthy: false if you'd rather see the all-clear check, or make something fail to try it:

systemd-run --user --unit=oops-test /bin/false

Then clear it again with x in the panel.

Manual install

git clone https://github.com/tpatzelt/omarchy-systemd-widget.git \
  ~/.config/omarchy/plugins/tpatzelt.systemd
omarchy-shell shell rescanPlugins
omarchy plugin enable tpatzelt.systemd

Updating

omarchy plugin update tpatzelt.systemd

Uninstall

omarchy plugin remove tpatzelt.systemd

Using it

Action What it does
Left-click Open the panel
Right-click Re-check now (or run your own command — see onRightClick)
Middle-click Re-check now
Hover What failed, or how many things did
j / k, / Move between failed units
Enter Restart the selected unit
l Open its journal in a floating terminal
x Clear its failed state without restarting it
c Clear every failed state
r Re-check now
Tab Move to the next bar panel
Esc Close

The panel can also be opened from anywhere:

omarchy-shell tpatzelt.systemd toggle

Restart or clear?

  • Restart (Enter) for anything that is supposed to be running now — a daemon that crashed, a mount that dropped.
  • Clear (x) for a oneshot or timer-driven job that failed once and will run again on its own schedule. Restarting it runs the job immediately, which is usually not what you want at 3pm for a job that belongs at 3am.

Configuration

Settings live inline in the widget's entry in ~/.config/omarchy/shell.json. Changes there hot-reload; you do not need to restart the shell.

{
  "id": "tpatzelt.systemd",
  "hideWhenHealthy": false,
  "ignore": ["snapper-cleanup.service", "user@*"],
  "notify": true
}
Key Default What it does
interval 60 Seconds between checks
hideWhenHealthy true Take no bar space while nothing has failed
watchSystem true Watch the system manager
watchUser true Watch your user manager
notify true One desktop notification the first time each unit fails
showCount true Append the number of failures when there is more than one
ignore [] Unit names to skip. A trailing * matches a prefix
onJournal "omarchy-launch-floating-terminal-with-presentation" Run with a journalctl invocation appended. Empty hides the log button
onRightClick "" Empty means right-click re-checks
failedIcon "" Nerd Font glyph shown when something has failed
healthyIcon "" Glyph for the all-clear state

Standalone Quickshell

UnitsCore.qml is plain QtQuick plus Quickshell.Io — no Omarchy imports, no compositor assumptions. Copy it next to your own shell.qml and bind to it:

UnitsCore {
  id: sysd
  intervalMs: 60000
  onUnitFailed: function (unit, scope, description) {
    console.log("failed:", scope, unit, description)
  }
}

A runnable example lives in examples/standalone:

./scripts/run-example.sh

Quickshell only scans QML inside the config directory it is pointed at, so UnitsCore.qml has to sit next to the shell.qml that uses it — the script assembles a temporary directory containing both.

Core API

Property Type Notes
ready bool False until the first check has completed
units array { unit, load, active, sub, description, scope }, system scope first
count int Number of failed units
healthy bool Ready and nothing failed
systemState string running, degraded, starting, …
busy / actionUnit / actionKind An action in flight, attributed to its unit
lastError / lastErrorUnit string Last failure, cleared after 8s
unitFailed(unit, scope, description) signal Fires once per newly failed unit
restart(unit, scope)
resetFailed(unit, scope) / resetAll()
journalCommand(unit, scope) string A ready-to-run journalctl invocation

How it works

One process, both scopes. systemctl --failed for the system manager, the same for --user, and is-system-running, all in a single bash -c separated by a control character no unit name or description can contain. Three separate processes could land out of order and paint a frame where one scope's results are applied against the other's.

Four fields, then the rest. Each line is UNIT LOAD ACTIVE SUB DESCRIPTION, and only the description can contain spaces — so the parser splits off exactly four fields and keeps the remainder whole rather than splitting on every space.

Notifications are edge-triggered. The set of failed unit keys from the previous poll is kept, and unitFailed fires only for keys that weren't in it. The first poll only seeds that set, so restarting the shell on an already-degraded machine doesn't re-announce everything.

Restarts go through polkit, not pkexec. systemctl restart on a system unit asks polkit, and the session's agent — Omarchy ships one — puts up the prompt. Wrapping it in pkexec would work too, but it would mean a second, different authentication dialog for the same operation.

An action's exit only counts while an action is running. Process teardown during a settings hot-reload also fires exited(); treating that as a failure would latch the bar icon in the theme's urgent red.

Requirements

  • Omarchy 4 (Quattro) or newer, with omarchy-shell
  • systemctl and journalctl — you already have them
  • notify-send for notifications (libnotify), or set notify: false
  • A Nerd Font for the glyphs (Omarchy's default bar font has them)

Related

License

MIT — see LICENSE.

About

Failed systemd units in the Omarchy bar — invisible while everything is fine, with restart, journal and clear actions.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages