Skip to main content

max / alloy

install: stop displacing the greeter on machines the installer never runs on Every machine installed from the ISO booted to a dead VT1: no greeter, no getty, a bare cursor and no way to log in. The boot was otherwise perfect, which is what made it hard to see. graphical.target reached, hostname applied, SELinux enforcing, no failed units, and greetd not merely failing but never mentioned in the journal at all. alloy-installer.service is shipped enabled and gated with ConditionKernelCommandLine, so it cannot run on an installed machine. It also declared Conflicts=getty@tty1.service greetd.service, and Conflicts is applied when the start job is ENQUEUED, not when the unit activates. So multi-user.target pulled the unit in, systemd queued stop jobs for greetd and getty@tty1, and only then did the condition fail and skip the installer. The gate worked. The two units it displaced on the way were gone anyway. Stop them from ExecStartPre instead, which runs after the condition has passed, so an installed machine never touches either one. The leading `-` keeps stopping a unit that is not running from failing the install. Also drops `exec xdg-user-dirs-update` from the sway config, added earlier today in e074d79 on a premise that turns out to be false. Its comment said nothing in this session activates graphical-session-pre.target, so the unit could never run. It does run: sway-systemd ships /etc/sway/config.d/10-systemd-session.conf, whose session.sh starts sway-session.target, which binds graphical-session.target and wants graphical-session-pre.target. The uid 1000 journal reaches all three and finishes xdg-user-dirs.service on its own. The line was redundant and the comment was wrong; both are gone. GO task 6a601881 has the evidence. Verified by installing from a rebuilt ISO in QEMU and booting the result with nothing masked: Started greetd.service - Greeter daemon. alloy-installer.service - Alloy installer skipped, unmet condition check tuigreet drew, the account authenticated through PAM, sway came up, and ~/Documents and friends exist without the exec line that used to make them.
Co-Authored-By
Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-22 03:04 UTC
Signed with PGP, not checked
Commit: 7b977b2ca1e8789c1c0a30862ec655f7ff15fa0c
Parent: c745d9f
2 files changed, +28 insertions, -13 deletions
@@ -6,18 +6,35 @@
6 6 # machine is a way to lose a disk. `alloy.installer` is set only by the
7 7 # GRUB entries in build/build-iso.sh, so this unit is inert everywhere else.
8 8 #
9 - # Conflicts with greetd because both want VT1. On the live medium there is
10 - # no account to log into yet, so the greeter has nothing to offer and would
11 - # only race the installer for the terminal.
9 + # greetd and getty@tty1 have to be out of the way, because all three want
10 + # VT1. On the live medium there is no account to log into yet, so the
11 + # greeter has nothing to offer and would only race the installer for the
12 + # terminal. They are stopped from ExecStartPre rather than declared in
13 + # `Conflicts=`, and that is not a style choice:
14 + #
15 + # `Conflicts=` is applied when the start job is ENQUEUED, not when the unit
16 + # activates. The condition above is checked later, when the job runs. So on
17 + # an installed machine multi-user.target would pull this unit in, systemd
18 + # would queue stop jobs for greetd and getty@tty1, and only then would the
19 + # condition fail and skip the installer. The two units it displaced never
20 + # start, nothing owns VT1, and the machine boots to a black screen with a
21 + # cursor and no way to log in. That is what shipped: found 2026-07-21 by
22 + # installing from the ISO in QEMU and booting the result, where the journal
23 + # showed graphical.target reached, no failed units, and zero mentions of
24 + # greetd anywhere. Masking this one unit brought tuigreet straight back.
25 + #
26 + # ExecStartPre runs only after the condition has passed, so an installed
27 + # machine never touches either unit. The leading `-` keeps a stop of a unit
28 + # that is not running from failing the install before it starts.
12 29 [Unit]
13 30 Description=Alloy installer
14 31 Documentation=https://git.sr.ht/~maxmj/alloy
15 32 ConditionKernelCommandLine=alloy.installer
16 33 After=systemd-user-sessions.service systemd-logind.service
17 34 Before=getty.target
18 - Conflicts=getty@tty1.service greetd.service
19 35
20 36 [Service]
37 + ExecStartPre=-/usr/bin/systemctl stop getty@tty1.service greetd.service
21 38 # Type=idle so the boot's own console output has finished before a
22 39 # full-screen TUI takes the terminal; without it the first frame draws
23 40 # over service status lines and the user sees a torn screen.
@@ -81,15 +81,13 @@
81 81 # -------------------------------------------------------------------
82 82 # Startup
83 83 # -------------------------------------------------------------------
84 - # XDG user directories (~/Documents, ~/Downloads, ~/Pictures ...). The
85 - # package ships xdg-user-dirs.service and Fedora's own user preset
86 - # enables it, but the unit is WantedBy=graphical-session-pre.target and
87 - # greetd exec's sway directly, so nothing in this session ever activates
88 - # that target. Run it here, which is the same reason swayosd-server is
89 - # exec'd below rather than left to its user unit. Whether user units
90 - # should be activated at all under this session is an open question, not
91 - # something this line settles.
92 - exec xdg-user-dirs-update
84 + # XDG user directories (~/Documents, ~/Downloads, ~/Pictures ...) are not
85 + # created here. xdg-user-dirs.service does it, and it runs: sway-systemd's
86 + # /etc/sway/config.d/10-systemd-session.conf exec's session.sh, which
87 + # starts sway-session.target, which binds graphical-session.target and
88 + # wants graphical-session-pre.target. Confirmed on an installed machine by
89 + # reading the uid 1000 journal, which reaches all three and finishes
90 + # xdg-user-dirs.service. User units work here the ordinary way.
93 91 exec mkdir -p ~/Pictures/Screenshots
94 92 # Clipboard history: two watchers per docs/STACK.md#clipboard-history.
95 93 exec wl-paste --type text --watch cliphist store