💻 Developer Guide & Self-Hosting

Run Your Own Downloader

Deploy a private, local-first YouTube downloading and audio conversion service on your own home server, VPS, or Docker cluster using modern Python, Flask, yt-dlp, and FFmpeg.

Deploy in Under 3 Minutes

Clone the repository, configure your environment variables, and launch using standard Python or Docker Compose.

⭐ View on GitHub

Why Self-Host Your Own Media Downloader?

Public downloader websites are notoriously plagued with intrusive pop-up advertisements, deceptive download buttons, invasive browser fingerprinting scripts, and aggressive artificial rate limits. Even worse, many commercial web converters secretly route your media requests through opaque proxy networks that inspect traffic or inject promotional watermarks into encoded audio and video files.

By self-hosting this open-source Flask and yt-dlp application, you gain complete operational sovereignty over your media processing pipeline. Every download is executed directly on your hardware, audio is converted locally via your own FFmpeg binary, and temporary media files reside exclusively on your local filesystem under a strict 24-hour automatic retention purge policy.

Core Principles of this Implementation

This architecture was designed from the ground up for resilience and safety: non-blocking background thread pool execution, SQLite job state tracking with WAL mode, strict URL host allowlisting, SSRF protection against private IP probing, and zero client-side JavaScript tracking.

System Prerequisites

Before deploying the service, ensure that your host system meets the following software requirements:

  • Python 3.10+: Python 3.10, 3.11, or 3.12 with venv and pip installed.
  • FFmpeg 5.0+: Essential for merging DASH video and audio streams (1080p, 1440p, 4K) and converting audio extractions to MP3, M4A, or FLAC.
  • Git: To clone and update the codebase.
  • Disk Space: At least 10–20 GB of free storage allocated to your temporary download directory, depending on concurrent usage.
  • RAM: Minimum 1 GB RAM (2 GB recommended when transcribing or converting high-bitrate audio).

Bare-Metal Quickstart (Linux, macOS, Windows)

Follow these step-by-step commands to get the application running locally in a virtual environment:

bash Terminal Setup
# 1. Clone the repository
git clone https://github.com/your-username/yt-downloader.git
cd yt-downloader

# 2. Create and activate a Python virtual environment
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# 3. Upgrade pip and install production dependencies
pip install --upgrade pip
pip install -r requirements.txt

# 4. Copy the environment configuration template
cp .env.example .env

# 5. Verify FFmpeg installation
ffmpeg -version

# 6. Initialize database and start the local server
python app.py

Once started, open your web browser and navigate to http://127.0.0.1:5000. The application dashboard will automatically test connectivity to FFmpeg and initialize the SQLite database table schema.

Environment Variables Configuration

The service is configured via environment variables or a local .env file. The table below details every configuration parameter, its default value, and its operational purpose:

Variable Default Description & Operational Impact
FLASK_ENV development Set to production on live public deployments to disable debug error traces.
SECRET_KEY Auto-generated Cryptographic key for signing Flask session cookies and CSRF tokens.
DOWNLOAD_DIR ./downloads Directory where in-progress and completed media files are written.
DATABASE_PATH ./ytdl.db Path to SQLite database file. Automatically uses WAL mode for concurrent write safety.
MAX_CONCURRENT_DOWNLOADS 3 Worker thread concurrency limit for downloading and transcode jobs.
MAX_BATCH_SIZE 20 Maximum number of discrete video URLs permitted in a single bulk submission.
MAX_PLAYLIST_ITEMS 50 Strict ceiling on items extracted and queued from a single YouTube playlist URL.
MAX_QUEUE_DEPTH 50 Maximum total pending jobs allowed in the waiting queue before returning 429.
DOWNLOAD_TIMEOUT 1800 Job timeout threshold in seconds (30 minutes) to prevent zombie processes.
FILE_RETENTION_HOURS 24 Hours before completed files and expired database job records are permanently pruned.
RATE_LIMIT_PER_MINUTE 30 IP-based client rate limit enforced via Flask-Limiter memory backend.
FFMPEG_PATH (empty) Optional custom path to FFmpeg binary if not present in your system $PATH.

Docker & Docker Compose Deployment

For containerized environments, running the downloader in Docker ensures FFmpeg is packaged consistently without host system dependency conflicts:

dockerfile Dockerfile
FROM python:3.11-slim

# Install system dependencies including FFmpeg
RUN apt-get update && apt-get install -y --no-install-recommends \
    ffmpeg \
    curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy dependency specifications and install
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt gunicorn

# Copy application source code
COPY . .

# Create volume mount points
RUN mkdir -p /app/downloads && chmod 777 /app/downloads

EXPOSE 5000

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--threads", "4", "--timeout", "120", "app:create_app()"]

Deploy using Docker Compose with dedicated persistent volume mounts for your downloads directory and SQLite state database:

yaml docker-compose.yml
version: '3.8'

services:
  yt-downloader:
    build: .
    container_name: yt-downloader
    restart: unless-stopped
    ports:
      - "127.0.0.1:5000:5000"
    environment:
      - FLASK_ENV=production
      - MAX_CONCURRENT_DOWNLOADS=3
      - FILE_RETENTION_HOURS=24
      - RATE_LIMIT_PER_MINUTE=60
    volumes:
      - ./downloads:/app/downloads
      - ./data:/app/data
    security_opt:
      - no-new-privileges:true
    read_only: false

Production Hardening Checklist

If you intend to expose your self-hosted instance to the public internet or share it with remote family and colleagues, follow these mandatory security practices:

1

Deploy Behind a Reverse Proxy (Nginx / Caddy)

Never expose Flask's built-in WSGI server or bare Gunicorn directly to the public web. Place Nginx or Caddy in front to manage TLS termination, request buffering, and client header sanitization.

2

Enable Automatic SSL/TLS Encryption

Issue a free Let's Encrypt TLS certificate using Certbot or Caddy's automatic HTTPS. Ensure HTTP requests are redirected to HTTPS with HSTS headers enabled.

3

Enforce Authentication or Tailscale VPN Access

If the downloader is meant solely for your personal use, restrict access via HTTP Basic Auth, Authelia, Cloudflare Access, or a private WireGuard / Tailscale mesh network. This shields your server bandwidth from scrapers.

4

Regularly Update yt-dlp

YouTube frequently updates video stream extraction signatures and anti-bot challenges. Set up a weekly cron job to update the yt-dlp package: pip install --upgrade yt-dlp.

Egress Bandwidth & VPS Hosting Notice

Video downloads consume significant upstream and downstream network bandwidth. If deploying on low-cost cloud providers (such as DigitalOcean, Linode, or Hetzner), monitor your monthly egress bandwidth allowances to prevent overage charges.

Related Tools & Reading

Explore our related in-depth guides and technical documentation: