Self-Hosted YouTube Frontend

Watch your subscriptions privately without touching Google

TypeScriptExpressRedis / BullMQShaka PlayerPlaywrightDocker
Role
Independent full-stack and systems developer
Context
Private, chronological subscription viewing without browser calls to Google
Implemented
Feed ingestion, stream extraction, media proxying, playback, storage, queues, and deployment tooling
Constraint
Keep every dependency beyond Node optional and swappable
Outcome
One tested codebase that runs locally or with PostgreSQL, S3, Redis, and worker replicas
Status
Working self-hosted application with resilience and deployment suites

YouTube knows a lot about you. This is a self-hosted frontend where the server proxies every stream, thumbnail, and piece of metadata. The browser never makes a single request to Google. No account, no tracking, no algorithm. Just a chronological feed of channels I chose.

Every screenshot below is a live capture from a running instance with imported subscriptions, real feed, and real playback.

01

A subscription feed without the algorithm

The Today page lists new videos from subscribed channels in plain chronological order. Durations, titles, and thumbnails are all served through the proxy. Subscriptions come in via OPML/CSV import or straight from your browser session, so leaving YouTube proper is a five-minute move, not a fresh start.

The Today feed showing new subscription videos with proxied thumbnails and durations
02

Playback through the server, not Google

The watch page shows the hard part working: the server extracts the stream (the badge in the header reads "innertube/dash, 0.78s"), and Shaka Player plays adaptive DASH through the proxy with a progressive MP4 fallback. Metadata, subscriber counts, and comments all arrive server-side. This frame was captured mid-playback.

A video playing through the server proxy with extraction timing badge and full metadata
03

Own your subscription graph

The import surface handles the messy reality of leaving a platform: fetch subscriptions from your logged-in browser, upload a cookies.txt, import OPML/CSV, and refresh extraction cookies when YouTube’s bot detection bites. Operational edge cases are treated as first-class UI, not error messages.

The subscriptions page with browser fetch, cookies.txt upload, and OPML/CSV import options

The same code runs on a laptop or a cluster

The premise is privacy: the server proxies every stream, thumbnail, and byte of metadata, so the browser never makes a request to Google. The hard part is extraction — pulling a playable stream server-side and handing it to the player through the proxy — and that is where most of the engineering went.

Everything beyond Node is optional and chosen by an environment variable, not baked in. The store is SQLite by default or PostgreSQL; downloads live on local disk or any S3-compatible bucket; extraction runs in-process or on a Redis/BullMQ queue with worker replicas. One code path serves a single laptop process and a clustered deployment.

And it is built to actually run somewhere. Playwright suites cover the end-to-end flows, the player, and resilience — the failure paths, not just the happy ones — and the repo ships a real deploy tree: Dockerfile, nginx, Terraform, Ansible, systemd units, and a Varnish cache. It reads like something meant to be operated, not just demoed.

  • extractors.tsThe proxy’s hard part: extracting a playable stream server-side, with fallback backends, so the browser never calls Google.
  • db.tsThe data store behind one interface — SQLite by default, PostgreSQL (db-pg.ts) via a single env var.
  • lib/storage.tsDownloads on local disk by default, or any S3-compatible bucket when STORAGE_URL is set — one abstraction, two backends.
View 4 more implementation artifacts
  • extraction-worker.tsExtraction runs in-process, or on a Redis/BullMQ queue with worker replicas when you scale out.
  • cluster.tsThe single-process-to-clustered switch: the same code path runs on a laptop or across worker replicas.
  • deploy/Infrastructure as code: Terraform, Ansible, systemd units, and a Varnish config — a real deploy path, not a toy.
  • tests/resilience.test.mjsPart of the Playwright coverage: e2e, the player, and resilience — the failure paths get exercised, not just the happy ones.
  • TypeScript and Express with server-rendered EJS that streams. The page shell flushes before data is ready, so navigation feels instant.
  • Every dependency beyond Node is optional and swappable: SQLite by default with PostgreSQL via one env var, local disk or S3 for downloads, in-process work or a Redis/BullMQ extraction queue with worker replicas. The same code runs on a laptop or a cluster.
  • Tested with Playwright suites for e2e flows, the player, and resilience; ships with Dockerfile, docker-compose, nginx config, and Terraform deploy definitions.

Like what you see? I'm available for full-stack and automation work.

Get in touch