A serverless Jellyfin-compatible video streaming server running on Cloudflare Workers + R2 + D1.
┌──────────────────────────────────────────────────────────────┐
│ Cloudflare Edge │
│ ┌─────────────────┐ ┌──────────┐ ┌─────────────────┐ │
│ │ Worker │───>│ D1 │ │ R2 Bucket │ │
│ │ (Jellyfin API) │ │ (SQLite) │ │ (video files) │ │
│ │ │────────────────────>│ (posters) │ │
│ └────────^─────────┘ └──────────┘ └─────────────────┘ │
│ │ ^ │
└───────────┼───────────────────────────────────────┼──────────┘
│ │
┌───────┴─────────┐ ┌─────────┴─────────┐
│ Jellyfin │ │ CLI Scanner │
│ Clients │ │ (local, bun) │
│ - Swiftfin │ │ reads metadata │
│ - Findroid │ │ fetches TMDB │
│ - Streamyfin │ │ uploads to R2 │
│ - Infuse │ │ writes to D1 │
└─────────────────┘ └───────────────────┘
- Jellyfin API Compatible: Works with Swiftfin, Findroid, Streamyfin, Infuse, and Jellyfin Web
- Serverless: Runs entirely on Cloudflare's edge infrastructure
- Zero Egress Fees: Video streaming via R2 has no egress costs
- Direct Streaming: No transcoding required (clients must support your video format)
- Metadata from TMDB: Automatic fetching of posters, backdrops, plots, ratings
- Resume Position: Track playback progress across devices
- Cloudflare account
- D1 database
- R2 bucket
- TMDB API key (free at https://www.themoviedb.org/settings/api)
- Bun runtime (for CLI)
- ffprobe (for video metadata extraction)
git clone <repository>
cd cf-videoCopy the example config and fill in your credentials:
cp cli/cf-video.toml.example cli/cf-video.toml
# Edit cli/cf-video.toml with your credentialscd cli
bun install
cd ..cd worker
# Install dependencies
npm install
# Setup D1 database (local)
npm run db:migrate
# Deploy worker
npm run deploy
cd ..cd cli
bun run src/index.ts scan:movies /path/to/moviesbun run src/index.ts scan:tv /path/to/tv-showsbun run src/index.ts scan:all /path/to/mediabun run src/index.ts statsbun run src/index.ts user:create --username admin --password secret --adminbun run src/index.ts nuke --confirmMovies/
Movie Title (2020) [1080p].mp4
Movie Title (2020) [1080p]-poster.jpg (optional)
Movie Title (2020) [1080p]-backdrop.jpg (optional)
TV Shows/
Show Name/
Season 01/
Show Name S01E01.mkv
Show Name S01E02.mkv
Season 02/
Show Name S02E01.mkv
poster.jpg
backdrop.jpg
For maximum compatibility without transcoding:
| Setting | Recommendation |
|---|---|
| Video codec | H.264 (AVC) |
| Audio codec | AAC |
| Container | MP4 |
| Resolution | Up to 1080p (or 4K for newer clients) |
For a 100GB library with 2 hours/day streaming:
| Service | Monthly Cost |
|---|---|
| R2 Storage (100GB) | ~$1.50 |
| Workers | Free tier |
| R2 Egress | $0 (always free) |
| D1 | Free tier |
| Total | ~$1.50/month |
| Client | Platform | Status |
|---|---|---|
| Swiftfin | iOS/tvOS | Target |
| Findroid | Android | Not working |
| Streamyfin | iOS/Android | Tested ✓ |
| Infuse | iOS/tvOS/macOS | Target |
| Jellyfin Web | Browser | Target |
cd worker
npm run dev # Start local dev servercd cli
bun run src/index.ts <command>MIT
Contributions welcome! Please read the contributing guidelines first.
- Inspired by the Jellyfin project
- Built on Cloudflare's edge infrastructure
- TMDB for movie and TV show metadata