Freshwax is a self-hosted music release tracker for people who want a clean, personal watchlist instead of another streaming-platform inbox.
It keeps a canonical list of artists and releases, lets each user follow artists with their own filters and preferences, syncs release metadata in the background, and exposes a private iCalendar feed for the releases that matter to them.
- Self-hosted and operator-friendly
- Local email/password auth built in
- Works without mandatory third-party platform credentials
- MusicBrainz-backed catalog search and release discovery
- Optional streaming platform links, account connections, and imports where implemented
- Deezer public API fallback and enrichment when mappings are available
- Private per-user release calendar feeds
- Background sync with BullMQ + Redis
- PostgreSQL + Prisma data model designed for long-lived canonical records
Freshwax is built around a simple idea: global release data should stay canonical, while visibility and follow state should stay personal.
Each user gets their own followed artists, recent-release feed, ignore state, notification preferences, timezone-aware release filtering, and tokenized calendar feed. The app stays server-rendered and product-focused rather than turning into a generic admin dashboard.
- Artist search backed by MusicBrainz, with Deezer used as a public fallback/enrichment source
- Per-user follows, recent-release feed, and ignore state
- Recent-first release browsing with a separate upcoming-release planning view
- Private
.icsfeed per user, exposed at/calendar/:token.ics - Optional browser push notifications and signed webhook delivery
- Installable PWA shell with app manifest, home-screen icons, and an offline fallback page
- Optional Last.fm import to seed a watchlist from listening history
- Optional external login and account-link flows for supported providers
- Background sync worker with lazy queue setup so app builds do not connect to Redis during import
- Docker-based runtime with PostgreSQL, Redis, web app, worker, and first-run schema bootstrapping
- Next.js 16 App Router
- TypeScript
- Prisma
- PostgreSQL
- BullMQ
- Redis
- Tailwind CSS v4
- Copy the environment template:
cp .env.example .env- Start the full stack:
docker compose up --buildThat launches:
postgresredisdb-initrunningprisma db push- the Next.js app
- the BullMQ worker
Freshwax works without third-party provider credentials. For a minimal setup, DATABASE_URL, REDIS_URL, and APP_URL are the only required values.
If you want a local demo account:
npm install
npx prisma generate
npx prisma db push
npm run prisma:seedDemo credentials:
demo@example.comdemo12345
Start PostgreSQL and Redis:
docker compose up postgres redisThen run the app and worker locally:
npm install
npx prisma generate
npx prisma db push
npm run dev:allUseful alternatives:
npm run dev
npm run dev:workerDATABASE_URL: PostgreSQL connection stringREDIS_URL: Redis connection string for BullMQAPP_URL: Public base URL used for calendar links, notifications, and OAuth callbacks
For local development, prefer http://127.0.0.1:3000 over localhost. Some OAuth providers reject localhost redirect URIs.
LASTFM_API_KEY: Enables Last.fm username importWEB_PUSH_PUBLIC_KEY,WEB_PUSH_PRIVATE_KEY: Enable browser push notificationsNOTIFICATION_WEBHOOK_URL: Enables instance-wide webhook deliveryNOTIFICATION_WEBHOOK_SECRET: Signs webhook payloads with HMAC-SHA256SYNC_INTERVAL_MINUTES: Full background sync intervalARTIST_SYNC_CONCURRENCY: Worker concurrency for artist sync jobsDEFAULT_TIMEZONE: Default timezone for new users
DEEZER_APP_ID,DEEZER_APP_SECRETTIDAL_CLIENT_ID,TIDAL_CLIENT_SECRETSPOTIFY_CLIENT_ID,SPOTIFY_CLIENT_SECRETGOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRETAMAZON_CLIENT_ID,AMAZON_CLIENT_SECRET
These enable optional login, linking, or import flows. Core release tracking does not depend on them.
- The app uses Next.js standalone output in containers
docker compose upruns schema bootstrapping withprisma db pushbefore app and worker startup- Queue setup is intentionally lazy so
next builddoes not create Redis connections at import time - The public calendar URL shape remains
/calendar/:token.ics, backed by a rewrite to the App Router route - PWA support is served from the app itself:
/manifest.webmanifest, generated app icons, and/push-sw.js. Browser push subscriptions and offline caching intentionally share that service worker URL.
Freshwax separates:
- canonical artists and releases
- provider-specific mappings
- per-user state such as follows, discovery events, ignored releases, calendar tokens, and notification settings
That separation keeps provider data replaceable and lets user-visible behavior stay user-specific instead of mutating shared release records.
For meaningful changes in this repo, the standard checks are:
npm run lint
npm run build
npx prisma validate
npx prisma generateIf schema or setup behavior changes, also run:
npx prisma db push
npm run prisma:seed- Setup and operator details: docs/setup.md
- Architecture notes: docs/architecture.md
- MusicBrainz is the canonical identity and release-discovery source
- Streaming platform integrations are optional and additive; Deezer is not required for core tracking
- Deezer metadata can be incomplete and is used as best-effort public fallback/enrichment when mappings are available
- TIDAL is currently used for outbound links and optional account-linked import paths
- Some provider surfaces appear in preferences before full end-to-end support exists
- Release typing and provider matching rely on best-effort heuristics where source metadata is inconsistent
Freshwax is licensed under the GNU Affero General Public License v3.0 or later. See LICENSE.