Self-hosting FeedZero

Last updated: 2026-05-16

What you get

FeedZero is open source under AGPL-3.0-or-later. A self-hosted deployment unlocks every shipped feature. There is no license check, no kill switch, no telemetry. Features still in development stay locked until they ship, and they reach self-hosters the same day they reach hosted users.

What you provide

Self-hosting trades convenience for control. You run the infrastructure. We provide the code and the docs.

Quick start (Vercel, about 10 minutes)

The path of least resistance. The same setup we run for my.feedzero.app, minus the Stripe gates.

  1. Fork forcingfx/feedzero to your GitHub account.
  2. Import the project at vercel.com/new, pointing at your fork.
  3. Set environment variables in the Vercel project (Production environment):
    VITE_SELF_HOSTED=1            # build-time flag: unlocks every shipped feature
    VITE_PAID_TIER_VISIBLE=        # leave unset; hides Subscribe UI
    LAUNCH_PAID_TIER=              # leave unset; disables /api/sync bearer requirement
    SYNC_STORAGE=filesystem        # or "vercel-blob" / "upstash" if you want HA
  4. Deploy. Vercel builds and serves. Open the deployment URL.
  5. Add your domain in the Vercel project settings if you have one. Otherwise the vercel.app subdomain works.

That's it. The first launch auto-subscribes to the release feed so you know when new versions ship. Otherwise it is indistinguishable from my.feedzero.app, except every Personal feature is on by default.

Configuration reference

Build-time flags (set before npm run build:all)

VariableWhat it does
VITE_SELF_HOSTEDSet to 1 to bypass tier gates. Every shipped feature is treated as unlocked regardless of stored license tier. This is the flag that distinguishes "self-hosted" from "hosted dev mode".
VITE_PAID_TIER_VISIBLELeave unset for self-hosted. Setting it would show Subscribe buttons and pricing widgets, which is wrong for a self-hosted deployment where there is nothing to subscribe to.

Runtime flags (set on the server)

VariableWhat it does
LAUNCH_PAID_TIERLeave unset. Setting it would cause /api/sync to reject requests without a valid bearer token, which is pointless for self-hosters who have no license server.
SYNC_STORAGEPicks the sync-vault adapter: filesystem (default, fine for a single instance), vercel-blob (needs BLOB_READ_WRITE_TOKEN), upstash (needs UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN), or memory (dev only; vault evaporates on restart).
GITHUB_FEEDBACK_TOKEN + GITHUB_REPOOptional. Routes the in-app feedback form to GitHub Issues. Without it, the feedback button stays inactive.

Building from source (without Vercel)

If you would rather not use Vercel, FeedZero ships a standalone Hono server that serves the SPA and API routes in one process.

git clone https://github.com/forcingfx/feedzero.git
cd feedzero
echo "VITE_SELF_HOSTED=1" >> .env.production
npm install
npm run build:all
npm run serve              # binds to PORT (default 3000)

Reverse-proxy that with nginx, Caddy, or your container platform of choice. The Hono server is plain Node and runs anywhere Node runs.

Sync on a self-hosted server

Sync is end-to-end encrypted. Your passphrase derives a vault ID and an AES-GCM key on the client. The server only stores ciphertext. The same architecture that keeps hosted users private from us also means you do not need to trust your own server with plaintext. It just needs to hold opaque bytes.

Pick a sync adapter:

All three are tested in CI. Pick based on your platform; the client does not care which one the server uses.

Where the catalog comes from. The Explore tab's 1,300+ feed catalog is a static JSON snapshot bundled at build time (src/data/feed-catalog.generated.json). It works offline and never calls home. To refresh the snapshot, rebuild from a current main; we update the catalog periodically and ship it with each release.
The Stats page is partial in self-hosted mode. The "vault count" widget works. The "popular feeds" leaderboard depends on a centralized catalog backend (Upstash) and shows an empty list unless you wire one. Configure SYNC_STORAGE=upstash with matching env vars to enable it for your fork's users.

Updating

Pull from upstream, rebuild, redeploy. main on the upstream repo is the channel hosted production tracks. Tagged releases are also published. There is no separate self-hosted channel: same code, same release cadence.

git remote add upstream https://github.com/forcingfx/feedzero.git
git fetch upstream
git merge upstream/main
# resolve any conflicts on your fork
npm install
npm run build:all
# redeploy

License and the honor-system gates

FeedZero is open source under AGPL-3.0-or-later. A few features (auto-organize, sync, smart filters, offline starred) are gated client-side by tier; setting VITE_SELF_HOSTED=1 bypasses those gates. This is honor-system gating. A determined user could strip the gates from a fork. We accept that:

Self-hosters who run their own server already provide that value to themselves. It would be hostile to gate the code against them.

See ADR 012 for the full design.

Getting help