diff --git a/main/postmarketos-ui-os-installer/APKBUILD b/main/postmarketos-ui-os-installer/APKBUILD
new file mode 100644
index 0000000000000000000000000000000000000000..7108c360abf7bc8d027820c262dc740eb1055855
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/APKBUILD
@@ -0,0 +1,95 @@
+# Reference: https://postmarketos.org/uipkg
+# Maintainer: Clayton Craft <clayton@craftyguy.net>
+pkgname=postmarketos-ui-os-installer
+pkgver=1
+pkgrel=0
+pkgdesc="UI for installing postmarketOS"
+url="https://gitlab.gnome.org/p3732/os-installer"
+# armhf blocked by gnome-shell -> gjs -> mozjs102
+arch="noarch !armhf"
+license="GPL-3.0-or-later"
+depends="
+	os-installer
+	pmbootstrap
+	postmarketos-ui-gnome
+	"
+source="
+	sysusers-os-installer.conf
+	gdm-custom.conf
+
+	config.yaml
+	distro.svg
+	pmos_setup.sh
+
+	gnome.jpg
+	kde.png
+	xfce.png
+"
+# NOTE: this UI *only* works with systemd
+options="!check pmb:systemd"
+replaces="gdm"
+
+_source440="
+	etc/doas.d/os-installer.conf
+"
+
+_source644="
+	etc/xdg/autostart/org.postmarketOS.os-installer.desktop
+	etc/gdm/custom.conf
+	usr/lib/sysusers.d/os-installer.conf
+	usr/lib/tmpfiles.d/os-installer.conf
+	usr/share/glib-2.0/schemas/00_postmarketos-ui-os-installer.override
+
+	etc/os-installer/config.yaml
+	etc/os-installer/distro.svg
+	etc/os-installer/desktops/gnome.jpg
+	etc/os-installer/desktops/kde.png
+	etc/os-installer/desktops/xfce.png
+"
+
+_source755="
+	usr/bin/pmos_setup.sh
+"
+
+flatpath() {
+	local i
+	for i in $@; do
+		echo "rootfs-$i" | sed s./.-.g
+	done
+}
+source="$(flatpath $_source440 $_source644 $_source755)"
+
+package() {
+	local i
+	for i in $_source440; do
+		install -Dm440 "$srcdir/$(flatpath "$i")" "$pkgdir/$i"
+	done
+	for i in $_source644; do
+		install -Dm644 "$srcdir/$(flatpath "$i")" "$pkgdir/$i"
+	done
+	for i in $_source755; do
+		install -Dm755 "$srcdir/$(flatpath "$i")" "$pkgdir/$i"
+	done
+
+	mkdir -p "$pkgdir"/etc/os-installer/scripts
+	ln -s /usr/bin/pmos_setup.sh \
+		"$pkgdir"/etc/os-installer/scripts/configure.sh
+	ln -s /usr/bin/pmos_setup.sh \
+		"$pkgdir"/etc/os-installer/scripts/install.sh
+	ln -s /usr/bin/pmos_setup.sh \
+		"$pkgdir"/etc/os-installer/scripts/prepare.sh
+}
+sha512sums="
+da5f5408cc58c9202989dfd5730499abe5977ef0ca7fd017dde657f1e1d84ebffb011a171778e0b74e28424ac39796aa97085ca8d9340446eb8e6def2b7e4c17  rootfs-etc-doas.d-os-installer.conf
+ebbff3d4c55d631e5c75c7de77852ea77cf0bae67349536ae7714616dbe2baf7762dd71578779670fc97d286ab8b26834e2b857bb843da421a5a4e81d96b3c37  rootfs-etc-xdg-autostart-org.postmarketOS.os-installer.desktop
+e51733f5c75649206ec99b96486fc33419541dd8ff6fe2ccd9eb8cba0920a004c6190362342ecd943c99f6d6e7c5e41e1992dbe4f3f42180dfab08fa06436911  rootfs-etc-gdm-custom.conf
+5d43fcfbbcda66497067eb665537b2e38e77f335ba58989e6a24af35f10e07cf1d10ba47cd7536de87bcf60fdb5c8582c7a84b7ffaa0c47c964b0b7ae6c95cae  rootfs-usr-lib-sysusers.d-os-installer.conf
+1d3268d5eed453a31bcdea1a9d6525a2a6f2172c7a54bdf0fbe6f01d6dbec4d4e793e5cabb03f97e80ae98a6a6712fc0391a2a1f0c19cf59f0bf698c06b10ac7  rootfs-usr-lib-tmpfiles.d-os-installer.conf
+eaaa7f76df4c997602fed8c19c8ee2e02b19bf6160d502cc090a2a978ae15e4783c56df56bbe030e65b3369a86c5dd7465f8f629ed0b4dc38763d11280ef7098  rootfs-usr-share-glib-2.0-schemas-00_postmarketos-ui-os-installer.override
+863a4f36406e18094fee410b1f1f603a4df07a47331ad2a94c1ae6d28124e7cf1c88f800013603dfd3ac88e3d2e4fee263fe0cddd89cf7cd34e2d57b3455c574  rootfs-etc-os-installer-config.yaml
+98984fd67b99400d2b08422c4d314da558d3c94ee67dbc895e7bbfd6fdf22d4b2c9d35f48f4b73b383a054f535d4089fb705c5532f034cb2ee54eab2184e8e86  rootfs-etc-os-installer-distro.svg
+48736338cb0ead62b394812455742ef162dcc4000a4059408c21d8b3881891d54d57c01c21d65c40134b1b394b943cd53732e6b437aa60aa596599783f09f025  rootfs-etc-os-installer-desktops-gnome.jpg
+302261b7e63e259d70a77a9280a7e0abd909c0d389c7e5b873bfab96d87feb46768bc3feaf1a65e0820b9c25675d98ec34c990458f1c5aa0b273c9b754ade013  rootfs-etc-os-installer-desktops-kde.png
+41528fc57c6df41440923f11d6c43ff179d437c2591406a75b38f47165d3a11f278032c449044f08cb4d335bb37be4fc42ec3998a59af9ad061449c375ce0433  rootfs-etc-os-installer-desktops-xfce.png
+9299b7c55c1dd26447cadd9ab2deae635db56abee6731e1e195a7d376e0bcf97f829d3f355f713fd028a2ed7c0b267b8906bddba7b45771299dd58aac33a04ed  rootfs-usr-bin-pmos_setup.sh
+"
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-doas.d-os-installer.conf b/main/postmarketos-ui-os-installer/rootfs-etc-doas.d-os-installer.conf
new file mode 100644
index 0000000000000000000000000000000000000000..38e0b3bb1b3842d9bf978428877399f493fcc349
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-etc-doas.d-os-installer.conf
@@ -0,0 +1 @@
+permit nopass keepenv :installer as root cmd /usr/bin/os-installer
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-gdm-custom.conf b/main/postmarketos-ui-os-installer/rootfs-etc-gdm-custom.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5eb5e72aba8509bc382533f77fc612c228183814
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-etc-gdm-custom.conf
@@ -0,0 +1,3 @@
+[daemon]
+AutomaticLoginEnable=True
+AutomaticLogin=installer
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-config.yaml b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e204957f835a3ac48f4d765622e9bff85548385b
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-config.yaml
@@ -0,0 +1,85 @@
+%YAML 1.2
+---
+distribution_name: 'postmarketOS'
+
+internet_connection_required: yes
+internet_checker_url: 'http://networkcheck.postmarketos.org'
+
+# Skip the language selection dialog by specifing a language, e.g. 'pt_BR'
+# Default: no (No fixed language, not Norwegian)
+fixed_language: no
+
+# A selection of languages the installer should present by default.
+# Note: For a language to be available, it needs to be provided by the system
+# and the installer needs to have a translation into that language.
+# Default: ['ar', 'de', 'en', 'es', 'fr', 'ja', 'ru', 'zh']
+suggested_languages:
+  - 'ar'
+  - 'de'
+  - 'en'
+  - 'es'
+  - 'fr'
+  - 'ja'
+  - 'ru'
+  - 'zh'
+
+welcome_page:
+  logo    : '/etc/os-installer/distro.svg'
+  text    : "Let's install postmarketOS!"
+  usage   : yes
+
+minimum_disk_size: 5  # GB, probably enough for most UIs?
+
+disk_encryption:
+  offered: yes
+  forced: no
+  min_length: 1
+  confirmation: yes
+
+desktop:
+  - name        : 'GNOME'
+    description : 'Simple desktop that can be personalized with extensions.'
+    image_path  : '/etc/os-installer/desktops/gnome.jpg'
+    keyword     : 'gnome'
+  - name        : 'KDE Plasma'
+    description : 'Configurable desktop that is similar to Windows.'
+    image_path  : '/etc/os-installer/desktops/kde.png'
+    keyword     : 'plasma-desktop'
+  - name        : 'Xfce'
+    description : 'Lightweight desktop with focus on modularity.'
+    image_path  : '/etc/os-installer/desktops/xfce.png'
+    keyword     : 'xfce4'
+
+user:
+  request_username: True
+  provide_autologin: False  # FIXME
+  min_password_length: 1
+  password_confirmation: yes
+
+# gnome-initial-setup can handle user and locale setup.
+# These settings allow to disable these pages.
+skip_user: no
+skip_locale: no
+
+# additional_software:
+#   - name           : 'Firefox'
+#     description    : 'Popular Web Browser from Mozilla'
+#     icon_path      : '/etc/os-installer/icons/firefox.png'
+#     keyword        : 'firefox'
+#     suggested      : yes
+
+# additional_features:
+#   - name           : 'Dummy Feature'
+#     description    : 'Does not do anything'
+#     keyword        : 'dummy'
+#   - name           : 'Snapshots'
+#     description    : 'Snapshots allow restoring a previous state of your system'
+#     icon_path      : '/etc/os-installer/icons/snapshot.svg'
+#     keyword        : 'snapshots'
+#     suggested      : yes
+
+failure_help_url: 'https://wiki.postmarketos.org'
+
+browser_cmd: 'firefox'
+disks_cmd  : 'gnome-disks'
+wifi_cmd   : 'gnome-control-center wifi'
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-gnome.jpg b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-gnome.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..fb5f36a4b732573174620a187582392725f4985d
Binary files /dev/null and b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-gnome.jpg differ
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-kde.png b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-kde.png
new file mode 100644
index 0000000000000000000000000000000000000000..40081d96e31fdf5b15472cac814705ee668111ea
Binary files /dev/null and b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-kde.png differ
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-xfce.png b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-xfce.png
new file mode 100644
index 0000000000000000000000000000000000000000..871fc9ee01770eb4e3dc55e535da79093df31939
Binary files /dev/null and b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-desktops-xfce.png differ
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-distro.svg b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-distro.svg
new file mode 100644
index 0000000000000000000000000000000000000000..3cd9ea03ab934655e3511970856d090933c1946b
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-etc-os-installer-distro.svg
@@ -0,0 +1 @@
+<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><g transform="translate(100,100) rotate(180) translate(0, 6.698729810778069)"><polygon points="29.7,17.5 25.9,27.8 15.0,26.0 0.0,0.0 65.0,0.0 58.0,8.5 65.0,17.0 29.8,17.0" fill="#090" /><g transform="translate(100,0) rotate(120) "><polygon points="29.7,17.5 25.9,27.8 15.0,26.0 0.0,0.0 65.0,0.0 58.0,8.5 65.0,17.0 29.8,17.0" fill="#090" /></g><g transform="translate(50,86.60254037844386) rotate(240) "><polygon points="29.7,17.5 25.9,27.8 15.0,26.0 0.0,0.0 65.0,0.0 58.0,8.5 65.0,17.0 29.8,17.0" fill="#090" /></g></g></svg>
diff --git a/main/postmarketos-ui-os-installer/rootfs-etc-xdg-autostart-org.postmarketOS.os-installer.desktop b/main/postmarketos-ui-os-installer/rootfs-etc-xdg-autostart-org.postmarketOS.os-installer.desktop
new file mode 100644
index 0000000000000000000000000000000000000000..4db5b62e7f41427436551b18ef7d15d183aaf5a2
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-etc-xdg-autostart-org.postmarketOS.os-installer.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Name=Install postmarketOS
+Icon=preferences-system
+Exec=doas /usr/bin/os-installer
+Terminal=false
+Type=Application
+StartupNotify=true
+Categories=GNOME;GTK;System;
+OnlyShowIn=GNOME;
+X-GNOME-HiddenUnderSystemd=false
+StartupWMClass=org.postmarketOS.os-installer
diff --git a/main/postmarketos-ui-os-installer/rootfs-usr-bin-pmos_setup.sh b/main/postmarketos-ui-os-installer/rootfs-usr-bin-pmos_setup.sh
new file mode 100644
index 0000000000000000000000000000000000000000..cc6490d13b485befca56884ea5c0e26e2efa2fe2
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-usr-bin-pmos_setup.sh
@@ -0,0 +1,80 @@
+#! /bin/sh
+
+set -eu
+
+# FIXME: Don't run this as root...
+pmb="pmbootstrap --as-root -y"
+
+if [ "$(id -u)" -ne 0 ]; then
+	echo "must be run as root! (FIXME)"
+	exit 1
+fi
+
+do_prepare() {
+	yes '' | $pmb init
+
+	# TODO: allow selecting device in the UI somehow?
+	# For now get it from deviceinfo...
+	# shellcheck disable=SC1091
+	. /usr/share/misc/source_deviceinfo
+
+	if [ -z "$deviceinfo_codename" ]; then
+		echo "unable to get device info"
+		exit 1
+	fi
+
+	$pmb config device "$deviceinfo_codename"
+}
+
+do_install() {
+	# OSI_DESKTOP             : Desktop keyword, or empty if 'desktop' was not configured
+	# OSI_LOCALE              : Locale to be used in the new system
+	# OSI_DEVICE_PATH         : Device path at which to install
+	# OSI_DEVICE_IS_PARTITION : 1 if the specified device is a partition (0 -> disk)
+	# OSI_DEVICE_EFI_PARTITION: Set if device is partition and system uses EFI boot.
+	# OSI_USE_ENCRYPTION      : 1 if the installation is to be encrypted
+	# OSI_ENCRYPTION_PIN      : The encryption pin to use (if encryption is set)
+
+	$pmb config ui "$OSI_DESKTOP"
+	$pmb config keymap "$OSI_KEYBOARD_LAYOUT"
+	$pmb config locale "$OSI_LOCALE"
+}
+
+do_configure() {
+	# The script gets called with the environment variables from the install step
+	# and these additional variables:
+	# OSI_USER_NAME          : User's name. Not ASCII-fied
+	# OSI_USER_USERNAME      : Linux username. ASCII-fied
+	# OSI_USER_AUTOLOGIN     : Whether to autologin the user
+	# OSI_USER_PASSWORD      : User's password. Can be empty if autologin is set.
+	# OSI_FORMATS            : Locale of formats to be used
+	# OSI_TIMEZONE           : Timezone to be used
+	# OSI_ADDITIONAL_SOFTWARE: Space-separated list of additional packages to install
+	# OSI_ADDITIONAL_FEATURES: Space-separated list of additional features chosen
+
+	$pmb config user "$OSI_USER_USERNAME"
+	$pmb config timezone "$OSI_TIMEZONE"
+
+	fde_args=""
+	if [ -n "$OSI_ENCRYPTION_PIN" ]; then
+		fde_args="--fde"
+	fi
+	PMB_FDE_PASSWORD="$OSI_ENCRYPTION_PIN" $pmb install --password "$OSI_USER_PASSWORD" --disk "$OSI_DEVICE_PATH" $fde_args
+}
+
+step="$(basename "$0")"
+case "$step" in
+install.sh)
+	do_install
+	;;
+prepare.sh)
+	do_prepare
+	;;
+configure.sh)
+	do_configure
+	;;
+*)
+	echo "unknown install step: $step"
+	exit 1
+	;;
+esac
diff --git a/main/postmarketos-ui-os-installer/rootfs-usr-lib-sysusers.d-os-installer.conf b/main/postmarketos-ui-os-installer/rootfs-usr-lib-sysusers.d-os-installer.conf
new file mode 100644
index 0000000000000000000000000000000000000000..2687c330afdd4628373c884a3ec36d4f65fb00db
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-usr-lib-sysusers.d-os-installer.conf
@@ -0,0 +1,13 @@
+# Create installer group
+g installer - "OS Installer Group"
+
+# Create installer user with home directory
+u installer - "OS Installer User" /home/installer /bin/ash
+
+# Add installer user to installer group
+m installer installer
+
+# Add installer user to other UI-related groups
+m installer audio
+m installer video
+m installer input
diff --git a/main/postmarketos-ui-os-installer/rootfs-usr-lib-tmpfiles.d-os-installer.conf b/main/postmarketos-ui-os-installer/rootfs-usr-lib-tmpfiles.d-os-installer.conf
new file mode 100644
index 0000000000000000000000000000000000000000..69eff096e959d3e8881c26b114aeddbbf9773c09
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-usr-lib-tmpfiles.d-os-installer.conf
@@ -0,0 +1 @@
+d /home/installer 0750 installer installer - -
diff --git a/main/postmarketos-ui-os-installer/rootfs-usr-share-glib-2.0-schemas-00_postmarketos-ui-os-installer.override b/main/postmarketos-ui-os-installer/rootfs-usr-share-glib-2.0-schemas-00_postmarketos-ui-os-installer.override
new file mode 100644
index 0000000000000000000000000000000000000000..61e5ae22f93b2367437ab6f912564700912e028d
--- /dev/null
+++ b/main/postmarketos-ui-os-installer/rootfs-usr-share-glib-2.0-schemas-00_postmarketos-ui-os-installer.override
@@ -0,0 +1,2 @@
+[org.gnome.desktop.screensaver]
+lock-enabled=false