How to Self-Host Invidious with Docker

What Is Invidious?

Invidious is a privacy-focused alternative frontend for YouTube. It lets you watch YouTube videos without ads, without JavaScript (optionally), without Google tracking, and without a Google account. You get subscriptions, playlists, and search — all running on your own server. Think of it as a YouTube proxy that strips out the surveillance layer.

Invidious is built in Crystal and uses PostgreSQL for storing user preferences, subscriptions, and cached video metadata. A companion service handles video stream retrieval from YouTube.

Official site: invidious.io

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 4 GB of free RAM (minimum 2 GB)
  • 20 GB of free disk space
  • A domain name (recommended for HTTPS access)
  • Git installed (required for database initialization files)

Docker Compose Configuration

Invidious requires three services: the main application, a companion service for video retrieval, and PostgreSQL.

First, clone the Invidious repository to get the required database initialization files:

git clone https://github.com/iv-org/invidious.git
cd invidious

Create a docker-compose.yml file in the cloned directory:

services:
  invidious:
    image: quay.io/invidious/invidious:2.20260207.0
    # For ARM64: image: quay.io/invidious/invidious:2.20260207.0-arm64
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      INVIDIOUS_CONFIG: |
        db:
          dbname: invidious
          user: kemal
          password: CHANGE_THIS_DB_PASSWORD
          host: invidious-db
          port: 5432
        check_tables: true
        # Companion handles video stream retrieval (required for playback)
        invidious_companion:
          - private_url: "http://companion:8282/companion"
        invidious_companion_key: "CHANGE_THIS_COMPANION_KEY"
        # HMAC key for CSRF tokens and cookies (must differ from companion key)
        hmac_key: "CHANGE_THIS_HMAC_KEY"
        # Your domain (uncomment and set for production)
        # domain: invidious.yourdomain.com
        # external_port: 443
        # https_only: true
        # Disable telemetry
        popular_enabled: true
        registration_enabled: true
        login_enabled: true
        captcha_enabled: true
        statistics_enabled: false
    healthcheck:
      test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/stats || exit 1
      interval: 30s
      timeout: 5s
      retries: 2
    logging:
      options:
        max-size: "1G"
        max-file: "4"
    depends_on:
      - invidious-db

  companion:
    image: quay.io/invidious/invidious-companion:latest  # Companion has no semver tags published — :latest is the only option
    restart: unless-stopped
    environment:
      # Must match invidious_companion_key above
      - SERVER_SECRET_KEY=CHANGE_THIS_COMPANION_KEY
    logging:
      options:
        max-size: "1G"
        max-file: "4"
    cap_drop:
      - ALL
    read_only: true
    volumes:
      - companioncache:/var/tmp/youtubei.js:rw
    security_opt:
      - no-new-privileges:true

  invidious-db:
    image: docker.io/library/postgres:14
    restart: unless-stopped
    volumes:
      - postgresdata:/var/lib/postgresql/data
      - ./config/sql:/config/sql
      - ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
    environment:
      POSTGRES_DB: invidious
      POSTGRES_USER: kemal
      # Must match db.password in INVIDIOUS_CONFIG
      POSTGRES_PASSWORD: CHANGE_THIS_DB_PASSWORD
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgresdata:
  companioncache:

Generate your secret keys before starting:

# Generate HMAC key
openssl rand -hex 16

# Generate companion key (must be different from HMAC key)
openssl rand -hex 16

# Generate database password
openssl rand -hex 24

Replace CHANGE_THIS_DB_PASSWORD, CHANGE_THIS_COMPANION_KEY, and CHANGE_THIS_HMAC_KEY with your generated values. The companion key must match between the invidious service (invidious_companion_key) and the companion service (SERVER_SECRET_KEY).

Start the stack:

docker compose up -d

Initial Setup

  1. Open http://your-server-ip:3000 in your browser
  2. You should see the Invidious home page with trending videos
  3. Click Register to create an account (if registration is enabled)
  4. Go to Preferences to configure:
    • Default video quality
    • Dark/light mode
    • Default locale and region
    • Subscription feed settings

No default credentials exist — the first registered user is a regular user. Set admins in the config to grant admin privileges to specific usernames.

Configuration

Invidious uses YAML configuration embedded in the INVIDIOUS_CONFIG environment variable. Key settings:

SettingDefaultDescription
domain(none)Your instance’s FQDN for production use
external_port3000Port exposed through your reverse proxy
https_onlyfalseForce HTTPS URLs throughout the interface
registration_enabledtrueAllow new account signups
login_enabledtrueAllow user login
captcha_enabledtrueBasic anti-bot captcha on registration
popular_enabledtrueShow the Popular/Trending tab
statistics_enabledfalseExpose /api/v1/stats endpoint
admins[]Array of admin usernames (e.g., ["myuser"])
default_home”Popular”Default landing page (Popular, Trending, Subscriptions)
feed_menu[“Popular”, “Trending”, “Subscriptions”]Visible menu items

Production Configuration

For a production instance behind a reverse proxy:

INVIDIOUS_CONFIG: |
  db:
    dbname: invidious
    user: kemal
    password: YOUR_DB_PASSWORD
    host: invidious-db
    port: 5432
  check_tables: true
  invidious_companion:
    - private_url: "http://companion:8282/companion"
  invidious_companion_key: "YOUR_COMPANION_KEY"
  hmac_key: "YOUR_HMAC_KEY"
  domain: invidious.yourdomain.com
  external_port: 443
  https_only: true
  registration_enabled: false
  login_enabled: true
  captcha_enabled: true
  popular_enabled: true
  admins: ["yourusername"]

Reverse Proxy

Invidious listens on port 3000. Point your reverse proxy to http://localhost:3000.

For Nginx Proxy Manager, create a proxy host pointing to invidious on port 3000 with WebSocket support enabled. For Caddy:

invidious.yourdomain.com {
    reverse_proxy localhost:3000
}

Reverse Proxy Setup

Backup

Back up the PostgreSQL data volume:

# Dump the database
docker exec invidious-db pg_dump -U kemal invidious > invidious_backup.sql

# Restore from dump
cat invidious_backup.sql | docker exec -i invidious-db psql -U kemal invidious

The companion cache volume (companioncache) is ephemeral and does not need backup.

Backup Strategy

Troubleshooting

Videos Won’t Play

Symptom: Video pages load but playback fails with a black screen or error message.

Fix: The companion service handles video stream retrieval. Verify it’s running and the secret keys match:

docker compose logs companion
# Check for authentication errors
# Verify SERVER_SECRET_KEY matches invidious_companion_key

“Could not check out from database pool” Error

Symptom: Pages fail to load with a database connection error.

Fix: PostgreSQL may not have initialized the Invidious schema. Ensure the init-invidious-db.sh script ran on first startup:

docker compose logs invidious-db | grep "init-invidious"
# If schema wasn't created, restart the database with a clean volume:
docker compose down
docker volume rm invidious_postgresdata
docker compose up -d

Memory Usage Grows Over Time

Symptom: Invidious consumes increasing RAM until the container crashes.

Fix: This is a known behavior. Add a restart policy or use a cron job to restart the container periodically:

# Restart every 6 hours
0 */6 * * * docker compose restart invidious

Registration Disabled After Config Change

Symptom: Changed registration_enabled to false but existing registrations still work.

Fix: This is expected. The setting only prevents new registrations — existing accounts remain active. To remove users, use the database directly.

ARM64 Image Not Found

Symptom: Image pull fails on Raspberry Pi or ARM server.

Fix: Use the ARM-specific tag: quay.io/invidious/invidious:2.20260207.0-arm64. There is no multi-arch manifest — you must specify the ARM tag explicitly.

Resource Requirements

  • RAM: ~200 MB idle, 500 MB+ under load with multiple users
  • CPU: Low-Medium (video transcoding happens on YouTube’s side)
  • Disk: 20 GB minimum for PostgreSQL data and companion cache; grows slowly with usage

Verdict

Invidious is the best self-hosted YouTube frontend if you want privacy, ad-free viewing, and subscription management without a Google account. The setup is straightforward — clone the repo, configure three services, and you’re watching YouTube without Google’s tracking.

The main limitation is YouTube’s ongoing efforts to block alternative clients. Invidious has historically faced periods where video playback breaks until the companion service is updated. If you need guaranteed reliability, consider it a complementary tool rather than a complete YouTube replacement. For archiving videos locally (rather than streaming them), look at Tube Archivist instead.

Note on image versioning: Invidious publishes only latest tags on Quay.io, not pinned semver tags. This is a deliberate project decision. The current release at time of writing is v2.20260207.0.

Frequently Asked Questions

Does Invidious still work with YouTube?

Invidious continues to work, but YouTube periodically implements changes that temporarily break playback. The companion service handles video stream retrieval and is updated by the developers when YouTube changes their API. Expect occasional downtime (hours to days) after YouTube pushes major changes. Check the GitHub repository for current status.

Do I need a Google account to use Invidious?

No. Invidious does not use or require a Google account. You create a local account on your instance for managing subscriptions and preferences. All YouTube interactions go through Invidious’s companion service, not through your Google credentials.

Why does Invidious use so much RAM?

Invidious is built in Crystal (compiled language) and caches video metadata and page renders in memory. Usage grows over time as the cache fills. Set up a cron job to restart the container periodically (e.g., every 6 hours) to reset memory usage. Plan for 500 MB+ under load with multiple users.

Can I run Invidious on a Raspberry Pi?

Yes. Use the ARM64 image tag: quay.io/invidious/invidious:2.20260207.0-arm64. There is no multi-arch manifest — you must specify the ARM tag explicitly. A Raspberry Pi 4 with 4 GB RAM handles single-user usage adequately.

Is there a mobile app for Invidious?

Several third-party apps support Invidious instances. On Android, NewPipe can be configured to use Invidious as a backend. LibreTube also supports custom Invidious/Piped instances. On iOS, Yattee supports Invidious. These are community-maintained, not official Invidious projects.

How is Invidious different from Piped?

Both are privacy-focused YouTube frontends. Invidious is built in Crystal with its own companion service for stream retrieval. Piped is built in Java and uses a different architecture. Invidious has been around longer and has a more mature codebase. Piped has a more modern UI. Both face the same YouTube blocking challenges. See our detailed comparison.

Comments