nemo.fm

in progress

A listening room platform for music that refuses to become content.

next.jstypescriptpostgresqlprismacloudflareterraformgcpmusicplatform
Website (testing)Feel free to request an invite — I'll accept you 😊

The problem

Modern streaming platforms have optimized for consumption, not connection. Music is fed by algorithms, stripped of context, compressed to mediocrity, and treated as content to scroll past.

Artists don't control their presentation. Listeners can't seek — they can only receive. Links break. Catalogs disappear. The relationship between music and the person who made it is severed by design.

Vision

nemo.fm is a place for artists to share their music in their own way — on their own terms, in their own space, without an algorithm deciding who hears it or when.

I'm building two things in parallel: a fully scalable, self-hosted cloud infrastructure version with automatic artist onboarding, and a lightweight single-tenant open-source repo that anyone can spin up themselves. The hosted version is for artists who just want a room. The self-hosted version is for developers and artists who want full control. Both are part of the same belief — this kind of space should be accessible to everyone, not just people who can afford a team.

The goal isn't to replace Spotify. It's to offer something Spotify cannot: a home. A stable URL. A listening room that belongs to an artist and won't disappear when the algorithm stops caring.

I'm building this because I make music and I want somewhere to put it that feels right. The testing site is live — if you make music and want a room, request an invite.

What nemo is

A multi-tenant listening room platform. Each artist gets their own subdomain (artist.nemo.fm), four themes, and full control over how their music is presented.

Music can be organized many different ways — by era, album, collection, timeline, and more. The goal is to give artists the flexibility to present their catalog however makes sense for their work, not force them into a single structure.

There's a timeline view that shows your entire catalog in chronological order, derived from file metadata tags or manually set. It's designed to be a historical artifact — a record of your creative output over time, not just a list of releases.

Version control is a first-class concept. Songs have multiple versions and always have. Artists re-release albums, revisit old tracks, put out deluxe editions. Most platforms weren't built around that — nemo is. Every version is a legitimate state of the work.

  • Multi-tenant: each artist owns their subdomain and presentation
  • Four themes: Void (cosmic dark), Light (clean), Archive (vintage), Moss (forest)
  • Lossless upload (FLAC, WAV, AIFF), transcoded to MP3 V0 for streaming
  • Multiple organization modes: albums, eras, collections, and timeline
  • Timeline view: full catalog in chronological order, from file metadata or manual input
  • Version control: multiple versions of any track or release, all preserved
  • Track states: finished, evolving, sketch, abandoned, returned
  • Open by default — optional gate or password protection per artist, fully removable; private analytics

Infrastructure

Everything is Terraform. Cloud Run (Next.js), Cloud SQL (PostgreSQL), Cloud Storage (audio), Artifact Registry, Secret Manager, Cloud Build CI/CD triggers — all defined as code, all reusable across environments.

I spent about two hours writing the Terraform modules that describe the full infrastructure — all defined as code and reusable across environments. Once those existed, spinning up a new environment takes 15–20 minutes, and most of that is Cloud SQL initialization time.

Why Cloud Run

Cloud Run is my default infrastructure choice for most applications right now. The model is simple: build a Docker image, push it, done. Cloud Build defines the pipeline in a cloudbuild.yaml, and Cloud Run manages everything else — scaling from zero to whatever traffic demands, with no servers to maintain, no Kubernetes to configure, no swarm to reason about.

For something in active development, that's the right tradeoff. You're not solving hyperscale problems yet. You're trying to ship, iterate, and learn. Cloud Run gets out of the way and lets you do that.

The database is the only place where scale becomes a real architectural question — Cloud SQL doesn't scale horizontally the way Cloud Run does. But if nemo reaches the point where Cloud SQL is the bottleneck, that's a genuinely great problem to have, and one I'd be happy to solve.

Subdomain routing

Every artist on production gets their own subdomain — artist.nemo.fm. Making that automatic was the interesting problem. A wildcard DNS record (*.nemo.fm) in Cloudflare with the proxy enabled means any subdomain routes to the app automatically, with SSL handled by Cloudflare. No DNS record per artist, no certificate provisioning, no infra change when a new artist joins.

A Next.js proxy middleware reads the host header on every request, extracts the artist slug, and internally rewrites to the correct route — the app never needs to know about DNS.

Staging is intentionally different: instead of managing DNS records for test artists, staging is accessed via the Cloud Run URL directly, using path-based routing with a ?artist=slug query param. The same codebase handles both — if the hostname ends in .run.app, it reads the query param; if it's a custom domain, it reads the subdomain. This lets you test the full artist experience on staging without touching DNS at all, while production gets real subdomains automatically.