diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt
index e3d267ee869c958661d089d48080100cae43d54d..7c50e19a02e550320111a27fcd1771c9085bd4f5 100644
--- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt
+++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt
@@ -105,7 +105,10 @@ class MailboxService : Service() {
         startForeground(
             NOTIFICATION_MAIN_ID,
             notificationManager.getServiceNotification(
-                Starting(getString(R.string.startup_headline))
+                Starting(
+                    status = getString(R.string.startup_headline),
+                    isCancelable = false,
+                )
             )
         )
 
diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/StatusManager.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/StatusManager.kt
index 78ed570c126f163e294ee1f2c2a4648c91c54686..6d2bce89ca6c22484d27ccc892fa2298dabaa80d 100644
--- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/StatusManager.kt
+++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/StatusManager.kt
@@ -125,7 +125,7 @@ class StatusManager @Inject constructor(
     object NeedOnboarding : MailboxAppState(false)
     object NeedsDozeExemption : MailboxAppState(false)
     object NotStarted : MailboxAppState(false)
-    data class Starting(val status: String) : MailboxAppState(true)
+    data class Starting(val status: String, val isCancelable: Boolean) : MailboxAppState(true)
     data class StartedSettingUp(val qrCode: Bitmap, val link: String) : MailboxAppState(true)
     object StartedSetupComplete : MailboxAppState(true)
     object ErrorClockSkew : MailboxAppState(true)
@@ -194,16 +194,26 @@ class StatusManager @Inject constructor(
             // Keep this check below WIPING, STOPPING and STOPPED so that the online check
             // does not interfere with these states - no point in showing a network error then.
             online != null && !online -> ErrorNoNetwork
-            ls != LifecycleState.RUNNING -> Starting(getString(R.string.startup_init_app))
+            ls != LifecycleState.RUNNING -> Starting(
+                status = getString(R.string.startup_init_app),
+                isCancelable = false,
+            )
             // RUNNING
             tor != TorState.Published -> when (tor) {
-                TorState.StartingStopping -> Starting(getString(R.string.startup_init_app))
+                TorState.StartingStopping -> Starting(
+                    status = getString(R.string.startup_init_app),
+                    isCancelable = true,
+                )
                 is TorState.Enabling -> Starting(
-                    getString(R.string.startup_bootstrapping_tor, tor.percent)
+                    status = getString(R.string.startup_bootstrapping_tor, tor.percent),
+                    isCancelable = true,
                 )
                 TorState.ClockSkewed -> ErrorClockSkew
                 TorState.Inactive -> ErrorNoNetwork
-                else -> Starting(getString(R.string.startup_publishing_onion_service))
+                else -> Starting(
+                    status = getString(R.string.startup_publishing_onion_service),
+                    isCancelable = true,
+                )
             }
             setup == SetupComplete.FALSE -> {
                 // FIXME we shouldn't do expensive calls on the UiThread
diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFragment.kt
index 739c5e62e1e49e18b1226379f04e61df3285a848..542cae88421cb8f8db81e377f57f68e5e51ae79e 100644
--- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFragment.kt
+++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFragment.kt
@@ -22,6 +22,8 @@ package org.briarproject.mailbox.android.ui
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
 import android.view.ViewGroup
 import android.widget.Button
 import android.widget.TextView
@@ -37,6 +39,7 @@ class StartupFragment : Fragment() {
 
     private val viewModel: MailboxViewModel by activityViewModels()
     private lateinit var statusDetail: TextView
+    private lateinit var cancelButton: Button
 
     override fun onCreateView(
         inflater: LayoutInflater,
@@ -48,22 +51,27 @@ class StartupFragment : Fragment() {
 
     override fun onViewCreated(v: View, savedInstanceState: Bundle?) {
         statusDetail = v.findViewById(R.id.statusDetail)
+        cancelButton = v.findViewById(R.id.button)
 
         launchAndRepeatWhileStarted {
             viewModel.appState.collect { onAppStateChanged(it) }
         }
 
         viewModel.startLifecycle()
-
-        v.findViewById<Button>(R.id.button).setOnClickListener {
-            viewModel.stopLifecycle()
-            requireActivity().finishAffinity()
-        }
     }
 
     private fun onAppStateChanged(state: MailboxAppState) {
         if (state is Starting) {
             statusDetail.text = state.status
+            if (state.isCancelable) {
+                cancelButton.visibility = VISIBLE
+                cancelButton.setOnClickListener {
+                    viewModel.stopLifecycle()
+                    requireActivity().finishAffinity()
+                }
+            } else {
+                cancelButton.visibility = GONE
+            }
         }
     }