---
title: "Deploy YT Zero"
description: "YouTube subscriptions as a feed reader. RSS, no Google account needed."
category: "Other"
url: https://railway.com/deploy/yt-zero
---

# Deploy YT Zero

YouTube subscriptions as a feed reader. RSS, no Google account needed.

**[Deploy YT Zero on Railway](https://railway.com/template/yt-zero)**

- **Creator:** Auromations
- **Category:** Other
- **Total deploys:** 4

## Template content

### YT Zero https://raw.githubusercontent.com/Pelski/ytzero/main/docs/assets/ytzero-logo.svg

- **Image:** ghcr.io/pelski/ytzero:latest
- **Health check:** /api/health
- **Public domain:** Yes

## Documentation

# 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_URL` is already correct for the Railway
  domain. On a custom domain, update `APP_URL` and add `WEBAUTHN_RP_ID`.
- `REFRESH_INTERVAL_MINUTES` and `FULL_SYNC_INTERVAL_MINUTES` to 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.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — [Jul'26] Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/yt-zero
