diff --git a/Rakefile b/Rakefile
index 32c278a0b93b83838dd07abb34ecc21cf13886e0..416b03d2f871f79b5a1b7e0825faf27539abb60b 100644
--- a/Rakefile
+++ b/Rakefile
@@ -439,6 +439,8 @@ task :build => ['parse_build_options', 'ensure_clean_repository', 'maybe_clean_u
       # command to modify the #{hostname} below.
       '-o', 'StrictHostKeyChecking=no',
       '-o', 'UserKnownHostsFile=/dev/null',
+      # Speed up the copy
+      '-o', 'Compression=no',
     ]
     fetch_command += artifacts.map { |a| "#{user}@#{hostname}:#{a}" }
     fetch_command << ENV['ARTIFACTS']
diff --git a/auto/build b/auto/build
index 7b2a351c185d41cc43258d8f6a596d16e61f1519..bfed3a6815fd7cb86920f18d84ae1514fad2dde3 100755
--- a/auto/build
+++ b/auto/build
@@ -85,7 +85,7 @@ find \
    config/binary_local-includes \
    config/chroot_local-includes \
    wiki/src \
-   -exec touch --date="@$SOURCE_DATE_EPOCH" '{}' \;
+   -exec touch --no-dereference --date="@$SOURCE_DATE_EPOCH" '{}' \;
 
 # build the image
 
diff --git a/auto/scripts/apt-mirror b/auto/scripts/apt-mirror
index b069aa8904bd5924e3ec2b464f595ca8ee052c2b..e2e5b8aedf43fe56f8962379379d56fc10d84344 100755
--- a/auto/scripts/apt-mirror
+++ b/auto/scripts/apt-mirror
@@ -33,19 +33,19 @@ CURRENT_BRANCH=$(git_current_branch)
 
 if [ "$BASE_BRANCH" = stable ] \
        || [ "$BASE_BRANCH" = testing ] \
-       || ( git_on_a_tag && [ "$CURRENT_BRANCH" = feature/buster ] ) \
+       || ( git_on_a_tag && [ "$CURRENT_BRANCH" = feature/bullseye ] ) \
 then
     case "$ARCHIVE" in
        debian-security)
           [ "$SERIAL" = latest ] \
               || fatal "APT snapshots are frozen for the debian-security archive," \
-                       "which should happen neither on feature/buster nor on"     \
+                       "which should happen neither on feature/bullseye nor on"     \
                         "a branch based on $BASE_BRANCH"
           ;;
        *)
           [ "$SERIAL" != latest ] \
               || fatal "APT snapshots are not frozen for the $ARCHIVE archive," \
-                       "which should happen neither on feature/buster nor on"  \
+                       "which should happen neither on feature/bullseye nor on"  \
                         "a branch based on $BASE_BRANCH"
     esac
     if version_was_released "$(version_in_changelog)"; then
@@ -61,10 +61,10 @@ then
        output_time_based_snapshot "$ARCHIVE" "$RESOLVED_SERIAL"
     fi
 else
-    if [ "$BASE_BRANCH" = devel ] || [ "$CURRENT_BRANCH" = feature/buster ]; then
+    if [ "$BASE_BRANCH" = devel ] || [ "$CURRENT_BRANCH" = feature/bullseye ]; then
         if [ "$SERIAL" != latest ]; then
             fatal "APT snapshots are frozen, which should happen neither on" \
-                  "feature/buster nor on a branch based on the devel one"
+                  "feature/bullseye nor on a branch based on the devel one"
         fi
     fi
     output_time_based_snapshot "$ARCHIVE" "$RESOLVED_SERIAL"
diff --git a/auto/scripts/create-usb-image-from-iso b/auto/scripts/create-usb-image-from-iso
index ebc78bf4d50042dd15ecdf134ad3e0888a2b98db..d5d5186101a669dfae26c59ff263a0611287668d 100755
--- a/auto/scripts/create-usb-image-from-iso
+++ b/auto/scripts/create-usb-image-from-iso
@@ -106,7 +106,7 @@ class ImageCreator(object):
             self.create_partition()
             # udisks' create_partition function seems to ignore arg_type
             # in Stretch, so we set it via sgdisk.
-            # XXX:Buster: Remove set_partition_type
+            # XXX: Remove set_partition_type once our Vagrant box runs Buster (#16868)
             self.set_partition_type()
             self.set_partition_flags()
             # XXX: Rescan?
diff --git a/config/chroot_apt/preferences b/config/chroot_apt/preferences
index ccdc0c2d75ca719c8f03c5ec769211fc896c50ac..457af5c87658eb2aea3d2717f3e6b22017f43eb7 100644
--- a/config/chroot_apt/preferences
+++ b/config/chroot_apt/preferences
@@ -10,7 +10,7 @@ Package: b43-fwcutter
 Pin: release o=Debian,n=sid
 Pin-Priority: 999
 
-Explanation: unavailable in stretch and stretch-backports, version in sid is intentionally broken (Debian#928518)
+Explanation: unavailable in Buster, version in sid is intentionally broken (Debian#928518)
 Package: electrum python3-electrum
 Pin: origin deb.tails.boum.org
 Pin-Priority: 999
diff --git a/config/chroot_local-hooks/10-tbb b/config/chroot_local-hooks/10-tbb
index a4cb1255474cff016c2f10c61c3f5ee0ac8247b6..7fa0bff7028142f88f6271657ebb614802b7982b 100755
--- a/config/chroot_local-hooks/10-tbb
+++ b/config/chroot_local-hooks/10-tbb
@@ -74,7 +74,7 @@ install_tor_browser() {
     # instead of the system one, whenever ours is too old.
     # For details see projects/firefox/abicheck.cc in
     # https://git.torproject.org/builders/tor-browser-build.git
-    # Tor Browser 8.0a10 requires GLIBCXX_3.4.22, which Stretch has
+    # Tor Browser 8.0a10 requires GLIBCXX_3.4.22, which Buster has
     # so disable this for now.
     # cp "${prep}"/TorBrowser/Tor/libstdc++.so.6 "${prep}"
 
diff --git a/config/chroot_local-hooks/12-kernel-modules-build-environment b/config/chroot_local-hooks/12-kernel-modules-build-environment
index e7549e3c81a97dc7270fc5b15c4437fc7ff2be5b..a3bb017c26d99f4420c468b900c86f8907186130 100755
--- a/config/chroot_local-hooks/12-kernel-modules-build-environment
+++ b/config/chroot_local-hooks/12-kernel-modules-build-environment
@@ -11,9 +11,11 @@ echo "Setting up a build environment for kernel modules"
 # Import ensure_hook_dependency_is_installed()
 . /usr/local/lib/tails-shell-library/build.sh
 
+# This hack is not needed on Buster but let's keep the commented code around
+# for next time we need it.
+#
 # # Install gcc-6 and fake linux-compiler-gcc-8-x86
 # # (linux-headers-4.19+ depends on it, but Stretch hasn't GCC 8)
-# # XXX:Buster: remove this hack.
 # ensure_hook_dependency_is_installed gcc-6
 # NEWEST_INSTALLED_KERNEL_VERSION="$(
 #     dpkg-query --showformat '${Version}\n' --show 'linux-image-*-amd64' \
diff --git a/config/chroot_local-hooks/40-pinentry b/config/chroot_local-hooks/40-pinentry
index 0736147045e62a856b5201935101b957a9c34aee..4fbd6a95c0e3ec4ca3c70e79d418fad3e89210b2 100755
--- a/config/chroot_local-hooks/40-pinentry
+++ b/config/chroot_local-hooks/40-pinentry
@@ -8,7 +8,7 @@ for alternative in pinentry pinentry-x11 ; do
    update-alternatives --set "$alternative" /usr/bin/pinentry-gtk-2
 done
 
-# XXX:Buster remove once Debian bug #869416 is fixed
+# XXX:Bullseye remove once Debian bug #869416 is fixed
 mkdir -p /usr/lib/pinentry
 dpkg-divert --add --rename --divert \
             /usr/lib/pinentry/pinentry-gtk-2 \
diff --git a/config/chroot_local-hooks/52-update-rc.d b/config/chroot_local-hooks/52-update-rc.d
index ec54037a0220965765ba95e0aa54aa7e099a8ac2..fae1a00e572a95e7082455a95778ba092162bf3f 100755
--- a/config/chroot_local-hooks/52-update-rc.d
+++ b/config/chroot_local-hooks/52-update-rc.d
@@ -1,10 +1,11 @@
 #!/bin/sh
 
 set -e
+set -u
 
 ### Tweak systemd unit files
 
-# Workaround for https://bugs.debian.org/714957
+# Workaround for https://bugs.debian.org/934389
 systemctl enable memlockd.service
 
 # Enable our own systemd unit files
@@ -32,10 +33,9 @@ systemctl --global enable tails-virt-notify-user.service
 systemctl --global enable tails-wait-until-tor-has-bootstrapped.service
 
 # Use socket activation only, to delay the startup of cupsd.
-# In practice, on Jessie this means that cupsd is started during
+# In practice, this means that cupsd is started during
 # the initialization of the GNOME session, which is fine: by then,
 # the persistent /etc/cups has been mounted.
-# XXX: make sure it's the case on Stretch, adjust if not.
 systemctl disable cups.service
 systemctl enable  cups.socket
 
@@ -50,7 +50,8 @@ systemctl disable NetworkManager.service
 systemctl disable NetworkManager-wait-online.service
 
 # systemd-networkd fallbacks to Google's nameservers when no other nameserver
-# is provided by the network configuration. In Jessie, this service is disabled
+# is provided by the network configuration. As of Debian Buster,
+# this service is disabled
 # by default, but it feels safer to make this explicit. Besides, it might be
 # that systemd-networkd vs. firewall setup ordering is suboptimal in this respect,
 # so let's avoid any risk of DNS leaks here.
diff --git a/config/chroot_local-hooks/99-zzzzzz_reproducible-builds-post-processing b/config/chroot_local-hooks/99-zzzzzz_reproducible-builds-post-processing
index 55e0a01cef8d3f1797b870c0567c71d8c1db9aa3..11dc7eccd4f82cd6132a14d9a75484631fbb2204 100644
--- a/config/chroot_local-hooks/99-zzzzzz_reproducible-builds-post-processing
+++ b/config/chroot_local-hooks/99-zzzzzz_reproducible-builds-post-processing
@@ -35,14 +35,6 @@ rm /var/lib/systemd/catalog/database
 # Remove logs.
 rm -r /var/lib/dkms/*/*/*/*/log
 
-# Post-process /etc/shadow by setting the sp_lstchg field to the number of days
-# since SOURCE_DATE_EPOCH instead of 1st Jan 1970. (#12339)
-# XXX:Buster: drop this if https://bugs.debian.org/857803 is fixed.
-cut -d: -f1 /etc/shadow | \
-    xargs -L1 \
-        chage --lastday \
-            "$(($(date --utc --date "@${SOURCE_DATE_EPOCH}" "+%s") / 86400))"
-
 # A user reported all executable bits of /etc/hostname being set when
 # trying to reproduce Tails 3.1. See #13623 for details.
 chmod u=rw,go=r /etc/hostname
diff --git a/config/chroot_local-includes/etc/skel/.local/share/applications/mimeapps.list b/config/chroot_local-includes/etc/skel/.local/share/applications/mimeapps.list
index 301d35e7259c17cb19ef0384d16658eef8238037..616dbda0225b57eb248e39b4008bed8299c86584 100644
--- a/config/chroot_local-includes/etc/skel/.local/share/applications/mimeapps.list
+++ b/config/chroot_local-includes/etc/skel/.local/share/applications/mimeapps.list
@@ -1,5 +1,8 @@
-# XXX: Stretch -- the seahorse associations in here fix:
+# The seahorse associations in here fix:
 # - https://bugs.freedesktop.org/show_bug.cgi?id=93656
+#   aka. https://gitlab.freedesktop.org/xdg/shared-mime-info/issues/39
+#   aka. https://bugs.freedesktop.org/show_bug.cgi?id=93656
+#   aka. https://bugs.debian.org/913550
 # - Tails#10889
 # - Tails#10571
 # - Tails#10943
diff --git a/config/chroot_local-includes/etc/sudoers.d/zzz_tails-additional-software b/config/chroot_local-includes/etc/sudoers.d/zzz_tails-additional-software
index 11baa4827d6afe5b0da5e52de3db519da192aeb0..37878169660f457edb4d54fc6becd41358c5d3d9 100644
--- a/config/chroot_local-includes/etc/sudoers.d/zzz_tails-additional-software
+++ b/config/chroot_local-includes/etc/sudoers.d/zzz_tails-additional-software
@@ -1,4 +1,4 @@
-# XXX:Buster this sudo rule should be replaced by a polkit rule once we have
+# XXX:Bullseye this sudo rule should be replaced by a polkit rule once we have
 # policykit >= 0.106. The rule is already in
 # [[blueprint/additional_software_packages/org.boum.tails.additional-software.rules]]
 # and should be installed in /usr/share/polkit-1/rules.d/
diff --git a/config/chroot_local-includes/lib/live/config/1500-reconfigure-APT b/config/chroot_local-includes/lib/live/config/1500-reconfigure-APT
index 60c68876a18485e0f98d12c1a47768370419bfa8..2212fec61a26f5655bc446d54a5c049552fd0afa 100755
--- a/config/chroot_local-includes/lib/live/config/1500-reconfigure-APT
+++ b/config/chroot_local-includes/lib/live/config/1500-reconfigure-APT
@@ -107,7 +107,7 @@ s{
    ^
    (Pin:\s+release\s+)
    o=Debian
-   (,[an]=stretch-backports)
+   (,[an]=buster-backports)
    $
 }{$1o=Debian Backports$2}xms;
 ' | perl -pi - /etc/apt/preferences
diff --git a/config/chroot_local-includes/lib/systemd/system/tails-gdm-failed-to-start.service b/config/chroot_local-includes/lib/systemd/system/tails-gdm-failed-to-start.service
index ae598f88d472e4283c49dc492e1a255b8d504683..c7632de98cfc316c2e0e22c8be7ca7d6e050dff7 100644
--- a/config/chroot_local-includes/lib/systemd/system/tails-gdm-failed-to-start.service
+++ b/config/chroot_local-includes/lib/systemd/system/tails-gdm-failed-to-start.service
@@ -20,9 +20,7 @@ Type=oneshot
 # watch_for_coldplug_completion will set up a watcher and return before
 # there's any place where plymouthd can create a seat to display its
 # splash and messages on. So we tell plymouthd to ignore udev which makes
-# it create a fallback seat.
-# XXX:Buster: check if plymouth.ignore-udev is still necessary (this code path
-# has changed in plymouth 0.9.3)
+# it create a fallback seat. The removal of this hack is tracked on #16964.
 ExecStart=/bin/sh -c \
     '/sbin/plymouthd --mode=shutdown --tty=tty5 \
         --kernel-command-line="plymouth.ignore-udev $(cat /proc/cmdline)"'
diff --git a/config/chroot_local-includes/usr/lib/systemd/user/tails-additional-software-install.service b/config/chroot_local-includes/usr/lib/systemd/user/tails-additional-software-install.service
index fff9413ba385453bb7ded96ab4772109afb5ea9c..ace956d1ee35745b171165df54a5ec25b86c15d1 100644
--- a/config/chroot_local-includes/usr/lib/systemd/user/tails-additional-software-install.service
+++ b/config/chroot_local-includes/usr/lib/systemd/user/tails-additional-software-install.service
@@ -9,7 +9,7 @@ Documentation=https://tails.boum.org/contribute/design/persistence/
 Type=oneshot
 RemainAfterExit=yes
 ExecStart=/usr/bin/sudo /bin/systemctl start tails-additional-software-install.service
-# XXX:Buster: when policykit-1 >= 0.106 is available in Tails, we should
+# XXX:Bullseye: when policykit-1 >= 0.106 is available in Tails, we should
 # use the following, and remove sudoers.d configuration:
 #ExecStart=/bin/systemctl start tails-additional-software-install.service
 TimeoutStartSec=0
diff --git a/config/chroot_local-includes/usr/local/lib/start-systemd-desktop-target b/config/chroot_local-includes/usr/local/lib/start-systemd-desktop-target
index f4905b461401246a0475cbeb546666a28e1eb2bf..33331a018fb09244a3ba193c6e8a3de7b5b52a0f 100755
--- a/config/chroot_local-includes/usr/local/lib/start-systemd-desktop-target
+++ b/config/chroot_local-includes/usr/local/lib/start-systemd-desktop-target
@@ -3,8 +3,6 @@
 set -e
 set -u
 
-# XXX: check if we still need that in Stretch
-
 # Import (almost all) XDG_*, locale-related and DBUS_SESSION_BUS_ADDRESS variables
 # into the systemd user instance's environment. We're filtering some
 # XDG_* out in order not to pretend that processes run via `systemd --user`
diff --git a/config/chroot_local-includes/usr/local/sbin/live-persist b/config/chroot_local-includes/usr/local/sbin/live-persist
index 8bdba4f76c1e24c57fbad27c022fb55f33f63b5c..656a18c7fd8989468b5ec52f91b8e0c309b5b6f4 100755
--- a/config/chroot_local-includes/usr/local/sbin/live-persist
+++ b/config/chroot_local-includes/usr/local/sbin/live-persist
@@ -434,31 +434,6 @@ activate_volumes ()
 	fi
 	rm -f ${custom_mounts} 2> /dev/null
 
-	# Update persistent GnuPG configuration for Stretch
-	if mountpoint --quiet /home/amnesia/.gnupg ; then
-		# Install current dirmngr.conf if there is no persistent one
-		if [ ! -e /home/amnesia/.gnupg/dirmngr.conf ]
-		then
-			install --owner amnesia --group amnesia --mode 0600 \
-			        /etc/skel/.gnupg/dirmngr.conf \
-			        /home/amnesia/.gnupg/dirmngr.conf \
-			|| warning "Could not install dirmngr.conf"
-		fi
-		# Disable gpg.conf settings that either are obsolete,
-		# or would break communication with keyservers
-		if [ -e /home/amnesia/.gnupg/gpg.conf ]
-		then
-			obsolete_keyserver_options_str='http-proxy|ca-cert-file'
-			obsolete_keyserver_options_bool='no-try-dns-srv|no-honor-keyserver-url'
-			sed -i --regexp-extended \
-			    "s/^(keyserver\s+)/#\1/ ; \
-			     s/^(keyserver-options\s+($obsolete_keyserver_options_str)=)/\#\\1/ ; \
-			     s/^(keyserver-options\s+($obsolete_keyserver_options_bool))\$/\#\\1/" \
-			    /home/amnesia/.gnupg/gpg.conf \
-			|| warning "Could not update gpg.conf"
-		fi
-	fi
-
 	# Get rid of any Enigmail configuredVersion that we previously used
 	# to set in a way that would persistently override the value maintained
 	# by Enigmail itself (#12680, #15693). We stopped writing this pref
diff --git a/config/chroot_local-includes/usr/share/applications/tails-documentation.desktop.in b/config/chroot_local-includes/usr/share/applications/tails-documentation.desktop.in
index d7bd7732b7a78f02732889933cc491660674e14e..fb3bef63493025e98567f60f207799c877ccd408 100644
--- a/config/chroot_local-includes/usr/share/applications/tails-documentation.desktop.in
+++ b/config/chroot_local-includes/usr/share/applications/tails-documentation.desktop.in
@@ -7,5 +7,5 @@ Categories=Documentation;Tails;
 Type=Application
 Terminal=false
 Exec=/usr/local/bin/tails-documentation doc
-Icon=/usr/share/icons/gnome/48x48/categories/system-help.png
+Icon=tails-help
 StartupNotify=true
diff --git a/config/chroot_local-packageslists/tails-common.list b/config/chroot_local-packageslists/tails-common.list
index 589e499017d9a8bc770ebd0c51d3bf522a2fd4ba..e93671eb407b3eb09ae2f8c0b1a5cb1f3c704604 100644
--- a/config/chroot_local-packageslists/tails-common.list
+++ b/config/chroot_local-packageslists/tails-common.list
@@ -39,7 +39,7 @@ acl
 zenity
 # Needed by tails-htp-notify-user and others
 libdesktop-notify-perl
-# Needed by tails-persistence-setup on Buster
+# Needed by tails-persistence-setup
 libblockdev-crypto2
 # Needed by tails-transform-mirror-url
 nodejs
@@ -110,6 +110,7 @@ gnome-menus
 gnome-power-manager
 gnome-screenshot
 gnome-session
+gnome-shell
 gnome-shell-extension-desktop-icons
 gnome-shell-extension-top-icons-plus
 gnome-shell-extensions
@@ -356,9 +357,7 @@ printer-driver-hpcups
 printer-driver-gutenprint
 printer-driver-postscript-hp
 
-### Make the MAT more powerful
-libimage-exiftool-perl
-python-cairo
+### Enable MAT2's Nautilus extension
 python-nautilus
 
 ### Needed by virtualbox-guest-utils
diff --git a/features/step_definitions/browser.rb b/features/step_definitions/browser.rb
index f2c2d49d2825582411ba965fa44c0eda282c43c8..d1a0f44ffdb869451adcf804fc33e440bfd30ee3 100644
--- a/features/step_definitions/browser.rb
+++ b/features/step_definitions/browser.rb
@@ -218,10 +218,10 @@ Then /^Tails homepage loads in the Unsafe Browser$/ do
 end
 
 Then /^the Tor Browser shows the "([^"]+)" error$/ do |error|
-  page = @torbrowser.child("Problem loading page - Tor Browser", roleName: "frame")
-  headers = page.children(roleName: "heading")
-  found = headers.any? { |heading| heading.text == error }
-  raise "Could not find the '#{error}' error in the Tor Browser" unless found
+  try_for(60) {
+    page = @torbrowser.child("Problem loading page - Tor Browser", roleName: "frame")
+    page.children(roleName: "heading").any? { |heading| heading.text == error }
+  }
 end
 
 Then /^I can listen to an Ogg audio track in Tor Browser$/ do
diff --git a/wiki/src/contribute/release_process.mdwn b/wiki/src/contribute/release_process.mdwn
index 4eccabe423fa88963d95467bd3c11d3cf9f3d947..a422ad840a9cf50de254c058410a2ffbbe22005a 100644
--- a/wiki/src/contribute/release_process.mdwn
+++ b/wiki/src/contribute/release_process.mdwn
@@ -314,15 +314,15 @@ Update other base branches
    during the freeze. It's fine if that results in a no-op
    (it depends on how exactly previous operations were performed).
 
-3. Merge `devel` into `feature/buster`, *without* following the instructions for
+3. Merge `devel` into `feature/bullseye` (if it exists), *without* following the instructions for
    [[merging base branches|APT_repository/custom#workflow-merge-main-branch]].
-   (For now `feature/buster` is handled as any other topic branch
+   (For now `feature/bullseye` is handled as any other topic branch
    forked off `devel`: its base branch is set to `devel`.)
    If the merge conflicts don't look like something you feel confident
    resolving properly, abort this merge and let the Foundations
    Team know.
 
-4. Ensure that the release, `devel` and `feature/buster` branches
+4. Ensure that the release, `devel` and `feature/bullseye` (if it exists) branches
    have the expected content in `config/APT_overlays.d/`: e.g. it must
    not list any overlay APT suite that has been merged already.
 
@@ -330,7 +330,7 @@ Update other base branches
 
         git push origin                          \
            "${RELEASE_BRANCH:?}:${RELEASE_BRANCH:?}" \
-           feature/buster:feature/buster       \
+           $(if git describe feature/bullseye >/dev/null 2>&1; then echo feature/bullseye:feature/bullseye ; fi) \
            devel:devel
 
 Update more included files