diff --git a/man/ostree-admin-deploy.xml b/man/ostree-admin-deploy.xml
index 30b5e90ac3..5ad71cafca 100644
--- a/man/ostree-admin-deploy.xml
+++ b/man/ostree-admin-deploy.xml
@@ -126,7 +126,7 @@ License along with this library. If not, see .
The deployment will not be "finalized" by default on shutdown; to later
- queue it, use ostree admin unlock-finalization.
+ queue it, use ostree admin lock-finalization --unlock.
diff --git a/man/ostree-admin-lock-finalization.xml b/man/ostree-admin-lock-finalization.xml
index 0dc31e8b8c..ac469b7ca1 100644
--- a/man/ostree-admin-lock-finalization.xml
+++ b/man/ostree-admin-lock-finalization.xml
@@ -61,7 +61,8 @@ License along with this library. If not, see .
will be set into a "finalization locked" state, which means it will not be queued for the next boot by default.
- This is the same as the --lock-finalization argument for ostree admin deploy.
+ This is the same as the --lock-finalization argument for ostree admin deploy,
+ which is the recommended way to use this feature in a race-free way.
However more commonly, one will use the --unlock argument for this command to later unlock
diff --git a/tests/kolainst/destructive/finalization.sh b/tests/kolainst/destructive/finalization.sh
new file mode 100755
index 0000000000..34624f5fa8
--- /dev/null
+++ b/tests/kolainst/destructive/finalization.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+set -xeuo pipefail
+
+. ${KOLA_EXT_DATA}/libinsttest.sh
+
+require_writable_sysroot
+prepare_tmpdir
+
+case "${AUTOPKGTEST_REBOOT_MARK:-}" in
+ "")
+ # Need to disable gpg verification for test builds
+ sed -i -e 's,gpg-verify=true,gpg-verify=false,' /etc/ostree/remotes.d/*.conf
+ # xref https://github.com/coreos/coreos-assembler/pull/2814
+ systemctl mask --now zincati
+
+ # Create a synthetic commit for upgrade
+ ostree commit --no-bindings --parent="${host_commit}" -b staged-deploy -I --tree=ref="${host_commit}"
+ newcommit=$(ostree rev-parse staged-deploy)
+ ostree admin deploy --lock-finalization staged-deploy
+ systemctl show -p ActiveState ostree-finalize-staged.service | grep active
+ ostree admin status > status.txt
+ assert_file_has_content status.txt 'finalization locked'
+ # Because finalization was locked, we shouldn't deploy on shutdown
+ /tmp/autopkgtest-reboot "2"
+ ;;
+ "2")
+ # Verify we didn't finalize
+ newcommit=$(ostree rev-parse staged-deploy)
+ booted=$(rpm-ostree status --json | jq -r .deployments[0].checksum)
+ assert_not_streq "${newcommit}" "${booted}"
+ prev_bootid=$(journalctl --list-boots -o json |jq -r '.[] | select(.index == -1) | .boot_id')
+ journalctl -b $prev_bootid -u ostree-finalize-staged.service > svc.txt
+ assert_file_has_content svc.txt 'Not finalizing'
+ ostree admin status > status.txt
+ assert_not_file_has_content status.txt 'finalization locked'
+
+ # Now re-deploy
+ ostree admin deploy --lock-finalization staged-deploy
+ ostree admin status > status.txt
+ assert_file_has_content status.txt 'finalization locked'
+ # And unlock
+ ostree admin lock-finalization --unlock
+ ostree admin status > status.txt
+ assert_not_file_has_content status.txt 'finalization locked'
+
+ /tmp/autopkgtest-reboot "3"
+ ;;
+ "3")
+ newcommit=$(ostree rev-parse staged-deploy)
+ booted=$(rpm-ostree status --json | jq -r .deployments[0].checksum)
+ assert_streq "${newcommit}" "${booted}"
+ prev_bootid=$(journalctl --list-boots -o json |jq -r '.[] | select(.index == -1) | .boot_id')
+ journalctl -b $prev_bootid -u ostree-finalize-staged.service > svc.txt
+ assert_file_has_content svc.txt 'Bootloader updated'
+ ;;
+ *) fatal "Unexpected AUTOPKGTEST_REBOOT_MARK=${AUTOPKGTEST_REBOOT_MARK}" ;;
+esac