diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp
index 221ab5c8a7..3675c7a3bc 100644
--- a/pcsx2-qt/QtHost.cpp
+++ b/pcsx2-qt/QtHost.cpp
@@ -94,6 +94,7 @@ static bool s_batch_mode = false;
 static bool s_nogui_mode = false;
 static bool s_start_fullscreen_ui = false;
 static bool s_start_fullscreen_ui_fullscreen = false;
+static bool s_test_config_and_exit = false;
 
 //////////////////////////////////////////////////////////////////////////
 // CPU Thread
@@ -1604,6 +1605,7 @@ void QtHost::PrintCommandLineHelp(const std::string_view& progname)
 	std::fprintf(stderr, "  -fullscreen: Enters fullscreen mode immediately after starting.\n");
 	std::fprintf(stderr, "  -nofullscreen: Prevents fullscreen mode from triggering if enabled.\n");
 	std::fprintf(stderr, "  -earlyconsolelog: Forces logging of early console messages to console.\n");
+	std::fprintf(stderr, "  -testconfig: Initializes configuration and checks version, then exits.\n");
 #ifdef ENABLE_RAINTEGRATION
 	std::fprintf(stderr, "  -raintegration: Use RAIntegration instead of built-in achievement support.\n");
 #endif
@@ -1721,6 +1723,11 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
 				s_start_fullscreen_ui = true;
 				continue;
 			}
+			else if (CHECK_ARG(QStringLiteral("-testconfig")))
+			{
+				s_test_config_and_exit = true;
+				continue;
+			}
 #ifdef ENABLE_RAINTEGRATION
 			else if (CHECK_ARG(QStringLiteral("-raintegration")))
 			{
@@ -1831,6 +1838,10 @@ int main(int argc, char* argv[])
 	if (!QtHost::InitializeConfig())
 		return EXIT_FAILURE;
 
+	// Are we just setting up the configuration?
+	if (s_test_config_and_exit)
+		return EXIT_SUCCESS;
+
 	// Set theme before creating any windows.
 	MainWindow::updateApplicationTheme();
 	MainWindow* main_window = new MainWindow();
@@ -1873,6 +1884,10 @@ int main(int argc, char* argv[])
 		delete g_main_window;
 	}
 
+	// Ensure config is written. Prevents destruction order issues.
+	if (s_base_settings_interface->IsDirty())
+		s_base_settings_interface->Save();
+
 	// Ensure emulog is flushed.
 	if (emuLog)
 	{
diff --git a/pcsx2/MTVU.h b/pcsx2/MTVU.h
index fce40e786b..f48e33c131 100644
--- a/pcsx2/MTVU.h
+++ b/pcsx2/MTVU.h
@@ -66,6 +66,7 @@ public:
 	~VU_Thread();
 
 	__fi const Threading::ThreadHandle& GetThreadHandle() const { return m_thread; }
+	__fi bool IsOpen() const { return m_thread.Joinable(); }
 
 	/// Ensures the VU thread is started.
 	void Open();
diff --git a/pcsx2/x86/microVU.cpp b/pcsx2/x86/microVU.cpp
index c48e0325ab..75eeb57aab 100644
--- a/pcsx2/x86/microVU.cpp
+++ b/pcsx2/x86/microVU.cpp
@@ -381,7 +381,8 @@ void recMicroVU0::Shutdown()
 }
 void recMicroVU1::Shutdown()
 {
-	vu1Thread.WaitVU();
+	if (vu1Thread.IsOpen())
+		vu1Thread.WaitVU();
 	mVUclose(microVU1);
 }