LEdoian's Blog

Migrating postmarketOS v25.06 from OpenRC to systemd

Recently, postmarketOS v25.06 came out, which brings support for using systemd as the service manager and init system. Both systemd and OpenRC are supported, but there is no official supported migration method for changing the init system:

So for this one-time scenario we ask you to please reinstall postmarketOS to get from OpenRC to systemd. Thank you for your understanding!

Moreover, SXMO images are still by default powered by OpenRC and need manual tweaks for systemd.

But I just didn't want to lose the legacy accumulated in the phone (mostly WiFi networks, random offline data, weird firewall config &c.), didn't want to go through the pain of making sure I migrate everything correctly and don't mind some breakage. (We will find some other parts of the legacy throughout this post :-D)

Disclaimer: This is unofficial, unsupported writeup of an experience. If something breaks by following this post, don't complain in the official channels or be very explicit what you did.

How it started

Is there an unofficial guide/writeup/set of hints about how to change the service manager from OpenRC to systemd? I know that the recommended solution is reinstalling, but there are a lot of tweaks on my phone I don't want to lose nor do a lot of dancing to migrate them to new installation, and I think I can handle a bit of chaos that would stem from that. (And I promise never to report bugs that I would not reproduce on an actually supported platform :-D)

If no guide is available, I might just apk add systemd and hope to be able to boot the phone ever again :-)

—me, 2025-06-27 13:49+0200 in #main:postmarketos.org

There were supposed to be scripts that would do that, but I only found this thing in the upgrade repo after the migration and if I read that correctly, my steps (as follow) were basically correct.

I spent a bit of time looking at what pmbootstrap does when selecting systemd as the init system, so that I don't do a completely bad move.

The night before I upgraded the system on my OnePlus 6 to v25.06, still with OpenRC, and while I had not done too much testing, it looked usable. (That is, after I recompiled my kernel [1], because I need 4in6 tunnels to connect to the internet.) For completeness, I am using SXMO on sway and tinydm to run it.

Required tools: good luck, free time and a USB cable to a computer. Being able to log in as root over SSH is recommended (but I only setup this during the upgrade).

Upgrading

  1. Add the systemd repository to /etc/apk/repositories. Its URL is https://mirror.postmarketos.org/postmarketos/extra-repos/systemd/v25.06, I think I extracted it from a random pre-built GNOME image.

  2. Do apk update and apk upgrade --available just because it sounded like a good idea. (It doesn't a day later, but since I was going to upgrade to systemd anyway, I don't think it was risky either.):

    Krypton:~# apk upgrade --available
    The following packages will be REMOVED:
      kpartx networkmanager-doc pipewire-openrc polkit-common polkit-elogind polkit-elogind-libs polkit-openrc wireplumber-openrc
    The following NEW packages will be installed:
      plasma-workspace-x11 polkit polkit-libs systemd-libs
    The following packages will be upgraded:
      accountsservice accountsservice-lang cups-dev cups-libs dbus dbus-daemon-launch-helper dbus-dev dbus-doc dbus-libs dbus-x11 drkonqi drkonqi-lang gst-plugin-pipewire
      kscreenlocker kscreenlocker-lang kwin kwin-doc kwin-lang libgnome-desktop-3 libnm linux-postmarketos-qcom-sdm845 multipath-tools multipath-tools-doc
      multipath-tools-openrc networkmanager networkmanager-bash-completion networkmanager-bluetooth networkmanager-cli networkmanager-dnsmasq networkmanager-lang
      networkmanager-tui networkmanager-wifi networkmanager-wwan pipewire pipewire-alsa pipewire-doc pipewire-lang pipewire-libs pipewire-pulse pipewire-spa-bluez
      pipewire-spa-tools pipewire-tools plasma-workspace plasma-workspace-doc plasma-workspace-lang plasma-workspace-libs polkit-doc polkit-lang sddm sddm-doc sddm-lang
      sddm-openrc upower upower-doc upower-lang wireplumber wireplumber-doc wireplumber-lang wireplumber-libs wireshark wireshark-common wireshark-doc xorg-server
      xorg-server-common xorg-server-doc yt-dlp yt-dlp-bash-completion yt-dlp-core yt-dlp-core-pyc yt-dlp-doc
    Need to download 95 MiB of packages.
    After this operation, 2330 KiB of additional disk space will be used.
    Do you want to continue [Y/n]?
    

    (Full log here)

    You may notice that my kernel was replaced, so I had to reinstall that. Not a big deal, it was still compiled on my machine so this was a matter of a quick rsync to the phone and installing it with apk add /tmp/linux-postmarketos-qcom-sdm845-6.14.0_rc5-r2.apk.

  3. Actually install systemd. From pmbootstrap I learned that the correct package to install is really postmarketos-base-systemd:

    Krypton:~# apk add postmarketos-base-systemd
    The following packages will be REMOVED:
      81voltd-openrc agetty-openrc alpine-conf alsa-utils-openrc avahi-openrc bird-openrc bluez-deprecated-openrc bluez-openrc bootmac-openrc bridge brightnessctl-openrc
      btrfs-progs-openrc busctl busybox-mdev-openrc busybox-openrc chrony-openrc cryptsetup-openrc dbus-openrc device-oneplus-enchilada-openrc dnsmasq-openrc elogind
      elogind-bash-completion elogind-common elogind-doc elogind-lang elogind-openrc eudev eudev-dev eudev-doc eudev-hwids eudev-libs eudev-openrc fuse-openrc fwupd-openrc
      gpsd-openrc haveged-openrc hexagonrpcd-openrc hfd-service-openrc hkdm-openrc ifupdown-ng ifupdown-ng-doc ifupdown-ng-ethtool ifupdown-ng-iproute2 ifupdown-ng-wifi
      iio-sensor-proxy-openrc iperf-openrc iperf3-openrc iptables-openrc iwd-openrc kbd-openrc logbookd logbookd-doc logbookd-openrc lxc-openrc mariadb-openrc mdev-conf
      minidlna-openrc modemmanager-openrc msm-modem-openrc msm-modem-uim-selection-openrc multipath-tools-openrc networkmanager-openrc nftables-openrc openrc
      openrc-bash-completion openrc-doc openrc-settingsd openrc-settingsd-doc openrc-settingsd-openrc openrc-user openssh-server-common-openrc pd-mapper-openrc
      pipewire-pulse-openrc postmarketos-base-nftables-openrc postmarketos-base-openrc postmarketos-base-ui-bluez-openrc postmarketos-base-ui-elogind
      postmarketos-base-ui-openrc postmarketos-base-ui-openrc-settingsd postmarketos-base-ui-plasma-openrc postmarketos-base-ui-tinydm
      postmarketos-base-ui-wifi-wpa_supplicant-openrc postmarketos-bootsplash-openrc postmarketos-tweaks-openrc postmarketos-ui-plasma-desktop-openrc
      postmarketos-ui-plasma-mobile-openrc postmarketos-ui-sxmo-de-sway-openrc q6voiced-openrc qbootctl-openrc rmtfs-openrc rsync-openrc sddm-openrc seatd-openrc
      sleep-inhibitor-openrc smartmontools-openrc soc-qcom-gstreamer-openrc soc-qcom-modem-openrc soc-qcom-openrc soc-qcom-pulseaudio-openrc soc-qcom-qbootctl-openrc
      swclock-offset-openrc syncthing-openrc tinydm-openrc tqftpserv-openrc ttyescape-openrc udev-init-scripts udev-init-scripts-openrc udisks2-openrc unbound-openrc
      urfkill-openrc util-linux-openrc waydroid-openrc wpa_supplicant-openrc zram-init-openrc
    The following NEW packages will be installed:
      81voltd-systemd alsa-utils-systemd at-spi2-core-systemd audit-libs avahi-systemd bluez-systemd bootmac-systemd dbus-systemd dconf-systemd
      device-oneplus-enchilada-systemd evolution-data-server-systemd feedbackd-systemd fprintd-systemd geoclue-systemd hexagonrpcd-systemd hfd-service-systemd
      iio-sensor-proxy-systemd iwd-systemd kactivitymanagerd-systemd kded-systemd kglobalacceld-systemd ksystemstats-systemd kwallet-pam-systemd kwin-systemd libapparmor
      lxc-systemd mmsd-tng-systemd modemmanager-systemd msm-modem-systemd networkmanager-systemd nftables-systemd openssh-server-pam-systemd pd-mapper-systemd
      pipewire-pulse-systemd pipewire-systemd plasma-mobile-systemd plasma-workspace-systemd polkit-kde-agent-1-systemd polkit-systemd postmarketos-base-systemd
      postmarketos-bootsplash-systemd postmarketos-ui-plasma-desktop-systemd power-profiles-daemon-systemd powerdevil-systemd q6voiced-systemd qbootctl-systemd rmtfs-systemd
      rtkit rtkit-doc rtkit-systemd sleep-inhibitor-systemd soc-qcom-gstreamer-systemd soc-qcom-pulseaudio-systemd swclock-offset-systemd syncthing-systemd systemd
      systemd-bash-completion systemd-doc systemd-journald systemd-journald-bash-completion systemd-lang systemd-logind systemd-logind-bash-completion
      systemd-networkd-bash-completion systemd-resolved-bash-completion systemd-services systemd-timesyncd systemd-timesyncd-bash-completion systemd-udevd
      systemd-udevd-bash-completion systemd-x11 tinydm-systemd tqftpserv-systemd udisks2-systemd upower-systemd util-linux-login util-linux-login-doc vvmd-systemd
      waydroid-systemd wireplumber-systemd wpa_supplicant-systemd xdg-desktop-portal-gtk-systemd xdg-desktop-portal-kde-systemd xdg-desktop-portal-systemd
      xdg-desktop-portal-wlr-systemd zram-init-systemd
    Need to download 8794 KiB of packages.
    After this operation, 13 MiB of additional disk space will be used.
    Do you want to continue [Y/n]?
    

    (Full log here)

    There are several packages that are a bit suspicious to be removed: alpine-conf, bridge, ifupdown-ng and postmarketos-base-ui-tinydm. The two network ones are probably harmless as networkmanager should know what it depends on. The other are worth noting down in case anything breaks. And I didn't check whether all of the OpenRC packages have systemd replacements.

    I think at this point sshd stopped working (just dropping my connection without any response), so I killed it, and when OpenRC (still running) didn't restart it, do service sshd restart. [2]

  4. Do the required tweaks to make audio in SXMO work after reboot in case it works right away. The blog post links a MR comment that links to a patch and I don't remember, how long the chain is, but eventually wiki says that the correct thing to do is enable pipewire, pipewire-pulse and wireplumber in the user instance and drop the corresponding lines from sxmo_hook_start.sh.

  5. Reboot and hope… Well, rather, hope to be able to reboot, because reboot is now obviously a systemd command, but we are still running OpenRC, so it won't work. I took a guess and did kill -TERM 1, which worked. The upstream has somewhere busybox reboot which works for obvious reasons, my solution might have been nicer to running services (not that I needed them).

Fixing

The inevitable phase. Luckily, the system booted well, unl0kr unlocked rootfs &c., so I avoided the most annoying part of getting the system to boot and getting a shell there. (Been there, done that, but that article is not final yet.)

I thought the kernel had been replaced once again, so no 4in6 tunnels and no internet, but over the USB cable it is still possible to use IPv4 and private addresses and SSH. (Checking the logs when writing this post: no, it wasn't, but I think I just didn't try. Also, since the issues were mostly local in nature, there was no need of internet connection. Turns out it is possible to live half a day with connection only on the computer, especially when you're pegged to it because you want your smartphone working :-D)

The system booted to SDDM, which is where the legacy shows up now. Yes, apart from tinydm there is SDDM installed from past experiments and for testing, it just wasn't supposed to show up by itself. Also, I do have both iwd and wpa_supplicant installed, but NetworkManager is using wpa_supplicant backend and therefore having iwd run is also bad (and they were fighting over the NIC). Nothing disabling a few services couldn't solve. Also, I rebooted the phone just to simulate the actual bootup of the system (I think. I know I rebooted the system quite frequently at this point, but I might be wrong about when exactly that happened.)

Then it turned out tinydm (or rather, sway) was not granted access to GPU, so it didn't run. Disabled that… and plasma-mobile showed up – another thing from the past. However, it worked well and I probably could have just used it. Surprisingly, kwallet still knew some WiFi passwords, so it was able to connect to the local network (but not to the internet, since I live on an IPv6 island atm).

Disabling plasma-mobile.service kind of worked, temporarily. After another reboot and a manual try, tinydm/sway still didn't want to run automatically from systemd (I don't remember whether it even updated the log at ~/.local/state/tinydm.log). So I solved a side quest and using the magic combo I tried logging in on the tty. There, I could just run sxmo_winit.sh manually, and my session appeared. Somehow (I blame D-Bus activation and systemd-xdg-autostart-generator), a while later my Plasma Mobile also appeared.

Here we enter a rather disappointing spiral of random steps trying to understand and fix both the plasma starting and tinydm not starting. I don't really know about all the changes I made (what was enabled in the systemd user instance and what was not), I went to awful lengths to try to understand what environment was tinydm supposed to run in including substituting ExecStart with socat and ncat to get a shell in that environment. I tried randomly enabling and disabling logind lingering for the user. At some point, I just ran pmbootstrap install on the computer to compare state between the phone and a correct installation.

I don't have much to take away from the above spiral, but I know about two changes: I installed acl (because I needed to inspect permissions on /dev/dri/card0) and I created the symlink /etc/systemd/system/display-manager.service pointing to tinydm.

I have no idea whether these could help (and I kind of doubt it), but at some point logind started cooperating, created the session for tinydm and the system just started booting.

I hate software. I've been trying to debug, why my random attempt at migrating from openrc to systemd (see my message from 13:49 CET) didn't work (best guess: logind was not giving tinydm access to whatever), and after poking it just long enough that I don't remember all the steps, it started working. It might have gotten fixed by me installing acl, or any other reason at this point…

—me, 2025-06-28 00:59+0200 in #main:postmarketos.org

Since then, the system boots, network works (I have rebuilt and reinstalled the kernel just in case the artifact with systemd installed would be different, I would have that), waydroid works. I didn't check much else (both because there was little time for that and because I use the smartphone more as a PDA, so the ordinary stuff like camera and calls are not too interesting for me), but I think this is a good position to be able to fix possible bugs as I discover them.

Side quest

I talked about using the magic combo (hold volume down and press power button three times). That is done by hkdm invoking ttyescape which runs buffyboard. The combo has worked for quite a long time during which many environments matured, so I was not sure whether it was still supported – it seemed working when ran as hkdm by root, but was not started automatically.

Me: Is hkdm supposed to work in v25.06? Because it does, but there is no systemd service for it… (And that is the only way I am able to run sxmo_winit.sh for now, because it has some issue with libseat and tinydm…)

craftyguy: hmm.. ya I guess we forgot to add a sysetmd unit file for starting hkdm

craftyguy: it should be fairly trivial to add this to pmaports/extra-repos/systemd/systemd-services, do you want to send a patch?

—2025-06-27 18:16+0200 in #main:postmarketos.org

Time to learn packaging for postmarketOS, I guess. Yes, it was trivial, the hardest part was getting it built, because there is no other postmarketOS in my vicinity and it sounded like it would be easier to just run that on the phone than understand pmbootstrap (it can do so many things!), but once the repository is there, it is just a matter of one abuild checksum and one abuild.

This particular repository only contains service files, so I just copied the service above and did a few tweaks. Until merged, the patch is here.


[1]This is well documented on pmOS wiki, so I will not explain that here.
[2]I have no idea why that worked, since I'd expect apk to have removed OpenRC's tools altogether at this point. It is possible that it broke after the apk upgrade --available instead, that would make sense. Also, sshd is great that even when the main process dies, the existing connections survive, so I think the restart was done over SSH as well.