...
šŸš€ deploy invidious on ubuntu vps
Learn how to deploy invidious on ubuntu vps!

This article provides a guide demonstrating how to deploy Invidious on Ubuntu VPS.

What is Invidious?

Invidious is a free, open-source alternative front end for YouTube.

It lets users browse, search, and watch YouTube content through a separate website instead of using YouTube’s official interface. Depending on the instance’s configuration, it can offer:

  • Fewer trackers and less JavaScript
  • No Google account requirement
  • Subscriptions and playlists stored locally
  • RSS feeds
  • Audio-only playback
  • A lighter interface
  • An API for developers

Invidious does not host the videos itself. It retrieves public information and media from YouTube, so it can stop working when YouTube changes its systems or blocks an instance.

An Invidious instance is simply a server running the Invidious software. You can use a public instance or host your own for greater control and privacy.

Overview

This guide deploys Invidious on an Ubuntu VPS using:

  • Docker Engine and Docker Compose
  • PostgreSQL
  • Invidious Companion
  • NGINX as a reverse proxy
  • A free Let’s Encrypt TLS certificate
  • UFW firewall rules
  • Persistent Docker volumes

The finished instance will be available at a domain such as:

https://invidious.example.com

This guide assumes Ubuntu 22.04 LTS or Ubuntu 24.04 LTS and a user account with sudo privileges. Docker currently supports both releases.

Check the VPS requirements

For a small private instance, start with approximately:

  • 2 CPU cores
  • 4 GB RAM
  • 20 GB or more disk space
  • A public IPv4 or IPv6 address
  • A domain or subdomain

The official Invidious documentation lists at least 20 GB of disk space and 2 GB of free RAM for Invidious plus Invidious Companion. Public instances need substantially more bandwidth and resources.
Launch 100% ssd ubuntu vps from $3. 19/mo!


Compare Ubuntu VPS Plans

KVM-SSD-1
KVM-SSD-8
KVM-SSD-16
KVM-SSD-32
CPU
1 Core
2 Cores
4 Cores
8 Cores
Memory
1 GB
8 GB
16 GB
32 GB
Storage
16 GB NVMe
128 GB NVMe
256 GB NVMe
512 GB NVMe
Bandwidth
1 TB
4 TB
8 TB
16 TB
Network
1 Gbps
1 Gbps
1 Gbps
1 Gbps
Delivery Time
ā±ļø Instant
ā±ļø Instant
ā±ļø Instant
ā±ļø Instant
Location
US/EU/APAC
US/EU/APAC
US/EU/APAC
US/EU/APAC
Price
$7.58*
$39.50*
$79.40*
$151.22*
KVM-SSD-1
$7.58*
CPU 1 Core
Memory 1 GB
Storage 16 GB NVMe
Bandwidth 1 TB
Network 1 Gbps
Delivery Time ā±ļø Instant
Location US/EU/APAC
KVM-SSD-8
$39.50*
CPU 2 Cores
Memory 8 GB
Storage 128 GB NVMe
Bandwidth 4 TB
Network 1 Gbps
Delivery Time ā±ļø Instant
Location US/EU/APAC
KVM-SSD-16
$79.40*
CPU 4 Cores
Memory 16 GB
Storage 256 GB NVMe
Bandwidth 8 TB
Network 1 Gbps
Delivery Time ā±ļø Instant
Location US/EU/APAC
KVM-SSD-32
$151.22*
CPU 8 Cores
Memory 32 GB
Storage 512 GB NVMe
Bandwidth 16 TB
Network 1 Gbps
Delivery Time ā±ļø Instant
Location US/EU/APAC

How to Deploy Invidious on Ubuntu VPS

To deploy Invidious on Ubuntu VPS, follow the steps provided below:

  1. Update server

    Check the server:

    lsb_release -a
    free -h
    df -h
    nproc
    

    Update Ubuntu:

    sudo apt update
    sudo apt upgrade -y
    sudo reboot
    

    Reconnect after the VPS restarts:

    ssh your-user@YOUR_SERVER_IP
    
  2. Configure the domain

    Create a DNS record with your DNS provider.

    For IPv4:

    Type: A
    Name: invidious
    Value: YOUR_SERVER_IPV4
    TTL: Auto
    

    For IPv6, optionally add:

    Type: AAAA
    Name: invidious
    Value: YOUR_SERVER_IPV6
    TTL: Auto
    

    The resulting hostname might be:

    invidious.example.com
    

    Verify that the domain resolves:

    getent hosts invidious.example.com
    

    You can also check the IPv4 address:

    dig +short A invidious.example.com
    

    Install dig when necessary:

    See Also: How to Install and Run Rootkit Hunter on AlmaLinux VPS

    sudo apt install -y dnsutils
    

    Do not request the TLS certificate until the domain resolves to the VPS.

  3. Create an administrative user

    Skip this section when the VPS already has a non-root administrative user.

    Create a user:

    sudo adduser deploy
    sudo usermod -aG sudo deploy
    

    Copy the existing SSH keys:

    sudo mkdir -p /home/deploy/.ssh
    sudo cp ~/.ssh/authorized_keys /home/deploy/.ssh/
    sudo chown -R deploy:deploy /home/deploy/.ssh
    sudo chmod 700 /home/deploy/.ssh
    sudo chmod 600 /home/deploy/.ssh/authorized_keys
    

    Open a second terminal and test the new account before closing the original session:

    ssh deploy@YOUR_SERVER_IP
    
  4. Secure SSH

    Open the SSH server configuration:

    sudo nano /etc/ssh/sshd_config
    

    Recommended settings:

    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    

    Only disable password authentication after confirming that SSH key login works.

    Validate the configuration:

    sudo sshd -t
    

    Restart SSH:

    sudo systemctl restart ssh
    

    Keep the original SSH session open until a new login succeeds.

  5. Configure the firewall

    Install and configure UFW:

    sudo apt install -y ufw
    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow OpenSSH
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    

    Check the result:

    sudo ufw status verbose
    

    Expected open ports:

    22/tcp
    80/tcp
    443/tcp
    

    Do not expose PostgreSQL port 5432, Invidious port 3000, or Companion port 8282 publicly.

    Docker-published ports can bypass some UFW rules. This deployment avoids that problem for Invidious by binding port 3000 exclusively to 127.0.0.1. Docker specifically warns administrators to account for its interaction with UFW and firewall rules.

  6. Install required utilities

    sudo apt install -y \
      ca-certificates \
      curl \
      git \
      openssl \
      nano
    
  7. Remove conflicting Docker packages

    Remove Ubuntu-packaged or conflicting Docker components:

    sudo apt remove -y \
      docker.io \
      docker-compose \
      docker-compose-v2 \
      docker-doc \
      docker-buildx \
      podman-docker \
      containerd \
      runc
    

    It is harmless if Ubuntu reports that some packages are not installed. Docker recommends removing these conflicting packages before installing Docker Engine from Docker’s official repository.

  8. Install Docker Engine

    Add Docker’s signing key:

    sudo install -m 0755 -d /etc/apt/keyrings
    
    sudo curl -fsSL \
      https://download.docker.com/linux/ubuntu/gpg \
      -o /etc/apt/keyrings/docker.asc
    
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    

    Add Docker’s APT repository:

    sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
    Types: deb
    URIs: https://download.docker.com/linux/ubuntu
    Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
    Components: stable
    Architectures: $(dpkg --print-architecture)
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF

    Install Docker Engine and Docker Compose V2:

    See Also: How to Install Pleroma on Ubuntu VPS (5 Minute Quick-Start Guide)

    sudo apt update
    
    sudo apt install -y \
      docker-ce \
      docker-ce-cli \
      containerd.io \
      docker-buildx-plugin \
      docker-compose-plugin
    

    These are the packages specified by Docker’s official Ubuntu installation procedure.

    Enable Docker:

    sudo systemctl enable --now docker
    

    Verify the service:

    sudo systemctl status docker --no-pager
    

    Verify Docker Compose V2:

    sudo docker compose version
    

    The command must be:

    docker compose
    

    not the legacy:

    docker-compose
    

    Invidious’s production instructions explicitly require Docker Compose V2.

    Run Docker’s test container:

    sudo docker run --rm hello-world
    
  9. Optionally allow the user to run Docker

    Add your user to the docker group:

    sudo usermod -aG docker "$USER"
    

    Log out and reconnect:

    exit
    

    After reconnecting, test:

    docker version
    docker compose version
    

    Membership in the docker group effectively grants root-level control over the server. Only add trusted administrative users.

    The rest of this guide shows Docker commands without sudo. Add sudo when you did not join the Docker group.

  10. Clone the Invidious repository

    Use /opt/invidious as the deployment directory:

    sudo git clone https://github.com/iv-org/invidious.git /opt/invidious
    sudo chown -R "$USER":"$USER" /opt/invidious
    cd /opt/invidious
    

    The repository is currently required because PostgreSQL initialization uses files from config/sql and docker/init-invidious-db.sh.

    Confirm the files exist:

    ls config/sql
    ls docker/init-invidious-db.sh
    
  11. Generate secure secrets

    Generate three independent secrets:

    INVIDIOUS_HMAC_KEY="$(openssl rand -hex 32)"
    COMPANION_KEY="$(openssl rand -hex 32)"
    POSTGRES_PASSWORD="$(openssl rand -base64 36 | tr -d '/+=' | head -c 40)"
    
    printf 'HMAC key: %s\n' "$INVIDIOUS_HMAC_KEY"
    printf 'Companion key: %s\n' "$COMPANION_KEY"
    printf 'Database password: %s\n' "$POSTGRES_PASSWORD"
    

    Save the values in a password manager.

    You need:

    1. An Invidious HMAC key
    2. A separate Invidious Companion key
    3. A PostgreSQL password

    Do not reuse the HMAC key as the Companion key. The official installation documentation specifically recommends separate keys.

  12. Create the production Compose configuration

    Back up the repository’s existing Compose file:

    cd /opt/invidious
    cp docker-compose.yml docker-compose.yml.repository
    

    Create the production file:

    nano docker-compose.yml
    

    Paste the following configuration.

    Replace all values marked CHANGE_ME.

    services:
      invidious:
        image: quay.io/invidious/invidious:latest
        restart: unless-stopped
    
        ports:
          - "127.0.0.1:3000:3000"
    
        environment:
          INVIDIOUS_CONFIG: |
            db:
              dbname: invidious
              user: invidious
              password: "CHANGE_ME_DATABASE_PASSWORD"
              host: invidious-db
              port: 5432
    
            check_tables: true
    
            domain: "invidious.example.com"
            external_port: 443
            https_only: true
    
            registration_enabled: false
            statistics_enabled: false
    
            invidious_companion:
              - private_url: "http://companion:8282/companion"
    
            invidious_companion_key: "CHANGE_ME_COMPANION_KEY"
            hmac_key: "CHANGE_ME_HMAC_KEY"
    
        depends_on:
          invidious-db:
            condition: service_healthy
          companion:
            condition: service_started
    
        healthcheck:
          test:
            - CMD-SHELL
            - wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/stats || exit 1
          interval: 30s
          timeout: 5s
          retries: 3
          start_period: 30s
    
        logging:
          options:
            max-size: "100m"
            max-file: "4"
    
      companion:
        image: quay.io/invidious/invidious-companion:latest
        restart: unless-stopped
    
        environment:
          SERVER_SECRET_KEY: "CHANGE_ME_COMPANION_KEY"
    
        volumes:
          - companioncache:/var/tmp/youtubei.js:rw
    
        cap_drop:
          - ALL
    
        read_only: true
    
        security_opt:
          - no-new-privileges:true
    
        logging:
          options:
            max-size: "100m"
            max-file: "4"
    
      invidious-db:
        image: docker.io/library/postgres:14
        restart: unless-stopped
    
        environment:
          POSTGRES_DB: invidious
          POSTGRES_USER: invidious
          POSTGRES_PASSWORD: "CHANGE_ME_DATABASE_PASSWORD"
    
        volumes:
          - postgresdata:/var/lib/postgresql/data
          - ./config/sql:/config/sql:ro
          - ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh:ro
    
        healthcheck:
          test:
            - CMD-SHELL
            - pg_isready -U invidious -d invidious
          interval: 10s
          timeout: 5s
          retries: 10
    
        logging:
          options:
            max-size: "100m"
            max-file: "4"
    
    volumes:
      postgresdata:
      companioncache:
    

    The current production architecture includes Invidious, PostgreSQL, and Invidious Companion. Companion replaces the older signature-helper and trusted-session-generator approach.

    See Also: How to Install and Run Zulip on Debian VPS

    Important replacements

    Replace:

    invidious.example.com
    

    with your real hostname.

    Replace both occurrences of:

    CHANGE_ME_COMPANION_KEY
    

    with exactly the same Companion key.

    Replace both occurrences of:

    CHANGE_ME_DATABASE_PASSWORD
    

    with exactly the same PostgreSQL password.

    Replace:

    CHANGE_ME_HMAC_KEY
    

    with the HMAC key.

    The password and keys must remain quoted because generated values can contain characters that YAML may otherwise interpret.

  13. Validate the Compose file

    Run:

    cd /opt/invidious
    docker compose config
    

    This renders and validates the Compose configuration.

    Carefully inspect the output for:

    • YAML indentation errors
    • Missing environment values
    • Unexpected empty strings
    • Duplicate fields
    • Invalid service dependencies

    To avoid displaying secrets on screen, use:

    docker compose config --quiet
    

    A successful validation produces no output and exits with status zero:

    echo $?
    

    Expected:

    0
    
  14. Pull the container images

    docker compose pull
    

    The Invidious production image is distributed through Quay, while the database image comes from the official PostgreSQL image repository.

    Review downloaded images:

    docker images
    
  15. Start Invidious

    docker compose up -d
    

    Check container status:

    docker compose ps
    

    You should see three services:

    invidious
    companion
    invidious-db
    

    The database may take a short period to initialize on the first start.

    Follow all service logs:

    docker compose logs -f
    

    Stop following logs with Ctrl+C.

    See Also: šŸ›  Guide to Using phpMyAdmin in cPanel

    Inspect individual services:

    docker compose logs --tail=100 invidious
    docker compose logs --tail=100 companion
    docker compose logs --tail=100 invidious-db
    
  16. Test Invidious locally

    Because Invidious is bound to the loopback address, it should be available from the VPS itself but not directly over the public internet.

    Test the API:

    curl -fsS http://127.0.0.1:3000/api/v1/stats
    

    Test the homepage:

    curl -I http://127.0.0.1:3000/
    

    Expected result:

    HTTP/1.1 200 OK
    

    or another successful HTTP response.

    Confirm the port is loopback-only:

    sudo ss -lntp | grep ':3000'
    

    Expected binding:

    127.0.0.1:3000
    

    Do not change this to 0.0.0.0:3000 for a normal reverse-proxy deployment. The official production example also binds Invidious to 127.0.0.1:3000.

  17. Install NGINX

    sudo apt install -y nginx
    sudo systemctl enable --now nginx
    

    Check its status:

    sudo nginx -t
    sudo systemctl status nginx --no-pager
    

    Open the server IP in a browser:

    http://YOUR_SERVER_IP
    

    You should see the NGINX default page.

  18. Create the NGINX reverse proxy

    Create a server configuration:

    sudo nano /etc/nginx/sites-available/invidious
    

    Paste:

    server {
        listen 80;
        listen [::]:80;
    
        server_name invidious.example.com;
    
        location / {
            proxy_pass http://127.0.0.1:3000;
    
            proxy_http_version 1.1;
    
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
            proxy_set_header Connection "";
    
            proxy_buffering off;
            proxy_read_timeout 300s;
            proxy_send_timeout 300s;
        }
    }
    

    Replace:

    invidious.example.com
    

    with your real domain.

    Invidious’s NGINX documentation proxies requests to 127.0.0.1:3000 and includes X-Forwarded-Host.

    Enable the site:

    sudo ln -s /etc/nginx/sites-available/invidious \
      /etc/nginx/sites-enabled/invidious
    

    Optionally remove the default site:

    sudo rm -f /etc/nginx/sites-enabled/default
    

    Validate NGINX:

    sudo nginx -t
    

    Apply the configuration:

    sudo systemctl reload nginx
    

    Test HTTP:

    curl -I http://invidious.example.com
    

    At this stage, the instance should load over unencrypted HTTP.

  19. Install Certbot

    Install Certbot and its NGINX plugin:

    sudo apt update
    sudo apt install -y certbot python3-certbot-nginx
    

    Request a certificate:

    sudo certbot --nginx -d invidious.example.com
    

    Certbot will ask for:

    • An email address
    • Agreement to the terms
    • Whether to redirect HTTP to HTTPS

    Select the HTTPS redirect.

    Test the site:

    curl -I https://invidious.example.com
    

    Open it in a browser:

    https://invidious.example.com
    

    Invidious default page

  20. Verify certificate renewal

    Check the Certbot timer:

    See Also: Comprehensive List of Top 10 Uses for VPS Servers

    sudo systemctl status certbot.timer --no-pager
    

    Test renewal without changing the certificate:

    sudo certbot renew --dry-run
    

    List installed certificates:

    sudo certbot certificates
    
  21. Verify the full deployment

    Check containers

    cd /opt/invidious
    docker compose ps
    

    Check health information

    docker inspect \
      --format='{{json .State.Health}}' \
      "$(docker compose ps -q invidious)"
    

    Test the public API

    curl -fsS \
      https://invidious.example.com/api/v1/stats
    

    Test NGINX

    sudo nginx -t
    systemctl is-active nginx
    

    Check exposed ports

    sudo ss -lntp
    

    The expected public listening ports are:

    22
    80
    443
    

    Port 3000 should only listen on 127.0.0.1.

    Test basic functionality

    In the browser:

    1. Open the homepage.
    2. Search for a video.
    3. Open a video page.
    4. Test playback.
    5. Open a channel.
    6. Test an RSS feed.
    7. Confirm HTTPS remains active throughout.

    Search results working does not necessarily mean video playback works. Playback depends on Invidious Companion being configured and healthy. The official documentation notes that playback does not work correctly without Companion.

  22. Disable public registration

    This guide sets:

    registration_enabled: false
    

    That is a sensible default for a private instance.

    After changing any INVIDIOUS_CONFIG value, recreate the Invidious container:

    cd /opt/invidious
    docker compose up -d --force-recreate invidious
    

    Check logs:

    docker compose logs --tail=100 invidious
    
  23. Updating Invidious

    Enter the deployment directory:

    cd /opt/invidious
    

    Back up the database first:

    mkdir -p backups
    
    docker compose exec -T invidious-db \
      pg_dump -U invidious -d invidious \
      > "backups/invidious-$(date +%F-%H%M%S).sql"
    

    Pull repository updates:

    git status
    git pull --ff-only
    

    If Git refuses because docker-compose.yml was modified, save your production file first:

    cp docker-compose.yml /tmp/invidious-compose.yml
    git restore docker-compose.yml
    git pull --ff-only
    cp /tmp/invidious-compose.yml docker-compose.yml
    

    Review upstream changes before continuing:

    git log --oneline -10
    git diff HEAD~1 -- config/sql docker/init-invidious-db.sh
    

    Pull current images:

    docker compose pull
    

    Recreate services:

    docker compose up -d
    

    Remove unused images:

    docker image prune -f
    

    Verify:

    docker compose ps
    docker compose logs --tail=100
    curl -fsS https://invidious.example.com/api/v1/stats
    
  24. Backing up the database

    Create a backup directory:

    sudo mkdir -p /var/backups/invidious
    sudo chown "$USER":"$USER" /var/backups/invidious
    sudo chmod 700 /var/backups/invidious
    

    Create a compressed database backup:

    cd /opt/invidious
    
    docker compose exec -T invidious-db \
      pg_dump -U invidious -d invidious \
      | gzip \
      > "/var/backups/invidious/invidious-$(date +%F-%H%M%S).sql.gz"
    

    Confirm the backup:

    See Also: Install Modoboa Email Server on Debian VPS (1-Stop Complete Guide)

    ls -lh /var/backups/invidious
    gzip -t /var/backups/invidious/invidious-*.sql.gz
    

    A backup stored only on the same VPS is not sufficient. Copy backups to another server or encrypted object-storage location.

  25. Automate database backups

    Create a backup script:

    sudo nano /usr/local/sbin/backup-invidious
    

    Paste:

    #!/usr/bin/env bash
    
    set -Eeuo pipefail
    
    DEPLOYMENT_DIR="/opt/invidious"
    BACKUP_DIR="/var/backups/invidious"
    RETENTION_DAYS="14"
    
    mkdir -p "$BACKUP_DIR"
    chmod 700 "$BACKUP_DIR"
    
    cd "$DEPLOYMENT_DIR"
    
    filename="$BACKUP_DIR/invidious-$(date +%F-%H%M%S).sql.gz"
    
    docker compose exec -T invidious-db \
      pg_dump -U invidious -d invidious \
      | gzip > "$filename"
    
    gzip -t "$filename"
    
    find "$BACKUP_DIR" \
      -type f \
      -name 'invidious-*.sql.gz' \
      -mtime +"$RETENTION_DAYS" \
      -delete
    

    Make it executable:

    sudo chmod 750 /usr/local/sbin/backup-invidious
    

    Test it:

    sudo /usr/local/sbin/backup-invidious
    sudo ls -lh /var/backups/invidious
    

    Create a systemd service:

    sudo nano /etc/systemd/system/invidious-backup.service
    

    Paste:

    [Unit]
    Description=Back up the Invidious PostgreSQL database
    Requires=docker.service
    After=docker.service
    
    [Service]
    Type=oneshot
    ExecStart=/usr/local/sbin/backup-invidious
    

    Create a timer:

    sudo nano /etc/systemd/system/invidious-backup.timer
    

    Paste:

    [Unit]
    Description=Run the Invidious database backup daily
    
    [Timer]
    OnCalendar=*-*-* 03:30:00
    Persistent=true
    RandomizedDelaySec=10m
    
    [Install]
    WantedBy=timers.target
    

    Enable it:

    sudo systemctl daemon-reload
    sudo systemctl enable --now invidious-backup.timer
    

    Verify:

    systemctl list-timers --all | grep invidious
    
  26. Restore the database

    Stop Invidious while leaving PostgreSQL running:

    cd /opt/invidious
    docker compose stop invidious companion
    docker compose up -d invidious-db
    

    Drop and recreate the database:

    docker compose exec -T invidious-db \
      psql -U invidious -d postgres \
      -c "DROP DATABASE IF EXISTS invidious;"
    
    docker compose exec -T invidious-db \
      psql -U invidious -d postgres \
      -c "CREATE DATABASE invidious OWNER invidious;"
    

    Restore a compressed backup:

    gunzip -c /var/backups/invidious/invidious-BACKUP.sql.gz \
      | docker compose exec -T invidious-db \
          psql -U invidious -d invidious
    

    Restart everything:

    docker compose up -d
    docker compose ps
    

    Check logs:

    docker compose logs --tail=100
    

    Always test the restore process before relying on the backup strategy.

  27. Useful operational commands

    View status

    cd /opt/invidious
    docker compose ps
    

    View all logs

    docker compose logs -f
    

    View recent Invidious logs

    docker compose logs --tail=200 invidious
    

    Restart Invidious

    docker compose restart invidious
    

    Restart all services

    docker compose restart
    

    Stop the deployment

    docker compose down
    

    This does not delete the named database volume.

    Start the deployment

    docker compose up -d
    

    Show resource use

    docker stats
    

    Show disk use

    docker system df
    

    Validate configuration

    docker compose config --quiet
    
  28. Troubleshooting

    Invidious container keeps restarting

    Check:

    See Also: šŸš€ How to Deploy Supabase on AlmaLinux VPS (Production-Ready Guide)

    docker compose ps
    docker compose logs --tail=200 invidious
    

    Common causes include:

    • Invalid YAML under INVIDIOUS_CONFIG
    • Missing HMAC key
    • Companion key mismatch
    • Database password mismatch
    • Database still initializing
    • Incorrect indentation
    • Insufficient memory

    Validate Compose syntax:

    docker compose config --quiet
    

    Database authentication fails

    Look for errors such as:

    password authentication failed
    

    The password must match in both locations:

    INVIDIOUS_CONFIG:
      db:
        password: "..."
    

    and:

    invidious-db:
      environment:
        POSTGRES_PASSWORD: "..."
    

    Changing POSTGRES_PASSWORD after PostgreSQL has initialized does not automatically change the password stored inside an existing database volume.

    For a new installation with no data worth preserving, remove and rebuild the database volume:

    docker compose down
    docker volume ls | grep postgresdata
    docker volume rm invidious_postgresdata
    docker compose up -d
    

    This permanently deletes the Invidious database.

    Companion errors or videos do not play

    Check Companion:

    docker compose logs --tail=200 companion
    

    Confirm that these values are identical:

    invidious_companion_key: "YOUR_COMPANION_KEY"
    

    and:

    SERVER_SECRET_KEY: "YOUR_COMPANION_KEY"
    

    Confirm the private URL is:

    private_url: "http://companion:8282/companion"
    

    Recreate both services:

    docker compose up -d --force-recreate companion invidious
    

    Invidious Companion is now part of the expected deployment and replaces older helper components.

    NGINX returns 502 Bad Gateway

    Check whether Invidious is running:

    docker compose ps
    curl -I http://127.0.0.1:3000
    

    Check logs:

    docker compose logs --tail=100 invidious
    sudo tail -n 100 /var/log/nginx/error.log
    

    Confirm NGINX uses:

    proxy_pass http://127.0.0.1:3000;
    

    Restart services:

    docker compose restart invidious
    sudo systemctl restart nginx
    

    Certificate issuance fails

    Verify DNS:

    dig +short A invidious.example.com
    dig +short AAAA invidious.example.com
    

    Verify port 80:

    sudo ufw status
    sudo ss -lntp | grep ':80'
    

    Verify NGINX:

    sudo nginx -t
    sudo systemctl status nginx --no-pager
    

    An incorrect AAAA record can cause validation failures even when the A record is correct. Remove an unusable AAAA record or configure IPv6 correctly.

    Out-of-memory errors

    Check memory:

    free -h
    sudo dmesg -T | grep -i -E 'out of memory|oom|killed process'
    

    Add a 4 GB swap file when the VPS has limited RAM:

    sudo fallocate -l 4G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    

    Persist it:

    echo '/swapfile none swap sw 0 0' \
      | sudo tee -a /etc/fstab
    

    Check:

    swapon --show
    free -h
    

    Swap can reduce abrupt out-of-memory failures but is not a substitute for sufficient RAM.

    See Also: šŸ“Ø Email Server Administration: Ensuring Email Deliverability to Gmail and Yahoo

    Disk usage grows unexpectedly

    Check Docker usage:

    docker system df
    

    Check logs:

    sudo du -sh /var/lib/docker/containers/* 2>/dev/null \
      | sort -h \
      | tail
    

    Remove unused images:

    docker image prune -f
    

    Do not run this unless you understand what it removes:

    docker system prune
    

    Never add --volumes casually, because volumes may contain the PostgreSQL database.

  29. Security checklist

    Before considering the deployment complete, confirm:

    • SSH root login is disabled.
    • SSH password login is disabled after key authentication is tested.
    • UFW only exposes SSH, HTTP, and HTTPS.
    • Invidious listens on 127.0.0.1:3000.
    • PostgreSQL has no public port mapping.
    • Companion has no public port mapping.
    • HTTPS works.
    • HTTP redirects to HTTPS.
    • Registration is disabled unless intentionally enabled.
    • HMAC, Companion, and database secrets are different.
    • Compose and backup files are readable only by administrators.
    • Database backups are created and tested.
    • Off-server backups exist.
    • Ubuntu and Docker receive regular security updates.
    • Container logs have size limits.
    • The instance is monitored for disk, memory, and container health.
  30. Final architecture

    The deployed request flow is:

    Browser
       |
       | HTTPS :443
       v
    NGINX
       |
       | HTTP over loopback
       v
    Invidious :3000
       |                 \
       |                  \ Internal Docker network
       v                   v
    PostgreSQL          Invidious Companion
    

    Only NGINX is publicly accessible. Invidious, PostgreSQL, and Companion remain behind the VPS and Docker networking layers.

    The official production Compose method is designed for Invidious to sit behind a reverse proxy rather than exposing its application port directly.

Launch 100% ssd ubuntu vps from $3. 19/mo!

Conclusion

You now know how to deploy Invidious on Ubuntu VPS.

Avatar of editorial staff

Editorial Staff

Rad Web Hosting is a leading provider of web hosting, Cloud VPS, and Dedicated Servers in Dallas, TX.
lg