Update invidious stack to use companion approach
- Replace inv_sig_helper with invidious-companion for better YouTube API handling - Add healthcheck for main container - Add resource limits for all containers - Add SQL init scripts for fresh database setup - Update README with invidious secrets documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ stacks/
|
|||||||
├── perilous/ # Blog/website
|
├── perilous/ # Blog/website
|
||||||
├── ramz/ # Go web app
|
├── ramz/ # Go web app
|
||||||
├── bookclub/ # Form mailer
|
├── bookclub/ # Form mailer
|
||||||
|
├── invidious/ # YouTube frontend
|
||||||
├── watchtower/ # Auto container updates
|
├── watchtower/ # Auto container updates
|
||||||
├── dockge/ # Container management UI
|
├── dockge/ # Container management UI
|
||||||
└── smokeping/ # Network monitoring
|
└── smokeping/ # Network monitoring
|
||||||
@@ -96,6 +97,13 @@ Set these in Gitea → Repository → Settings → Actions → Secrets:
|
|||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `PERILOUS_CODE_SERVER_PASSWORD` | Code-server password |
|
| `PERILOUS_CODE_SERVER_PASSWORD` | Code-server password |
|
||||||
|
|
||||||
|
### Invidious
|
||||||
|
| Secret | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `INVIDIOUS_DB_PASSWORD` | PostgreSQL password |
|
||||||
|
| `INVIDIOUS_HMAC_KEY` | Generate: `openssl rand -hex 16` |
|
||||||
|
| `INVIDIOUS_COMPANION_KEY` | Generate: `openssl rand -hex 16` (must match companion) |
|
||||||
|
|
||||||
## Runner Setup
|
## Runner Setup
|
||||||
|
|
||||||
The workflow requires a self-hosted runner on the prod server:
|
The workflow requires a self-hosted runner on the prod server:
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
DOMAIN=${DOMAIN}
|
DOMAIN=${DOMAIN}
|
||||||
INVIDIOUS_DB_PASSWORD=${INVIDIOUS_DB_PASSWORD}
|
INVIDIOUS_DB_PASSWORD=${INVIDIOUS_DB_PASSWORD}
|
||||||
INVIDIOUS_VISITOR_DATA=${INVIDIOUS_VISITOR_DATA}
|
|
||||||
INVIDIOUS_PO_TOKEN=${INVIDIOUS_PO_TOKEN}
|
|
||||||
INVIDIOUS_HMAC_KEY=${INVIDIOUS_HMAC_KEY}
|
INVIDIOUS_HMAC_KEY=${INVIDIOUS_HMAC_KEY}
|
||||||
|
INVIDIOUS_COMPANION_KEY=${INVIDIOUS_COMPANION_KEY}
|
||||||
|
|||||||
12
stacks/invidious/config/sql/annotations.sql
Normal file
12
stacks/invidious/config/sql/annotations.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
-- Table: public.annotations
|
||||||
|
|
||||||
|
-- DROP TABLE public.annotations;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.annotations
|
||||||
|
(
|
||||||
|
id text NOT NULL,
|
||||||
|
annotations xml,
|
||||||
|
CONSTRAINT annotations_id_key UNIQUE (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.annotations TO current_user;
|
||||||
30
stacks/invidious/config/sql/channel_videos.sql
Normal file
30
stacks/invidious/config/sql/channel_videos.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-- Table: public.channel_videos
|
||||||
|
|
||||||
|
-- DROP TABLE public.channel_videos;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.channel_videos
|
||||||
|
(
|
||||||
|
id text NOT NULL,
|
||||||
|
title text,
|
||||||
|
published timestamp with time zone,
|
||||||
|
updated timestamp with time zone,
|
||||||
|
ucid text,
|
||||||
|
author text,
|
||||||
|
length_seconds integer,
|
||||||
|
live_now boolean,
|
||||||
|
premiere_timestamp timestamp with time zone,
|
||||||
|
views bigint,
|
||||||
|
CONSTRAINT channel_videos_id_key UNIQUE (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.channel_videos TO current_user;
|
||||||
|
|
||||||
|
-- Index: public.channel_videos_ucid_idx
|
||||||
|
|
||||||
|
-- DROP INDEX public.channel_videos_ucid_idx;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS channel_videos_ucid_idx
|
||||||
|
ON public.channel_videos
|
||||||
|
USING btree
|
||||||
|
(ucid COLLATE pg_catalog."default");
|
||||||
|
|
||||||
25
stacks/invidious/config/sql/channels.sql
Normal file
25
stacks/invidious/config/sql/channels.sql
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
-- Table: public.channels
|
||||||
|
|
||||||
|
-- DROP TABLE public.channels;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.channels
|
||||||
|
(
|
||||||
|
id text NOT NULL,
|
||||||
|
author text,
|
||||||
|
updated timestamp with time zone,
|
||||||
|
deleted boolean,
|
||||||
|
subscribed timestamp with time zone,
|
||||||
|
CONSTRAINT channels_id_key UNIQUE (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.channels TO current_user;
|
||||||
|
|
||||||
|
-- Index: public.channels_id_idx
|
||||||
|
|
||||||
|
-- DROP INDEX public.channels_id_idx;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS channels_id_idx
|
||||||
|
ON public.channels
|
||||||
|
USING btree
|
||||||
|
(id COLLATE pg_catalog."default");
|
||||||
|
|
||||||
22
stacks/invidious/config/sql/nonces.sql
Normal file
22
stacks/invidious/config/sql/nonces.sql
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
-- Table: public.nonces
|
||||||
|
|
||||||
|
-- DROP TABLE public.nonces;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.nonces
|
||||||
|
(
|
||||||
|
nonce text,
|
||||||
|
expire timestamp with time zone,
|
||||||
|
CONSTRAINT nonces_id_key UNIQUE (nonce)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.nonces TO current_user;
|
||||||
|
|
||||||
|
-- Index: public.nonces_nonce_idx
|
||||||
|
|
||||||
|
-- DROP INDEX public.nonces_nonce_idx;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS nonces_nonce_idx
|
||||||
|
ON public.nonces
|
||||||
|
USING btree
|
||||||
|
(nonce COLLATE pg_catalog."default");
|
||||||
|
|
||||||
19
stacks/invidious/config/sql/playlist_videos.sql
Normal file
19
stacks/invidious/config/sql/playlist_videos.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-- Table: public.playlist_videos
|
||||||
|
|
||||||
|
-- DROP TABLE public.playlist_videos;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.playlist_videos
|
||||||
|
(
|
||||||
|
title text,
|
||||||
|
id text,
|
||||||
|
author text,
|
||||||
|
ucid text,
|
||||||
|
length_seconds integer,
|
||||||
|
published timestamptz,
|
||||||
|
plid text references playlists(id),
|
||||||
|
index int8,
|
||||||
|
live_now boolean,
|
||||||
|
PRIMARY KEY (index,plid)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.playlist_videos TO current_user;
|
||||||
29
stacks/invidious/config/sql/playlists.sql
Normal file
29
stacks/invidious/config/sql/playlists.sql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
-- Type: public.privacy
|
||||||
|
|
||||||
|
-- DROP TYPE public.privacy;
|
||||||
|
|
||||||
|
CREATE TYPE public.privacy AS ENUM
|
||||||
|
(
|
||||||
|
'Public',
|
||||||
|
'Unlisted',
|
||||||
|
'Private'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Table: public.playlists
|
||||||
|
|
||||||
|
-- DROP TABLE public.playlists;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.playlists
|
||||||
|
(
|
||||||
|
title text,
|
||||||
|
id text primary key,
|
||||||
|
author text,
|
||||||
|
description text,
|
||||||
|
video_count integer,
|
||||||
|
created timestamptz,
|
||||||
|
updated timestamptz,
|
||||||
|
privacy privacy,
|
||||||
|
index int8[]
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON public.playlists TO current_user;
|
||||||
23
stacks/invidious/config/sql/session_ids.sql
Normal file
23
stacks/invidious/config/sql/session_ids.sql
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- Table: public.session_ids
|
||||||
|
|
||||||
|
-- DROP TABLE public.session_ids;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.session_ids
|
||||||
|
(
|
||||||
|
id text NOT NULL,
|
||||||
|
email text,
|
||||||
|
issued timestamp with time zone,
|
||||||
|
CONSTRAINT session_ids_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.session_ids TO current_user;
|
||||||
|
|
||||||
|
-- Index: public.session_ids_id_idx
|
||||||
|
|
||||||
|
-- DROP INDEX public.session_ids_id_idx;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS session_ids_id_idx
|
||||||
|
ON public.session_ids
|
||||||
|
USING btree
|
||||||
|
(id COLLATE pg_catalog."default");
|
||||||
|
|
||||||
29
stacks/invidious/config/sql/users.sql
Normal file
29
stacks/invidious/config/sql/users.sql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
-- Table: public.users
|
||||||
|
|
||||||
|
-- DROP TABLE public.users;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.users
|
||||||
|
(
|
||||||
|
updated timestamp with time zone,
|
||||||
|
notifications text[],
|
||||||
|
subscriptions text[],
|
||||||
|
email text NOT NULL,
|
||||||
|
preferences text,
|
||||||
|
password text,
|
||||||
|
token text,
|
||||||
|
watched text[],
|
||||||
|
feed_needs_update boolean,
|
||||||
|
CONSTRAINT users_email_key UNIQUE (email)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.users TO current_user;
|
||||||
|
|
||||||
|
-- Index: public.email_unique_idx
|
||||||
|
|
||||||
|
-- DROP INDEX public.email_unique_idx;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS email_unique_idx
|
||||||
|
ON public.users
|
||||||
|
USING btree
|
||||||
|
(lower(email) COLLATE pg_catalog."default");
|
||||||
|
|
||||||
23
stacks/invidious/config/sql/videos.sql
Normal file
23
stacks/invidious/config/sql/videos.sql
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- Table: public.videos
|
||||||
|
|
||||||
|
-- DROP TABLE public.videos;
|
||||||
|
|
||||||
|
CREATE UNLOGGED TABLE IF NOT EXISTS public.videos
|
||||||
|
(
|
||||||
|
id text NOT NULL,
|
||||||
|
info text,
|
||||||
|
updated timestamp with time zone,
|
||||||
|
CONSTRAINT videos_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
GRANT ALL ON TABLE public.videos TO current_user;
|
||||||
|
|
||||||
|
-- Index: public.id_idx
|
||||||
|
|
||||||
|
-- DROP INDEX public.id_idx;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS id_idx
|
||||||
|
ON public.videos
|
||||||
|
USING btree
|
||||||
|
(id COLLATE pg_catalog."default");
|
||||||
|
|
||||||
@@ -3,8 +3,6 @@ services:
|
|||||||
image: quay.io/invidious/invidious:latest
|
image: quay.io/invidious/invidious:latest
|
||||||
container_name: invidious
|
container_name: invidious
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
|
||||||
- "3001:3000"
|
|
||||||
environment:
|
environment:
|
||||||
INVIDIOUS_CONFIG: |
|
INVIDIOUS_CONFIG: |
|
||||||
db:
|
db:
|
||||||
@@ -14,45 +12,81 @@ services:
|
|||||||
host: invidious-db
|
host: invidious-db
|
||||||
port: 5432
|
port: 5432
|
||||||
check_tables: true
|
check_tables: true
|
||||||
signature_server: inv_sig_helper:12999
|
invidious_companion:
|
||||||
visitor_data: ${INVIDIOUS_VISITOR_DATA}
|
- private_url: "http://invidious-companion:8282/companion"
|
||||||
po_token: ${INVIDIOUS_PO_TOKEN}
|
public_url: "https://inv.${DOMAIN}"
|
||||||
|
invidious_companion_key: "${INVIDIOUS_COMPANION_KEY}"
|
||||||
|
external_port: 443
|
||||||
|
domain: inv.${DOMAIN}
|
||||||
|
https_only: true
|
||||||
hmac_key: "${INVIDIOUS_HMAC_KEY}"
|
hmac_key: "${INVIDIOUS_HMAC_KEY}"
|
||||||
|
hsts: false
|
||||||
|
log_level: Info
|
||||||
|
healthcheck:
|
||||||
|
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/trending || exit 1
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 2
|
||||||
logging:
|
logging:
|
||||||
options:
|
options:
|
||||||
max-size: "1G"
|
max-size: "1G"
|
||||||
max-file: "4"
|
max-file: "4"
|
||||||
depends_on:
|
depends_on:
|
||||||
- invidious-db
|
- invidious-db
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1'
|
||||||
|
memory: 2G
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.services.invidious.loadbalancer.server.port=3000"
|
- "traefik.http.services.invidious.loadbalancer.server.port=3000"
|
||||||
- "traefik.http.routers.invidious.entrypoints=https"
|
- "traefik.http.routers.invidious.entrypoints=https"
|
||||||
- "traefik.http.routers.invidious.rule=Host(`invid.${DOMAIN}`, `i.${DOMAIN}`)"
|
- "traefik.http.routers.invidious.rule=Host(`inv.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.invidious.tls.certresolver=http"
|
- "traefik.http.routers.invidious.tls.certresolver=http"
|
||||||
networks:
|
networks:
|
||||||
- web
|
- web
|
||||||
- default
|
- default
|
||||||
|
|
||||||
inv_sig_helper:
|
invidious-companion:
|
||||||
image: quay.io/invidious/inv-sig-helper:latest
|
image: quay.io/invidious/invidious-companion:latest
|
||||||
container_name: inv-sig-helper
|
container_name: invidious-companion
|
||||||
command: ["--tcp", "0.0.0.0:12999"]
|
|
||||||
environment:
|
|
||||||
- RUST_LOG=info
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- SERVER_SECRET_KEY=${INVIDIOUS_COMPANION_KEY}
|
||||||
|
- SERVER_BASE_URL=https://inv.${DOMAIN}/companion
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: "1G"
|
||||||
|
max-file: "4"
|
||||||
cap_drop:
|
cap_drop:
|
||||||
- ALL
|
- ALL
|
||||||
read_only: true
|
read_only: true
|
||||||
|
volumes:
|
||||||
|
- companion-cache:/var/tmp/youtubei.js:rw
|
||||||
security_opt:
|
security_opt:
|
||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 1G
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.services.invidious-companion.loadbalancer.server.port=8282"
|
||||||
|
- "traefik.http.routers.invidious-companion.entrypoints=https"
|
||||||
|
- "traefik.http.routers.invidious-companion.rule=Host(`inv.${DOMAIN}`) && PathPrefix(`/companion`)"
|
||||||
|
- "traefik.http.routers.invidious-companion.tls.certresolver=http"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
|
||||||
invidious-db:
|
invidious-db:
|
||||||
image: postgres:14
|
image: postgres:14
|
||||||
container_name: invidious-db
|
container_name: invidious-db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./postgres:/var/lib/postgresql/data
|
- postgres-data:/var/lib/postgresql/data
|
||||||
- ./config/sql:/config/sql
|
- ./config/sql:/config/sql
|
||||||
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
|
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
|
||||||
environment:
|
environment:
|
||||||
@@ -61,6 +95,15 @@ services:
|
|||||||
POSTGRES_PASSWORD: ${INVIDIOUS_DB_PASSWORD}
|
POSTGRES_PASSWORD: ${INVIDIOUS_DB_PASSWORD}
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 1G
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-data:
|
||||||
|
companion-cache:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
web:
|
web:
|
||||||
|
|||||||
12
stacks/invidious/docker/init-invidious-db.sh
Executable file
12
stacks/invidious/docker/init-invidious-db.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -eou pipefail
|
||||||
|
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channels.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/videos.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channel_videos.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/users.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/session_ids.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/nonces.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/annotations.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/playlists.sql
|
||||||
|
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/playlist_videos.sql
|
||||||
Reference in New Issue
Block a user