Deploy YT Zero
YouTube subscriptions as a feed reader. RSS, no Google account needed.
YT Zero
Just deployed
/data
Deploy and Host YT Zero on Railway
YT Zero is a self-hosted YouTube subscription inbox. It watches the public feeds of the channels you follow and gives you one chronological list of their videos, with no Google account, no YouTube Data API key and no recommendation algorithm in the way. Subscriptions, watch progress, playlists and tags live in your own database.
About Hosting YT Zero
YT Zero runs as a single container with everything stateful on one disk. It serves its own frontend, refreshes channel feeds on a background schedule, and stores subscriptions, watch history, playlists and cached thumbnails in a local SQLite database. There is no separate worker process, no queue and no cache server to run alongside it, which is why one Railway service with one volume is the whole deployment.
Because it reads public channel feeds rather than talking to a Google account, there is nothing to authorize and no API quota to manage. The container also bundles yt-dlp and ffmpeg, so the optional download feature works without adding anything to the stack.
Common Use Cases
- Following YouTube channels without a Google account, an algorithmic home feed or a watch history that follows you around the web.
- A household install with a profile each, including child profiles with daily watch-time limits, subscribed-content-only mode and a settings PIN.
- Offline and low-bandwidth viewing, where scheduled videos are downloaded ahead of time and played back from local disk with instant seeking.
Dependencies for YT Zero Hosting
- A container runtime and one persistent disk. Nothing else. SQLite is built in, and PostgreSQL is optional rather than required.
- Outbound internet access to fetch public channel feeds, video metadata and thumbnails.
Deployment Dependencies
- Upstream project: https://github.com/Pelski/ytzero
- Documentation: https://github.com/Pelski/ytzero/wiki
- Container image:
ghcr.io/pelski/ytzero - License: AGPL-3.0-only
Implementation Details
This template deploys one service from upstream's own image with a volume
mounted at /data, which holds the database, the thumbnail cache, profile
avatars, logs and any downloaded videos. The health check points at
/api/health, which is deliberately declared before the login middleware so it
keeps answering after you turn authentication on.
Four variables are set for you:
| Variable | Value | Reason |
|---|---|---|
PORT | 3001 | Railway injects 8080 into image services by default, which would move the listener off the port the public domain targets |
APP_URL | your Railway URL | Used as the sign-in redirect origin for single sign-on and passkeys |
DOWNLOAD_COOKIES_DIR | /data/download-cookies | The image does not set this one, so it would fall back to disposable container disk |
RESTORE_SESSION_DIR | /data/restore-sessions | Same gap, and this directory stages backup restores |
Everything else the application reads is already pointed at /data inside the
image and does not need repeating here.
Turn on a login before you share the URL. YT Zero ships with authentication
set to None, because upstream assumes a trusted home network. On a public
address that means anyone with the link can read your feed and change your
settings. There is no variable that switches a login on, so open Settings >
Authentication, choose Shared login + profiles or Login per profile,
set a password and activate it. Only the first profile created can change this.
Exactly one login method is active at a time and there is no fallback to any
other. If you lock yourself out, add YTZERO_AUTH_DISABLE=1 to the service
variables, redeploy, fix the setting in the app, then remove the variable and
redeploy again.
Getting started. The app boots empty. Add channels under Settings >
Channels by URL, by OPML file, or by importing subscriptions.csv from Google
Takeout. The feed fills within a minute or two, then refreshes every five
minutes on a schedule that adapts to how often each channel actually uploads.
Optional extras, all off by default and addable later:
- Video downloads, using the bundled yt-dlp and ffmpeg. Files go to
/data/downloads, so set a retention policy and a storage cap in the same settings screen or the volume will grow quickly. - Single sign-on and passkeys.
APP_URLis already correct for the Railway domain. On a custom domain, updateAPP_URLand addWEBAUTHN_RP_ID. REFRESH_INTERVAL_MINUTESandFULL_SYNC_INTERVAL_MINUTESto slow the refresh pace if you follow a very large number of channels.
Moving to PostgreSQL later. Add a Postgres service, then use Settings >
Dangerous > Database in the app to run the migration, which copies a snapshot
and verifies row counts before anything switches over. Only then set
DATABASE_URL and redeploy. Setting DATABASE_URL on an install that already
has data without doing the in-app migration is refused by design.
Troubleshooting. If a deploy hangs in "Deploying" and then fails, check that
PORT is still 3001. If the feed stays empty after adding a channel, the
first fetch runs in the background just after you add it, so wait a minute and
reload before re-adding the channel with its full youtube.com/@handle URL. If
sign-in redirects break after you attach a custom domain, APP_URL still points
at the old address.
Updating. The template tracks latest, and upstream releases very often, so
redeploying the service picks up the newest build. Migrations run forward
automatically on boot. Pin the image to a dated tag on your own service if you
would rather have reproducible redeploys.
Why Deploy YT Zero on Railway?
Railway gives this app exactly the two things it needs and nothing it does not: a container and a persistent disk. There is no database server to provision, no queue, no reverse proxy to configure for TLS, and no port mapping to reason about. The public domain, the certificate and the health check are handled for you.
It also suits how the app is maintained. Upstream ships releases constantly, and a redeploy on Railway is a single click that pulls the newest image and runs migrations on boot. Scaling up is raising the volume size when downloads start piling up, and moving to PostgreSQL later is adding one more service in the same project rather than rebuilding the deployment.
Template Content
YT Zero
ghcr.io/pelski/ytzero:latest