diff --git a/.ci/integration.sh b/.ci/integration.sh
new file mode 100755
index 0000000000000000000000000000000000000000..aef303b0c3ca7740b80dabd5e3a0c05054389d0e
--- /dev/null
+++ b/.ci/integration.sh
@@ -0,0 +1,30 @@
+#!/bin/sh -e
+
+set -x
+
+if [ "$(id -u)" = 0 ]; then
+	exec su "${TESTUSER:-build}" -c "sh -e $0"
+fi
+
+pmaports="$(cd "$(dirname "$0")"/..; pwd -P)"
+# Make sure that the work folder format is up to date, and that there are no
+# mounts from aborted test cases (pmbootstrap#1595)
+echo "Initializing pmbootstrap"
+yes '' | ./pmbootstrap.py --details-to-stdout init
+
+./pmbootstrap.py work_migrate
+./pmbootstrap.py config aports "$pmaports"
+./pmbootstrap.py -q shutdown
+
+# TODO: make device configurable?
+device="qemu-amd64"
+# TODO: make UI configurable?
+ui="phosh"
+./pmbootstrap.py config device "$device"
+./pmbootstrap.py config ui "$ui"
+
+echo "Building $ui image for $device"
+./pmbootstrap.py -y install --zap --password 147147
+
+echo "Building $ui image for $device, with FDE"
+./pmbootstrap.py -y install --zap --password 147147 --fde
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6a3077a1f162c16ab41556b0d27f1a62c04922c4..728964a068beefe8129ea7c9070efe94e9e3c7e9 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -95,3 +95,17 @@ deploy:
     - rsync -hrvz --delete -e "ssh -p ${SSH_PORT}" public/ "${SSH_HOST}":/var/www/docs.postmarketos.org/pmbootstrap/
   environment:
     name: deploy
+
+integration:
+  stage: test
+  before_script:
+    - apk upgrade -U
+    - apk add python3 git openssl
+  script:
+    - ".ci/integration.sh"
+  after_script:
+    - "cp /home/build/.local/var/pmbootstrap/log.txt ."
+  artifacts:
+    when: on_failure
+    paths:
+      - "log.txt"
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3e9061213f556642689b92a592127a7a986005b9
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -euo pipefail
+
+doas rm -r ~/.local/var/pmbootstrap/packages/systemd-edge/aarch64/ || true
+pmbootstrap -y zap
+pmbootstrap build --force apk-tools --src \~/src/apk-tools/ || true
+pmbootstrap -y zap