How to Watch YouTube Privately with Invidious (No Ads, No Account, No Tracking, on Your Server)

YouTube through your own front end: Invidious. Subscriptions without a Google account, RSS for every channel, no ads, no tracking — and the catch to know about.

YouTube's site is an advert-and-recommendation machine wrapped round the videos. Invidious is an alternative front end: your server fetches the video and metadata from YouTube and shows them on a clean page — no ads, no autoplay rabbit hole, subscriptions stored locally with no Google account, an RSS feed per channel for FreshRSS (89), and phone apps that use it. The honest caveat up front: YouTube actively fights unofficial clients, so Invidious needs updates and sometimes workarounds. For a household it's worth it.

Quick reference

TaskWhat
Folder~/docker/invidious/compose.yaml
Webhttp://server-ip:3010
NeedsPostgreSQL, an hmac_key, and (now) a companion or PO-token helper for playback
Accountlocal, no email; stores subscriptions and watch history on your server
RSShttps://yt.example.com/feed/channel/UC…
PhoneClipious (Android), Yattee (iOS) → your instance
Browserthe LibRedirect extension sends youtube.com links to your instance

1. The compose file

mkdir -p ~/docker/invidious && cd ~/docker/invidious
nano compose.yaml

services:
invidious:
image: quay.io/invidious/invidious:latest
container_name: invidious
ports:
- "127.0.0.1:3010:3000"
environment:
INVIDIOUS_CONFIG: |
db:
dbname: invidious
user: kemal
password: ${DB_PASSWORD}
host: invidious-db
port: 5432
check_tables: true
hmac_key: "${HMAC_KEY}"
domain: yt.example.com
https_only: true
external_port: 443
registration_enabled: true
captcha_enabled: false
default_user_preferences:
quality: dash
dark_mode: dark
region: GB
invidious_companion:
- private_url: "http://companion:8282"
invidious_companion_key: "${COMPANION_KEY}"
depends_on: [invidious-db, companion]
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/trending || exit 1
interval: 30s
timeout: 5s
retries: 2
restart: unless-stopped

companion:
image: quay.io/invidious/invidious-companion:latest
container_name: invidious-companion
environment:
SERVER_SECRET_KEY: ${COMPANION_KEY}
cap_drop: [ALL]
read_only: true
security_opt: [no-new-privileges:true]
restart: unless-stopped

invidious-db:
image: postgres:16-alpine
container_name: invidious-db
environment:
POSTGRES_DB: invidious
POSTGRES_USER: kemal
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- ./db:/var/lib/postgresql/data
restart: unless-stopped

.env: DB_PASSWORD, HMAC_KEY (openssl rand -hex 20), COMPANION_KEY (exactly 16 characters, openssl rand -hex 8). The companion handles the video-stream side that YouTube keeps changing; check Invidious's docs for the current required pieces — this is the part that moves.

docker compose up -d
docker compose logs -f invidious

2. Reverse proxy

yt.example.com → 127.0.0.1:3010 (28), HTTPS, and client_max_body_size 0 isn't needed but proxy_buffering off helps playback. Open it: a plain search box. Search, click, play — no ads.

3. An account and subscriptions

Log in → Register: a username and password, stored on your server only. Subscribe to channels; the Subscriptions page is a chronological feed, no algorithm. Import your YouTube subscriptions: Google Takeout (149) → subscriptions.csv → Invidious Preferences → Import/export. Then set registration_enabled: false once the household's accounts exist.

Every channel has an RSS feed (/feed/channel/UC…), and your subscriptions page has a private one — FreshRSS (89) turns YouTube into a newspaper.

4. Phones and redirects

5. The catch, honestly

YouTube rate-limits and blocks IPs it identifies as unofficial clients. Symptoms: "Sign in to confirm you're not a bot", videos that won't play, empty search. Mitigations, in order: keep the image current (Watchtower, 50 — this is one to leave unpinned); run the companion/PO-token helper the docs currently recommend; route the stack through a VPN container (136) and change server when blocked; keep the instance private (unlisted, LAN/VPN, no public registration) so it looks like one household, not a proxy. Most private instances work most of the time; some weeks need a pull.

6. Downloads for offline

Invidious can download (Preferences → Download), and for archiving channels you care about, yt-dlp on the server (pipx install yt-dlp, a cron with --download-archive) into a folder Jellyfin (42) indexes — your own content and creators who permit it.

Troubleshooting

Videos won't play but pages load. The companion isn't running or its key doesn't match; docker compose logs companion. Or YouTube is blocking — see step 5.

"Database error" on first start. check_tables: true creates the schema; the db wasn't ready — restart invidious once.

Thumbnails missing. Outbound access from the container; or image_proxy-style settings in preferences.

Registration page gone. registration_enabled: false — intended, once accounts exist.

Wrapping up

Three containers behind the proxy, a local account, subscriptions imported from Takeout, LibRedirect in the browser, Clipious or Yattee on the phone. YouTube without the machine around it — and a maintenance note: this is the one app in the series that fights back, so keep it updated and private.

Call us