We’ve all been there. You have a clip you want to share on Discord, but it’s 40MB. The upload limit stares back at you: 10MB.
So begins the ritual. You open HandBrake, fiddle with presets, guess at a bitrate, export, check the file size... 10.2MB. Rinse and repeat. Or perhaps you're a command-line warrior, trying to remember the exact ffmpeg incantation to hit your target. I was so tired of this process I decided to build a solution for myself, and I thought the self-hosting community might find it useful, too.
It’s called 8mb.local, and it’s a self-hosted, "fire-and-forget" video compressor built to solve this exact problem.
It’s all packed into a single Docker container. You get a clean SvelteKit web UI, a FastAPI backend, and a Celery worker queue. You just drag and drop your file, choose a target size (like 8MB, 25MB, or 50MB), and let it run.
What is 8mb.local?
The goal of 8mb.local is to make video compression simple, fast, and smart. It’s not just a basic ffmpeg wrapper; it’s a full-featured web UI designed for a homelab environment designed to be compatible with a wide range of system configurations out of the box.
The real magic is that it automatically detects and uses your GPU for much faster encoding.
- Multi-Vendor GPU Support: It auto-detects NVIDIA (NVENC), Intel (QSV), and AMD (VAAPI) hardware acceleration.
- Automatic CPU Fallback: If you don't have a compatible GPU, or if a driver is missing, it seamlessly falls back to CPU encoding (libx264, libx265, libaom-av1) so the job still gets done.
- Target-Size-First: The UI is built around file size, not confusing bitrate calculations. It does the math for you.
- Smart Auto-Retry: If the first pass misses the target (e.g., it comes out 2% over), it automatically runs a second pass with an adjusted bitrate to nail the file size.
- Modern UI: You get a live queue, real-time progress bars, and streaming FFmpeg logs directly in the browser thanks to Server-Sent Events (SSE). You can even run multiple jobs at once.
It's the tool I always wanted: a "set it and forget it" service running in my lab that my whole family (or team) can use without needing to understand video codecs.
Video Demo
How to Install 8mb.local with Docker
Getting started is simple. The entire application stack (frontend, backend, worker, and Redis broker) is bundled into one Docker image.
You just need to choose the right docker-compose.yml for your hardware.
1. CPU-Only (Works Everywhere)
This is the simplest setup and works on any machine (Windows, macOS, Linux).
YAML
services:
8mblocal:
image: jms1717/8mblocal:latest
container_name: 8mblocal
ports:
- "8001:8001"
volumes:
- ./uploads:/app/uploads
- ./outputs:/app/outputs
restart: unless-stopped
2. NVIDIA GPU (NVENC)
This is the one you want for maximum performance. It uses the deploy key to give the container access to your NVIDIA GPU.
Critically, you must add the NVIDIA_DRIVER_CAPABILITIES environment variable. This tells the NVIDIA Container Toolkit to mount the video encoding libraries, not just the compute libraries.
YAML
services:
8mblocal:
image: jms1717/8mblocal:latest
container_name: 8mblocal
ports:
- "8001:8001"
volumes:
- ./uploads:/app/uploads
- ./outputs:/app/outputs
restart: unless-stopped
environment:
# REQUIRED for NVENC
- NVIDIA_DRIVER_CAPABILITIES=compute,video,utility
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
3. Intel/AMD GPU (VAAPI - Linux Only)
If you're running on a Linux host with an Intel iGPU (QSV) or an AMD GPU (VAAPI), you can get hardware acceleration by passing through the /dev/dri device.
YAML
services:
8mblocal:
image: jms1717/8mblocal:latest
container_name: 8mblocal
ports:
- "8001:8001"
volumes:
- ./uploads:/app/uploads
- ./outputs:/app/outputs
devices:
- /dev/dri:/dev/dri # Pass through the GPU
restart: unless-stopped
⚠️ A Common Pitfall (and How to Fix It)
I’ve spent time in the troubleshooting trenches so you don't have to. Here is one of the most common issues self-hosters run into.
"My progress bar is stuck at 0% until it's done!"
This is almost always a reverse proxy buffering problem.
8mb.local uses Server-Sent Events (SSE) to stream real-time progress. If you put it behind Nginx, Nginx Proxy Manager, or Traefik, the proxy might buffer the entire response instead of streaming it.
The Fix: You need to disable buffering for the SSE endpoint.
For Nginx / Nginx Proxy Manager, add this to your advanced configuration:
Nginx
location /api/stream/ {
proxy_pass http://YOUR_8MBLOCAL_IP:8001;
proxy_buffering off; # REQUIRED
proxy_cache off;
proxy_set_header Connection '';
chunked_transfer_encoding on;
}
This ensures you get that smooth, real-time feedback.

Final Thoughts
As I mentioned, this is my first real open-source side project. I built it to solve a personal frustration, but I'm thrilled to contribute something back to the self-hosting and homelab community that I've learned so much from.
It’s been a fantastic learning experience, and I'm excited to see if other people find it as useful as I do.
Stop fighting with HandBrake presets. Give 8mb.local a try, check out the project on GitHub, and let me know what you think.
Discussion