diff --git a/app/src/components/ServerSettings/GpuAcceleration.tsx b/app/src/components/ServerSettings/GpuAcceleration.tsx
index bafcf738..b9cb70b3 100644
--- a/app/src/components/ServerSettings/GpuAcceleration.tsx
+++ b/app/src/components/ServerSettings/GpuAcceleration.tsx
@@ -246,13 +246,18 @@ export function GpuAcceleration() {
{/* CUDA download section - only show when no GPU is active (native or CUDA) */}
{!hasNativeGpu && !isCurrentlyCuda && (
<>
- {/* Download progress */}
+ {/* Download progress (manual download or auto-update) */}
{cudaDownloading && downloadProgress && (
- {downloadProgress.filename || 'Downloading CUDA backend...'}
+
+ {downloadProgress.filename ||
+ (cudaAvailable
+ ? 'Updating CUDA backend...'
+ : 'Downloading CUDA backend...')}
+
{downloadProgress.total > 0 && (
diff --git a/backend/cuda_download.py b/backend/cuda_download.py
index 1b4a4e60..bafc324d 100644
--- a/backend/cuda_download.py
+++ b/backend/cuda_download.py
@@ -199,6 +199,56 @@ async def download_cuda_binary(version: Optional[str] = None):
raise
+def get_cuda_binary_version() -> Optional[str]:
+ """Get the version of the installed CUDA binary, or None if not installed."""
+ import subprocess
+ cuda_path = get_cuda_binary_path()
+ if not cuda_path:
+ return None
+ try:
+ result = subprocess.run(
+ [str(cuda_path), "--version"],
+ capture_output=True, text=True, timeout=30,
+ )
+ # Output format: "voicebox-server 0.2.0"
+ for line in result.stdout.strip().splitlines():
+ if "voicebox-server" in line:
+ return line.split()[-1]
+ except Exception as e:
+ logger.warning(f"Could not get CUDA binary version: {e}")
+ return None
+
+
+async def check_and_update_cuda_binary():
+ """Check if the CUDA binary is outdated and auto-download if so.
+
+ Called on server startup. If a CUDA binary exists but its version
+ doesn't match the current app version, triggers a background download
+ of the updated CUDA binary. The download progress is visible to the
+ frontend via the existing SSE progress endpoint.
+ """
+ cuda_path = get_cuda_binary_path()
+ if not cuda_path:
+ return # No CUDA binary installed, nothing to update
+
+ cuda_version = get_cuda_binary_version()
+ current_version = __version__
+
+ if cuda_version == current_version:
+ logger.info(f"CUDA binary is up to date (v{current_version})")
+ return
+
+ logger.info(
+ f"CUDA binary version mismatch: binary=v{cuda_version}, app=v{current_version}. "
+ f"Auto-downloading updated CUDA backend..."
+ )
+
+ try:
+ await download_cuda_binary()
+ except Exception as e:
+ logger.error(f"Auto-update of CUDA binary failed: {e}")
+
+
async def delete_cuda_binary() -> bool:
"""Delete the downloaded CUDA binary. Returns True if deleted."""
path = get_cuda_binary_path()
diff --git a/backend/main.py b/backend/main.py
index dfb942be..9d238884 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -3104,6 +3104,10 @@ async def startup_event():
print(f"Backend: {backend_type.upper()}")
print(f"GPU available: {_get_gpu_status()}")
+ # Auto-update CUDA binary if installed but outdated
+ from .cuda_download import check_and_update_cuda_binary
+ _create_background_task(check_and_update_cuda_binary())
+
# Initialize progress manager with main event loop for thread-safe operations
try:
progress_manager = get_progress_manager()