selfhost.directory

The index of self-hostable projects — ranked, categorized and served with live updates.
Own your data and stop giving it to big tech.

YT Zero logo

Self-host YT Zero

2026.09.9

Self-hosted YouTube subscription inbox without algorithms

ReleaseOpen SourceUnlicense
Updatedlast month
LanguageTypescript
Setup difficultyEasy
MobileResponsive
Stars
669
Port
3001 (HTTP server)
Docker
ghcr.io/pelski/ytzero
Install with Docker Compose
Chronological subscription feed showing new videos from followed channelsTheater view video player interface

What is YT Zero?

YT Zero is a self-hosted video subscription manager that fetches public YouTube RSS feeds and stores content locally in SQLite or PostgreSQL. It provides a chronological feed of videos from channels you follow without requiring accounts or exposing users to recommendation algorithms. The software supports multiple isolated profiles for household use, child profiles with watch-time limits, direct streaming playback, optional local video downloads via yt-dlp plugin, channel imports from OPML and Google Takeout, audio-only background playback on mobile browsers, SponsorBlock integration, DeArrow metadata replacement, and TubeArchivist archive connection.

Should you self-host YT Zero?

YT Zero scores 52/100 on selfhost.directory (how scoring works) and ranks #52 of 118 in Media Server. It has 669 GitHub stars.

It shipped 9 stable releases in the past year. The latest, 2026.09.9, came out on 28 September 2026. New versions land roughly every 1 days.

Setup is easy: most people get it running in one sitting. An official Docker image is available. It is Unlicense-licensed and written mostly in Typescript.

Its closest alternative on selfhost.directory is Jellyfin (88/100, 58 727 stars). It scores higher overall, so compare the two before you pick. See YT Zero vs Jellyfin side by side.

Features

  • Audio-only playbackMedia Session integration
  • Direct video streamingH.264/AAC formats within quality limit
  • Local downloadsOptional plugin
  • Multiple Profiles
  • Subscription inbox
  • Tags and rules
  • Video player controls
  • Watch later scheduling

YT Zero README

How to install YT Zero

Install YT Zero with Docker Compose

Environment variables
IDLE_TIMEOUT_SECONDS, REFRESH_INTERVAL_MINUTES, FULL_SYNC_INTERVAL_MINUTES, VIDEO_MAINTENANCE_MAX_AGE_DAYS, DB_PATH, IMG_CACHE_DIR

docker compose up -d

Using the bundled compose file:

services: ytzero: image: ghcr.io/pelski/ytzero:latest container_name: ytzero ports: - "3001:3001" volumes: - ./data:/data environment: - IDLE_TIMEOUT_SECONDS=120 - REFRESH_INTERVAL_MINUTES=5 restart: unless-stopped

How to configure YT Zero

  • ADAPTIVE_REFRESH_INACTIVE_MAX_MINUTES4320Maximum adaptive interval for channels without recent uploads (up to three days).

    Maximum adaptive interval for channels without recent uploads (up to three days).

  • ADAPTIVE_REFRESH_MAX_MINUTES720Maximum automatic interval for one channel feed. This guarantees that infrequent channels remain in the refresh rotation.

    Maximum automatic interval for one channel feed. This guarantees that infrequent channels remain in the refresh rotation.

  • ADAPTIVE_REFRESH_MIN_MINUTES10Minimum automatic interval for one channel feed. This is the hard cooldown that prevents frequent uploaders from being polled continuously.

    Minimum automatic interval for one channel feed. This is the hard cooldown that prevents frequent uploaders from being polled continuously.

  • ADAPTIVE_REFRESH_UNKNOWN_MINUTES120Automatic interval used until a channel has at least three known publication dates.

    Automatic interval used until a channel has at least three known publication dates.

  • APP_EVENT_POLL_INTERVAL_MS750PostgreSQL cross-replica live-event polling interval in milliseconds (100–30000). Usually there is no reason to change it.

    PostgreSQL cross-replica live-event polling interval in milliseconds (100–30000). Usually there is no reason to change it.

  • APP_URL(derived from request)Public base URL. Used as the OIDC redirect origin and WebAuthn origin when behind a reverse proxy.

    Public base URL. Used as the OIDC redirect origin and WebAuthn origin when behind a reverse proxy.

  • AVATAR_DIR./data/avatarsUploaded profile avatars.

    Uploaded profile avatars.

  • AVATAR_REFRESH_BATCH_SIZE4Maximum channel avatars refreshed in one maintenance pass.

    Maximum channel avatars refreshed in one maintenance pass.

  • AVATAR_REFRESH_INTERVAL_MINUTES60Interval for refreshing stale channel avatars.

    Interval for refreshing stale channel avatars.

  • DATABASE_STATE_PATHnext to the data directoryMachine-local marker used to detect an unexpected engine/location change. It contains fingerprints and migration receipt IDs, never credentials.

    Machine-local marker used to detect an unexpected engine/location change. It contains fingerprints and migration receipt IDs, never credentials.

  • DATABASE_URLPostgreSQL connection URL

    PostgreSQL connection URL

  • DB_PATH./data/db/ytzero.dbSQLite database path

    SQLite database path

  • DOWNLOAD_COOKIES_DIR./data/download-cookiesPrivate persistent directory for per-profile YouTube cookie files. In a cluster it must be shared with the background worker.

    Private persistent directory for per-profile YouTube cookie files. In a cluster it must be shared with the background worker.

  • DOWNLOADS_DIR./data/downloadsWhere the YT-DLP Integration plugin stores downloaded video files and their <videoId>.ytz.json recovery sidecars. Move each sidecar with its media file.

    Where the YT-DLP Integration plugin stores downloaded video files and their <videoId>.ytz.json recovery sidecars. Move each sidecar with its media file.

  • DURATION_BATCH_SIZE20Videos processed per duration-backfill run.

    Videos processed per duration-backfill run.

  • DURATION_INTERVAL_MINUTES3Interval for the background job that backfills missing video durations.

    Interval for the background job that backfills missing video durations.

  • FFMPEG_PATHffmpegPath to ffmpeg, used for merged downloads and experimental stream-while-downloading playback.

    Path to ffmpeg, used for merged downloads and experimental stream-while-downloading playback.

  • FULL_SYNC_INTERVAL_MINUTES15Interval between full, rotating channel scans. One subscribed channel is scanned per run, using the same process as the manual channel sync button.

    Interval between full, rotating channel scans. One subscribed channel is scanned per run, using the same process as the manual channel sync button.

  • IDLE_TIMEOUT_SECONDS120HTTP idle timeout

    HTTP idle timeout

  • IMG_CACHE_DIR./data/imgcacheThumbnail and image cache directory

    Thumbnail and image cache directory

  • IMG_CACHE_TTL_DAYS5How long a cached image is fresh before a refetch is attempted.

    How long a cached image is fresh before a refetch is attempted.

  • IMPORT_ENRICH_BATCH_SIZE15Videos processed per import-enrichment run. Together with the interval, this sets the pace shown in the import wizard's time estimate.

    Videos processed per import-enrichment run. Together with the interval, this sets the pace shown in the import wizard's time estimate.

  • IMPORT_ENRICH_INTERVAL_MINUTES2Interval for the background job that fills in real metadata for videos brought in by a Takeout import.

    Interval for the background job that fills in real metadata for videos brought in by a Takeout import.

  • LIVE_INTERVAL_MINUTES3Followed-channel live-status check interval. This does not refetch old video metadata.

    Followed-channel live-status check interval. This does not refetch old video metadata.

  • LOG_PATH./data/logs/ytzero.logActive log file. Logs also go to stdout and rotate daily in the timezone selected in Settings, to dated files such as ytzero-2026-07-26.log; archives are retained. The in-app viewer reads the active file.

    Active log file. Logs also go to stdout and rotate daily in the timezone selected in Settings, to dated files such as ytzero-2026-07-26.log; archives are retained. The in-app viewer reads the active file.

  • PLAYLIST_SYNC_INTERVAL_MINUTES15Interval between followed-playlist refreshes. One playlist is synchronized per run.

    Interval between followed-playlist refreshes. One playlist is synchronized per run.

  • PORT3001HTTP server port

    HTTP server port

  • POSTS_SYNC_INTERVAL_MINUTES10Interval between Community Post scheduler passes. One eligible followed channel is synchronized per run.

    Interval between Community Post scheduler passes. One eligible followed channel is synchronized per run.

  • POSTS_SYNC_MAX_AGE_MINUTES360Per-channel freshness window for the persisted Community Post catalog, including channels whose last successful result was empty.

    Per-channel freshness window for the persisted Community Post catalog, including channels whose last successful result was empty.

  • REFRESH_INTERVAL_MINUTES5Followed-channel RSS refresh interval.

    Followed-channel RSS refresh interval.

  • RESTORE_SESSION_DIR./data/restore-sessionsTemporary staging directory for validated portable-restore sessions.

    Temporary staging directory for validated portable-restore sessions.

  • SQLITE_BUSY_TIMEOUT_MS5000How long SQLite waits for another process to release a database lock before returning SQLITE_BUSY (0–60000).

    How long SQLite waits for another process to release a database lock before returning SQLITE_BUSY (0–60000).

  • SQLITE_OPTIMIZE_INTERVAL_HOURS24Interval for bounded planner-statistics maintenance on a long-lived SQLite connection (1–168).

    Interval for bounded planner-statistics maintenance on a long-lived SQLite connection (1–168).

  • TUBE_ARCHIVIST_CONFIG_DIRnext to the data directoryDirectory containing the machine-local TubeArchivist access token. Share it between replicas that serve or synchronize TubeArchivist content.

    Directory containing the machine-local TubeArchivist access token. Share it between replicas that serve or synchronize TubeArchivist content.

  • TZOptional IANA timezone (for example Europe/Warsaw)

    Optional IANA timezone (for example Europe/Warsaw)

  • UI_DIST./publicBuilt frontend directory served by the backend.

    Built frontend directory served by the backend.

  • VIDEO_MAINTENANCE_MAX_AGE_DAYS90Maximum video age considered by automatic Shorts and duration backfills. Older videos are resolved only when accessed or manually synchronized.

    Maximum video age considered by automatic Shorts and duration backfills. Older videos are resolved only when accessed or manually synchronized.

  • WEBAUTHN_RP_ID(request hostname)Override the WebAuthn Relying Party ID (the registrable domain) when the auto-derived hostname is wrong.

    Override the WebAuthn Relying Party ID (the registrable domain) when the auto-derived hostname is wrong.

  • YTDLP_AUTO_UPDATE(unset; 1 in Docker)Initial default for automatic yt-dlp updates (1 means daily). An administrator can later choose Never, 1, 3, 7, or 30 days and the stable/nightly channel in the Downloads UI.

    Initial default for automatic yt-dlp updates (1 means daily). An administrator can later choose Never, 1, 3, 7, or 30 days and the stable/nightly channel in the Downloads UI.

  • YTDLP_PATHyt-dlpPath to the yt-dlp binary used by the YT-DLP Integration plugin.

    Path to the yt-dlp binary used by the YT-DLP Integration plugin.

  • YTZERO_AUTH_DISABLE(unset)Set to 1 to force the None auth method regardless of the saved setting. Emergency unlock if an auth method locks you out — see Authentication.

    Set to 1 to force the None auth method regardless of the saved setting. Emergency unlock if an auth method locks you out — see Authentication.

  • YTZERO_AUTH_METHODAuthentication method (set to shared to force shared-password auth)

    Authentication method (set to shared to force shared-password auth)

    values: shared, none, per-profile, OIDC, proxy

  • YTZERO_AUTH_PASSWORDShared login password when YTZERO_AUTH_METHOD=shared

    Shared login password when YTZERO_AUTH_METHOD=shared

  • YTZERO_BACKGROUND_TASKS1Set to 0 on HTTP-only replicas in a PostgreSQL cluster. Such replicas still serve requests and enqueue durable work, but do not start refresh schedulers, the download consumer, automatic yt-dlp updates, or TubeArchivist background synchronization. Exactly one replica should keep the default 1.

    Set to 0 on HTTP-only replicas in a PostgreSQL cluster. Such replicas still serve requests and enqueue durable work, but do not start refresh schedulers, the download consumer, automatic yt-dlp updates, or TubeArchivist background synchronization. Exactly one replica should keep the default 1.

  • YTZERO_INSTANCE_NAMEhostname and portOptional human-readable node name shown in Settings → Cluster. Nomad's NOMAD_ALLOC_NAME is used automatically when this variable is unset. Give each concurrently running instance a distinct name.

    Optional human-readable node name shown in Settings → Cluster. Nomad's NOMAD_ALLOC_NAME is used automatically when this variable is unset. Give each concurrently running instance a distinct name.

  • YTZERO_VERSIONdevVersion reported by /api/health. Set by the Docker build and by the native installer; there is no reason to set it by hand.

    Version reported by /api/health. Set by the Docker build and by the native installer; there is no reason to set it by hand.

YT Zero releases & version history

More in Media Server

Rate this project

Click to rate — no account needed.

0 comments

Posting as guest
Loading comments…