Replace the single MDX file per API tag (e.g. `admin.mdx`, `titles.mdx`) with individual files for each procedure (e.g. `admin/admin.backups.create.mdx`). Each file renders a single `<APIPage>` with one operation, includes an inline description, and carries its own `_openapi` frontmatter. Update `generate-api-docs.ts` to emit this per-procedure structure and adjust `source.ts` and `next.config.mjs` to resolve the new paths correctly.
Sofa — Self-hosted movie & TV tracker
Sofa is a self-hosted movie and TV tracker for nerds. Track what you've watched, discover what's next, and plug your data into your existing home media stack.
What it does
- Track episode-level progress for TV series and pick shows back up from a dedicated "Continue Watching" view
- Mark movies as watched to discover more like them
- Rate titles, browse cast and crew, and get recommendations based on what you are already tracking
- Search TMDB and explore trending movies and shows without leaving your own instance
- Show streaming availability from TMDB's US provider data
- Automatically log completed watches from Plex, Jellyfin, or Emby webhooks
- Expose your watchlist as import lists for Sonarr and Radarr
- Runs on SQLite with local image caching, built-in backups, and no external database requirement
- Supports local accounts or OIDC SSO for private instances
Note
Sofa is extremely US-centric right now, in terms of streaming providers, content rating systems, etc. Contributions to address this are more than welcome!
Quick start
A minimal docker-compose.yml is provided in this repo. For most setups, the shortest path is:
- Copy the example environment file:
cp .env.example .env
- Fill in the required values:
TMDB_API_READ_ACCESS_TOKEN=your_tmdb_read_access_token
BETTER_AUTH_SECRET=generate_a_long_random_secret
BETTER_AUTH_URL=http://localhost:3000
- Start the container:
docker compose up -d
- Open
http://localhost:3000and create the first account. The first account becomes the admin automatically, and registration closes after that by default.
The included Compose file uses
ghcr.io/jakejarvis/sofa:edge. If you prefer to pin releases, switch to a published version tag likeghcr.io/jakejarvis/sofa:<version>or use the matchingjakejarvis/sofa:<version>image on Docker Hub. Multi-arch images are published forlinux/amd64andlinux/arm64.
Required setup
TMDB
Sofa uses TMDB for metadata, posters, cast, recommendations, and streaming availability. You need a TMDB API Read Access Token before the app can do anything useful.
Create one here:
Tip
We want the API Read Access Token, not the shorter API key.
Auth secret
BETTER_AUTH_SECRET should be a long random string. To generate one:
npx @better-auth/cli@latest secret
# or
openssl rand -base64 32
Public URL
Set BETTER_AUTH_URL to the real external URL of your instance. This especially matters for login flows and OIDC callbacks behind a reverse proxy (like nginx or Traefik).
Configuration
| Variable | Required | Notes |
|---|---|---|
TMDB_API_READ_ACCESS_TOKEN |
Yes | TMDB metadata and discovery |
BETTER_AUTH_SECRET |
Yes | Session and auth secret |
BETTER_AUTH_URL |
Yes | Public base URL of the app |
DATA_DIR |
No | Root data directory. Defaults to /data in the container |
IMAGE_CACHE_ENABLED |
No | Defaults to enabled. Set to false to use TMDB images directly instead of caching them locally |
LOG_LEVEL |
No | error, warn, info, or debug |
OIDC_CLIENT_ID |
No | Enable OIDC when set with the matching secret and issuer |
OIDC_CLIENT_SECRET |
No | OIDC client secret |
OIDC_ISSUER_URL |
No | OIDC issuer URL |
OIDC_PROVIDER_NAME |
No | Login button label. Defaults to SSO |
OIDC_AUTO_REGISTER |
No | Defaults to true |
DISABLE_PASSWORD_LOGIN |
No | Set to true to hide email/password login when OIDC is configured |
See .env.example for the full list.
Integrations
Sofa ships with two kinds of integrations (for now): incoming watch activity and outgoing import lists.
Incoming watch activity
- Plex: logs completed watches through a webhook URL generated in Sofa. Requires an active Plex Pass license.
- Jellyfin: works through the Jellyfin Webhook plugin.
- Emby: logs completed watches through webhooks. Requires Emby Server 4.7.9+ and an Emby Premiere subscription.
These integrations are user-specific, so each user can connect their own media server account and watch history.
Outgoing import lists
- Sonarr: expose your Sofa TV watchlist as a custom import list
- Radarr: expose your Sofa movie watchlist as a custom import list
Development
For local development:
bun install
cp .env.example .env
bun run dev
Useful commands:
bun run testbun run lintbun run check-typescd packages/db && bun run db:generatecd packages/db && bun run db:migrate
TMDB notice
This product uses the TMDB API but is not endorsed or certified by TMDB.
