Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

How I Turned My Idle RTX 4060 into the Backend for a Free Audio Separation Website

I had an RTX 4060 in my Windows PC that wasn’t getting much gaming time. Instead of leaving it idle, I connected it to a website. The result is UVR Online: users upload a song, choose a separation model, then listen to

I had an RTX 4060 in my Windows PC that wasn’t getting much gaming time. Instead of leaving it idle, I connected it to a website.

The result is UVR Online: users upload a song, choose a separation model, then listen to and download the separated tracks.

The website runs in the cloud. The actual audio inference runs on my local computer in China, serving users around the world. I didn’t need a public IP address at home or port forwarding.

The architecture

Browser ── upload ──► Private Cloudflare R2
   │                          ▲
   │                          │ download input / upload results
   ▼                          │
Next.js → Go API ◄──── Windows Python Worker
              │               └── PyTorch CUDA → RTX 4060
              ├── PostgreSQL
              └── Redis

Each part has a specific job:

  • Next.js provides the interface, task pages and audio players.
  • Go handles task creation, permissions, upload authorization and Worker coordination.
  • PostgreSQL stores the authoritative task state.
  • Redis handles rate limiting.
  • Cloudflare R2 holds private input and output files.
  • Python, PyTorch CUDA and FFmpeg handle audio validation, separation and encoding locally.

The separation engine builds on the UVR ecosystem and headless inference code. I’m building the web service around existing models, rather than training a new separation model.

The local PC pulls work

The cloud server never opens a connection to my home computer. Instead, the Worker makes outbound HTTPS requests to register, send heartbeats and claim tasks.

For each task, it:

  1. Downloads the input using a short-lived signed URL.
  2. Checks the actual audio with ffprobe.
  3. Runs the selected model on the GPU.
  4. Encodes and uploads the results to R2.
  5. Reports completion to the API.

The browser also uploads directly to R2. This keeps large audio files out of the application server’s data path.

Private results are accessed through short-lived signed URLs, rather than public bucket links.

A home computer needs recoverable tasks

A local PC can restart. The network can disconnect. A process can crash halfway through a song.

To handle that, claimed tasks have a lease and an attempt token.

The Worker renews the lease while it works. If the lease expires, the server can requeue the task within its retry limit. An old attempt cannot later overwrite the result of a newer attempt.

Claiming is atomic in PostgreSQL, and completion is idempotent. Redis is useful for rate limiting, but it doesn’t become a second source of truth for task status.

This provides recovery, not uninterrupted availability. A single local machine is still a single point of failure.

The surprising bottleneck was file transfer

My first assumption was that GPU inference would dominate the wait.

In practice, moving audio between my local PC and overseas storage became an important bottleneck—especially over a slow VPN route.

Output size matters too. A small MP3 can expand into much larger uncompressed tracks. For example, five minutes of 44.1 kHz, 16-bit stereo audio is roughly 50.5 MiB per WAV track, before headers. Two stems are about 101 MiB.

That is why export format is an infrastructure decision as well as a user preference.

I also changed the Worker from a fully serial workflow to a bounded pipeline:

Task A: upload results
Task B: run inference
Task C: download input

Only one task uses the GPU at a time. Network transfers can overlap with inference, and temporary disk usage is bounded.

This reduces avoidable GPU idle time. It doesn’t make the internet connection faster, and a backed-up upload stage still applies backpressure.

Free processing still needs limits

Removing a daily usage cap doesn’t remove the need for admission control.

Free processing is constrained by file size, duration, supported formats and queue capacity. Each free user can have only one unfinished task. Network-level limits and bot verification add another layer of protection.

The point is to let someone process another song after finishing the previous one, without letting one client fill the queue.

The Worker also avoids silently falling back to CPU inference when CUDA is unavailable. On an 8 GB card, conservative segment sizes and a bounded out-of-memory retry are more useful than letting jobs become unexpectedly slow.

Model licensing needs separate attention: a library’s license does not automatically cover every model checkpoint it can load.

What this setup is good for

This architecture lets me reuse hardware I already own while keeping the public website separate from the GPU machine.

It also gives me a path to another Worker later. The task protocol doesn’t depend on the GPU living in my home.

The trade-off is operational: my PC, home connection and available upload bandwidth are part of the service. I currently start the Worker manually; this is not an always-on cloud GPU deployment.

My main lesson: turning a GPU into a public service is mostly about everything around inference—task ownership, transfers, recovery, storage and fair access.

The GPU is the engine. The surrounding system makes it usable.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.