diff --git a/app/Actions/Development/StartDevelopmentQemuVm.php b/app/Actions/Development/StartDevelopmentQemuVm.php
index 8e52a6dcc4..11cd1a369c 100644
--- a/app/Actions/Development/StartDevelopmentQemuVm.php
+++ b/app/Actions/Development/StartDevelopmentQemuVm.php
@@ -155,10 +155,18 @@ class StartDevelopmentQemuVm
/**
* nvidia-persistenced keeps the GPU open, which blocks libvirt from moving it to vfio-pci.
+ * Other users of the GPU (for example a desktop session) must stop first: if libvirt unbinds the
+ * nvidia driver while they hold the GPU open, the GPU stays unusable until the host reboots.
*/
private function releaseHostGpu(): void
{
Process::run('systemctl stop nvidia-persistenced');
+
+ $users = Process::run('fuser -v /dev/nvidia[0-9]* 2>&1');
+
+ if ($users->successful() && trim($users->output()) !== '') {
+ throw new RuntimeException("These host processes still use the NVIDIA GPU, so it cannot move to the VM. Stop them (for example the display manager), or remove the GPU from the VM with --fresh and without --gpu:\n".trim($users->output()));
+ }
}
private function destroyVm(string $domain): void
diff --git a/tests/Feature/DevelopmentQemuVmTest.php b/tests/Feature/DevelopmentQemuVmTest.php
index 68c43ba143..3ab9355f24 100644
--- a/tests/Feature/DevelopmentQemuVmTest.php
+++ b/tests/Feature/DevelopmentQemuVmTest.php
@@ -817,3 +817,21 @@ it('seeds a gpu vm through the container so it installs the driver', function ()
Process::assertRan("docker exec 'coolify' php artisan dev:qemu:seed 'ubuntu-root' --as-localhost --gpu");
});
+
+it('does not start a gpu vm while host processes still use the gpu', function () {
+ config(['development-qemu.storage_path' => sys_get_temp_dir().'/coolify-qemu-gpu-busy-test-'.uniqid()]);
+ Process::fake([
+ '* net-dumpxml *' => Process::result(output: ''),
+ '* network inspect *' => Process::result(output: "172.18.0.0/16\n"),
+ '* dominfo *' => Process::result(output: 'exists'),
+ '* domstate *' => Process::result(output: "shut off\n"),
+ '* dumpxml --inactive *' => Process::result(output: ""),
+ 'fuser *' => Process::result(output: "/dev/nvidia0: root 1210 F...m Xorg\n"),
+ '*' => Process::result(),
+ ]);
+
+ expect(fn () => StartDevelopmentQemuVm::run('ubuntu-root'))
+ ->toThrow(RuntimeException::class, 'Xorg');
+
+ Process::assertNotRan(fn ($process) => str_contains(remoteProcessScript($process), 'virsh start'));
+});