Invidious
Invidious to otwartoźródłowy, alternatywny frontend do YouTube napisany w Crystalu (licencja AGPLv3). Pobiera dane z YouTube po stronie serwera i serwuje własny, lekki interfejs: bez reklam, bez skryptów Google w przeglądarce, bez konta Google. Subskrypcje, playlisty i historia oglądania są trzymane lokalnie w PostgreSQL, a nie na koncie YouTube.

Interfejs jest prosty i szybki: feed subskrypcji, wyszukiwarka z filtrami i strony kanałów. Feed jest chronologiczny, bez algorytmu rekomendacji i bez wplecionych Shorts, a każdy kanał udostępnia RSS.
Zakładka Popularne nie odpowiada trendom YouTube: Invidious wylicza ją z subskrypcji użytkowników instancji. Pod odtwarzaczem jest lista Pobierz jako ze wszystkimi formatami i tryb audio-only. Konto Invidious nie ma związku z kontem Google, a subskrypcje można zaimportować z Google Takeout, NewPipe, FreeTube lub OPML.
Architektura
Instalacja składa się z trzech kontenerów: aplikacji Invidious, serwisu invidious-companion i bazy PostgreSQL. Od 2025 roku samo Invidious nie wystarcza. YouTube blokuje zapytania z adresów serwerowych komunikatem „Sign in to confirm you're not a bot”, a pobranie strumieni wymaga generowania tokenów po_token i deszyfrowania sygnatur playera. Tę część przejął osobny serwis invidious-companion napisany w TypeScript (Deno, biblioteka YouTube.js).
| Kontener | Opis |
|---|---|
invidious | Interfejs WWW, API, konta, subskrypcje |
invidious-companion | Player YouTube, po_token, proxy wideo |
postgres_17 | Baza danych (współdzielona) |
Architektura w sieci lokalnej
graph LR
B[Przeglądarka] -->|HTTP :80| P[Reverse proxy]
P -->|"/* → :3000"| I[invidious]
P -->|"/companion/* → :8282"| C[invidious-companion]
I -->|SQL :5432| DB[(PostgreSQL<br/>baza invidious)]
I -->|"private_url :8282<br/>SERVER_SECRET_KEY"| C
C --- V[(companioncache<br/>cache playera)]
Reverse proxy obsługuje jedną domenę i rozdziela ruch po ścieżce. Żądania z prefiksem /companion trafiają bezpośrednio do companiona, wszystko inne do Invidious. Baza nie jest wystawiona na zewnątrz: Invidious łączy się z nią po nazwie kontenera w sieci postgresql_17_default. Z companionem Invidious rozmawia wewnątrz sieci Docker (private_url), a tę komunikację zabezpiecza wspólny klucz COMPANION_KEY.
Połączenie z YouTube
Odtworzenie filmu przebiega w dwóch krokach. Najpierw przeglądarka otwiera stronę filmu w Invidious, które pyta companiona o dane playera:
sequenceDiagram
participant B as Przeglądarka
participant I as invidious
participant C as companion
participant Y as YouTube
B->>I: /watch?v=ID
I->>C: dane playera
C->>Y: player API + po_token
Y-->>C: formaty strumieni
C-->>I: formaty i napisy
I-->>B: strona z linkami /companion/...
Potem player w przeglądarce pobiera wideo już bez udziału Invidious, bezpośrednio od companiona:
sequenceDiagram
participant B as Przeglądarka
participant C as companion
participant Y as YouTube
B->>C: manifest DASH
C-->>B: lista segmentów
loop każdy segment
B->>C: /companion/videoplayback
C->>Y: segment z googlevideo.com
Y-->>C: audio/wideo
C-->>B: segment
end
Metadane potrzebne do list (wyszukiwanie, kanały, feedy subskrypcji, komentarze) Invidious pobiera samodzielnie z wewnętrznego API YouTube. Do companiona trafia wszystko, co dotyczy odtwarzania: dane playera, manifest DASH, napisy i same segmenty wideo.
Invidious obsługuje dwa tryby dostępu do companiona:
- Tylko
private_url: przeglądarka nie zna adresu companiona. Wszystkie żądania/companion/...przyjmuje Invidious i przekazuje dalej, więc każdy fragment wideo przechodzi przez dwa kontenery. Przy kilku równoległych odtworzeniach w jakości DASH Invidious staje się wąskim gardłem. private_url+public_url: Invidious nadal pyta companiona o dane playera wewnątrz sieci Docker, ale w HTML wstawia linki dopublic_url. Przeglądarka pobiera manifest i segmenty bezpośrednio od companiona. Wymaga to dodatkowej trasy w reverse proxy: prefiks ścieżki/companionw domenie Invidious kierowany na port 8282 companiona.
Opisywana instancja używa drugiego trybu. Manifest DASH generowany jest z local=true, a jego BaseURL wskazuje na /companion/videoplayback, więc segmenty dalej idą przez companiona. Przeglądarka nie nawiązuje połączeń z serwerami Google, a YouTube widzi jedynie publiczny adres IP łącza domowego. Kosztem trybu proxy jest transfer, bo każdy oglądany film przechodzi przez serwer.
Konfiguracja
config.yml
Konfiguracja Invidious leży w osobnym pliku config.yml, montowanym do kontenera tylko do odczytu.
check_tables: true
invidious_companion:
- private_url: "http://invidious-companion:8282/companion"
public_url: "http://invidious.mini-1.lan/companion"
domain: invidious.mini-1.lan
external_port: 80
https_only: false
statistics_enabled: false
registration_enabled: true
captcha_enabled: false
default_user_preferences:
locale: pl
region: PL
dark_mode: light
quality: dash
related_videos: true
# bez "Na czasie" (Trending)
feed_menu: ["Popular", "Subscriptions", "Playlists"]
default_home: Popular
docker-compose.yml
services:
invidious:
image: quay.io/invidious/invidious:latest
container_name: invidious
restart: unless-stopped
environment:
INVIDIOUS_DATABASE_URL: postgres://invidious:${DB_PASSWORD}@postgres_17:5432/invidious
INVIDIOUS_HMAC_KEY: ${HMAC_KEY}
INVIDIOUS_INVIDIOUS_COMPANION_KEY: ${COMPANION_KEY}
volumes:
- ./config.yml:/invidious/config/config.yml:ro
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/stats || exit 1
interval: 30s
timeout: 5s
retries: 2
depends_on:
- invidious-companion
networks: [proxy, postgres_internal]
deploy:
resources:
limits:
memory: 1G
cpus: "1.0"
invidious-companion:
image: quay.io/invidious/invidious-companion:latest
container_name: invidious-companion
restart: unless-stopped
environment:
SERVER_SECRET_KEY: ${COMPANION_KEY}
read_only: true
cap_drop: [ALL]
security_opt:
- no-new-privileges:true
volumes:
- companioncache:/var/tmp/youtubei.js:rw
networks: [proxy]
labels:
com.centurylinklabs.watchtower.enable: "true"
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
volumes:
companioncache:
networks:
proxy:
external: true
postgres_internal:
external: true
name: postgresql_17_default
.env
DB_PASSWORD=<haslo-do-bazy>
HMAC_KEY=<losowy-ciag> # openssl rand -hex 20
COMPANION_KEY=<16-znakow> # dokładnie 16 znaków [a-zA-Z0-9]: openssl rand -hex 8
Parametry konfiguracyjne
| Parametr | Wartość | Opis |
|---|---|---|
database_url | INVIDIOUS_DATABASE_URL | Połączenie z PostgreSQL w formacie URL, z hasłem z .env. Instancja korzysta ze współdzielonego kontenera bazy przez zewnętrzną sieć Docker. |
check_tables | true | Przy starcie tworzy brakujące tabele i kolumny. Bez tego bazę trzeba inicjalizować ręcznie skryptami SQL. |
invidious_companion | private_url, public_url | Lista instancji companiona. private_url to adres w sieci Docker, używany przez Invidious. public_url to adres companiona widziany przez przeglądarkę (domena instancji + /companion); bez niego Invidious przekazuje cały ruch wideo samodzielnie. |
invidious_companion_key | INVIDIOUS_INVIDIOUS_COMPANION_KEY | Wspólny sekret z SERVER_SECRET_KEY companiona. |
hmac_key | INVIDIOUS_HMAC_KEY | Klucz do podpisywania tokenów. Wymagany, bez niego Invidious nie wystartuje. |
domain | nazwa hosta instancji | Domena, pod którą instancja jest widoczna. Używana w generowanych linkach, RSS i ciasteczkach. |
external_port | 80 | Port widoczny dla klienta (port reverse proxy), nie port kontenera. |
https_only | false | Instancja działa po HTTP w sieci lokalnej. Przy true linki i ciasteczka wymuszałyby HTTPS. |
statistics_enabled | false | Wyłącza publiczne statystyki instancji (/api/v1/stats zwraca tylko nazwę, wersję i gałąź oprogramowania). |
registration_enabled | true | Pozwala zakładać konta. Instancja jest dostępna tylko z sieci lokalnej. |
captcha_enabled | false | Brak captchy przy logowaniu i rejestracji. |
default_user_preferences | locale: pl, region: PL, dark_mode: light | Domyślne preferencje dla nowych kont i niezalogowanych: polski interfejs, region treści PL, jasny motyw. |
quality | dash | Domyślnie adaptacyjny strumień DASH (do najwyższej dostępnej rozdzielczości) zamiast pojedynczego pliku MP4 360p/720p. |
feed_menu / default_home | Popular, Subscriptions, Playlists / Popular | Zakładki w menu. Usunięta zakładka Trending, ponieważ YouTube wycofał stronę „Na czasie”. |
Companion: utwardzenie kontenera
Companion przetwarza dane i kod pobierane z YouTube (skrypty playera wykonywane przez YouTube.js), więc jest najbardziej narażonym elementem instalacji. Poza cache playera nie przechowuje stanu, co pozwala uruchomić go z trzema ograniczeniami w docker-compose.yml:
| Opcja | Opis |
|---|---|
read_only: true | System plików kontenera jest tylko do odczytu, więc nie da się podmienić plików aplikacji ani zostawić nowych binarek. Zapisywalny jest wyłącznie wolumen companioncache (/var/tmp/youtubei.js) na cache playera. |
cap_drop: [ALL] | Usuwa wszystkie capabilities Linuksa, które Docker domyślnie nadaje kontenerom (np. CAP_CHOWN, CAP_NET_RAW, CAP_SETUID). Nawet proces z UID 0 nie zmieni właściciela plików, nie otworzy surowych gniazd ani nie zmieni UID. Companion potrzebuje tylko zwykłych połączeń TCP, więc nie traci żadnej funkcji. |
no-new-privileges:true | Ustawia flagę jądra PR_SET_NO_NEW_PRIVS. Procesy nie mogą zyskać uprawnień przez binarki setuid/setgid ani file capabilities, co blokuje eskalację np. przez su po przejęciu procesu. |
Te trzy opcje są niezależne i warto stosować je razem. read_only zamyka zapis, cap_drop odbiera uprawnienia, które proces już ma, a no-new-privileges uniemożliwia zdobycie nowych.
Aktualizacje
Strategia aktualizacji jest rozdzielona:
- invidious-companion ma label
com.centurylinklabs.watchtower.enable, więc Watchtower podmienia go automatycznie. YouTube często zmienia player i mechanizmy anty-botowe, a poprawki trafiają do companiona w ciągu godzin lub dni. - invidious aktualizowany jest ręcznie (
docker compose pull && docker compose up -d), bo nowa wersja może migrować schemat bazy.
Invidious vs YouTube
Zalety
- Brak reklam i sponsorowanych wstawek w interfejsie, bez rozszerzeń w przeglądarce.
- Prywatność: Google nie dostaje historii oglądania, wyszukiwań ani ciasteczek. Subskrypcje i historia leżą we własnej bazie.
- Chronologiczny feed bez algorytmu i bez Shorts wymuszanych na stronie głównej.
- Pobieranie i tryb audio bez abonamentu.
- Lekkość: strona działa bez ciężkiego JS YouTube.
- API (
/api/v1/...) zgodne z wieloma klientami zewnętrznymi i przydatne do automatyzacji (RSS, skrypty, agenci).
Wady
- Kruchość: YouTube regularnie zmienia player i zabezpieczenia. Po takiej zmianie odtwarzanie przestaje działać do czasu aktualizacji companiona (stąd Watchtower na companionie).
- Jedno wyjściowe IP: wszystkie zapytania idą z adresu domowego łącza. Przy intensywnym użyciu YouTube może ograniczyć ten adres.
- Brak interakcji: nie da się polubić filmu, skomentować ani wspierać twórcy członkostwem z poziomu Invidious.
- Brak oficjalnych aplikacji mobilnych i TV; wygoda zależy od klientów zewnętrznych.
- Brak archiwum: Invidious niczego nie przechowuje, więc film usunięty z YouTube znika także z instancji. Zapisywanie filmów na własnym dysku opisuje wpis MeTube i Tube Archivist .
Rozszerzenie LibRedirect (GitHub, Firefox, Chrome/Chromium) w przeglądarce automatycznie przepisuje linki youtube.com/watch?v=... na adres własnej instancji Invidious. Linki udostępnione przez innych otwierają się wtedy od razu we własnej instancji. Tak samo działa dla linków reddit.com i instancji Redlib .
Linki
- Invidious, strona projektu
- iv-org/invidious, kod źródłowy
- iv-org/invidious-companion, serwis pobierający strumienie
- Self-hosting #9 — MeTube i Tube Archivist , pobieranie i archiwizacja filmów z YouTube





