How to Deploy Invidious to Production: A Complete Self-Hosting Guide

Deploy Invidious to production by compiling a static Crystal binary using the official Dockerfile, configuring a PostgreSQL database with a unique HMAC key, and exposing the service through a TLS-terminating reverse proxy using Docker Compose or systemd.

Invidious is a lightweight, self-hosted YouTube front-end written in Crystal. To deploy Invidious to production, you build a production-optimized container image, initialize the PostgreSQL backend, and secure the instance behind Nginx or a similar reverse proxy. The iv-org/invidious repository contains all necessary build scripts, configuration templates, and service definitions to run a stable, publicly accessible instance.

Prerequisites

Before deploying Invidious to production, ensure your server meets these requirements:

  • Linux server with systemd support (any modern distribution)
  • Docker Engine ≥ 20.10 and the Docker Compose plugin (docker compose)
  • PostgreSQL client tools (for manual database inspection if needed)
  • Nginx or another reverse proxy capable of TLS termination
  • Git and make (optional, for building custom images)

The provided docker/Dockerfile automatically pulls an Alpine Linux base and installs runtime dependencies including rsvg-convert, ttf-opensans, tini, and tzdata, so no additional OS packages are required inside the final container.

Build the Production Image

The repository's docker/Dockerfile compiles a static binary and bundles it in a minimal Alpine runtime. On line 33, the build runs shards install --production, ensuring only required dependencies are included to minimize image size.

Clone the repository and build the image:

git clone https://github.com/iv-org/invidious.git
cd invidious
docker build -t invidious:latest -f docker/Dockerfile .

Alternatively, use Docker Compose:

docker compose build

Configure the Application

Copy the example configuration and edit the mandatory production settings:

cp config/config.example.yml config/config.yml

Key settings in config/config.example.yml (referenced at lines 17-63, 172-179, and 332-340) include:

  • hmac_key: Generate a random 20-character string (e.g., pwgen 20 1). This is required for CSRF protection, cookie signing, and PubSubHub authentication.
  • domain: Your public hostname (e.g., invidious.example.com).
  • db: Database connection parameters pointing to your PostgreSQL service.
  • https_only: Set to true when terminating TLS at the reverse proxy.
  • external_port: 443 for HTTPS.
  • statistics_enabled: true to register your instance on api.invidious.io.

Initialize the PostgreSQL Database

The docker/init-invidious-db.sh script automatically creates the required schema on first container start. When mounted to /docker-entrypoint-initdb.d/ inside the PostgreSQL container, it executes the SQL files found in config/sql/.

No manual migration is required when using the provided Docker Compose setup. If you need to reset the database, delete the Docker volume (postgresdata) and restart the services.

Deployment Options

Create a docker-compose.prod.yml file that binds the Invidious container to localhost and includes health checks:

version: "3"

services:
  invidious:
    image: invidious:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      INVIDIOUS_CONFIG: |
        db:
          user: kemal
          password: <YOUR_STRONG_PASSWORD>
          host: invidious-db
          port: 5432
          dbname: invidious
        domain: invidious.example.com
        hmac_key: "<YOUR_RANDOM_HMAC_KEY>"
        statistics_enabled: true
        https_only: true
    depends_on:
      invidious-db:
        condition: service_healthy
    healthcheck:
      test: wget -q -O- http://127.0.0.1:3000/api/v1/stats || exit 1
      interval: 30s
      timeout: 5s
      retries: 2

  invidious-db:
    image: 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
      POSTGRES_PASSWORD: <YOUR_STRONG_PASSWORD>
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgresdata:

Start the stack:

docker compose -f docker-compose.prod.yml up -d

Systemd Service (Alternative)

For host-level deployment without Docker, use the provided invidious.service file. First, extract the binary from the built image:

docker cp $(docker create --rm invidious:latest):/invidious/invidious /usr/local/bin/invidious
chmod +x /usr/local/bin/invidious

Install and enable the service (referencing line 14 of the unit file):

sudo cp invidious.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now invidious.service

The unit file specifies User=invidious and Group=invidious (defined in the Dockerfile at lines 67-68). Adjust WorkingDirectory and ExecStart if you place the binary elsewhere.

Configure the Reverse Proxy

Place the following Nginx configuration in /etc/nginx/sites-available/invidious.conf (adapt paths for your certificate provider):

server {
    listen 443 ssl http2;
    server_name invidious.example.com;

    ssl_certificate /etc/letsencrypt/live/invidious.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/invidious.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location ~* \.(js|css|png|svg|ico|json)$ {
        expires 30d;
        access_log off;
    }
}

Ensure https_only: true is set in your Invidious configuration so the application generates HTTPS URLs for API responses.

Verification and Health Checks

Confirm your deployment is healthy:

  1. Check container status: docker ps should show the database as healthy and the Invidious container as running.
  2. Verify logs: docker logs invidious should display "Listening on 0.0.0.0:3000".
  3. Test the API endpoint: curl http://127.0.0.1:3000/api/v1/stats should return JSON statistics.
  4. Verify systemd status (if applicable): systemctl status invidious.service should show active (running).

Summary

  • Build the production image using docker/Dockerfile, which runs shards install --production on line 33 to create a minimal static binary.
  • Configure config/config.yml with a unique hmac_key and correct domain settings from config/config.example.yml.
  • Initialize the database automatically using docker/init-invidious-db.sh mounted to the PostgreSQL container.
  • Deploy using either a production-hardened Docker Compose file with health checks or the provided invidious.service systemd unit.
  • Secure the instance behind Nginx with TLS termination and set https_only: true.

Frequently Asked Questions

What hardware resources does Invidious require for production?

A production Invidious instance runs comfortably on a small VPS with 1-2 CPU cores and 2GB of RAM. The Crystal binary is memory-efficient, and PostgreSQL handles the modest metadata storage requirements. For high-traffic instances serving many concurrent users, scale vertically to 4GB RAM or configure connection pooling.

How do I update an existing Invidious deployment?

Pull the latest code from the iv-org/invidious repository, rebuild the Docker image (docker build -t invidious:latest -f docker/Dockerfile .), and restart the container. The database schema updates automatically via docker/init-invidious-db.sh when the container restarts. For systemd deployments, replace the binary at /usr/local/bin/invidious and run systemctl restart invidious.

Why is the HMAC key mandatory and how do I generate it securely?

The hmac_key in config/config.yml is required for cryptographic signing of session cookies, CSRF tokens, and PubSubHub authentication. Without it, authentication features and feed subscriptions will fail. Generate a secure 20-character random string using pwgen 20 1 or openssl rand -base64 20, and keep it confidential—treat it like a password.

Can I run Invidious without Docker?

Yes. While Docker is recommended for production, you can compile the Crystal binary from source by installing Crystal and running shards install followed by crystal build src/invidious.cr. You must manually configure PostgreSQL (using scripts/deploy-database.sh to initialize the schema) and install the invidious.service file to manage the process with systemd. Ensure all runtime dependencies listed in the Dockerfile (such as rsvg-convert) are installed on the host system.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →