There is no skip button · Part 2

How to set up your own AI radio station on a Mac mini

An opinionated, step-by-step guide to running SUB/WAVE on a Mac mini with OrbStack, Ollama and Navidrome, so your own music plays with an AI DJ between the records.

A line drawing of the same large old table radio from Part I, still under construction: a tower crane lowers its ochre dial window into an empty opening while a tiny figure on the ground guides it down.

The first post was about Space & Time, the AI radio station that runs from my house, plays only my music, and does not ask my opinion about anything. I said I was saving the deep technical version for another post. The response pleasantly surprised me, and enough people asked how it works that this one is a tutorial. The next one will cover what I’ve done since to adjust and improve mine.

This is an opinionated guide. It’s how I’d set one up today, on a Mac mini, with the tools I use myself. By the end you’ll have a station running on one computer: your music, a DJ that chooses the next track and talks between them, and a live stream you can open in a browser or app. It won’t sound like Space & Time on day one.

I. Why, and what you’re building

Why run your own? The answer is really the contents of the first post, but a comment on it put it well. Frank Rose wrote: “Your music feed feels personalized, because it is. Also a reminder that ‘algorithmic’ and ‘personalized’ are quite different things.” This is spot on. Spotify and its peers are successfully algorithmic, but feel far less personalized. A station built on your own collection is inherently personal, and you can keep tuning it until it’s more so.

The station has four pieces.

SUB/WAVE is the station itself. It picks the records, writes what the DJ says, speaks it with a voice engine, and mixes everything into one stream. It’s open source, and almost all of it is the work of one developer, Parminder Klair. Version 1.0 came out the day I installed it, and he has kept shipping releases since. I’ve contributed a few fixes along the way. If you end up running it, you can support the project on Ko-fi.

Navidrome is a music server. It reads your music folder and hands tracks to the DJ. SUB/WAVE never touches your files directly.

Ollama runs the language model, the DJ’s brain, on your own machine. Docker runs SUB/WAVE and Navidrome in their own containers, so you don’t have to install them piece by piece. In this setup, the DJ’s thinking and its voice both run on your computer.

Fair warning about the requirements: A radio station never goes off the air, so you need a machine that can stay on and connected around the clock. That’s why a Mac mini is perfect for this: it uses relatively little power and it’s basically silent.

Which mini you choose depends on whether you’ll run the DJ’s model locally or pay for it in the cloud. For a local model the hardware bar is moderately steep, mostly because your computer has to run an open model at a decent speed. A cloud model, through a service like OpenRouter, takes the heaviest load off your computer, but you pay for all the inference. Storage matters too if your music library is big, or you want it to be.

How much time you’ll end up spending on it is highly variable. I spend a fair amount on mine, and what makes that easy is an agent I’ve set up: whenever I have an idea or hear something off, I drop it there immediately and the agent gets working. If you’re daunted by the skills required, I wouldn’t worry too much. An agent like Claude Code or Codex can do most of the technical work.

II. What you need

  • A Mac with Apple Silicon (any M-series chip), running macOS 13 or newer, that can stay on and online. For a local model, get as much memory as you can; mine is an M4 Pro Mini with 64 GB. For a cloud model, a base mini will do.
  • OrbStack, which runs Docker. I prefer it to Docker Desktop, which also works. Either way, let it use at least 4 GB of memory.
  • About 7 GB of free disk space for the station’s software, plus room for your music and the model.
  • Ollama, to run the DJ’s model on your Mac. The SUB/WAVE project suggests models that can use tools, and names gemma4:12b, qwen3.5 and qwen3.6. This guide uses gemma4:12b, its first pick. Plan on 16 GB of memory or more, since the model and Docker share it. Using a cloud model instead? Skip Ollama and step 2, and pick your cloud service in step 9.
  • At least 15 songs to start, as MP3 or FLAC files, in folders. Music you own is fine for a private station at home. Don’t open the station to the public without broadcast licenses; SUB/WAVE’s README explains why.
  • A web browser.

My agent ran these steps on a fresh setup with SUB/WAVE version 1.21.0.

III. The steps

Every command below goes in Terminal (in Applications, then Utilities). Paste it, press Return, and wait until it finishes.

1. Start Docker and check it works.

Open OrbStack (or Docker Desktop) and wait until it says it’s running. Then check it:

docker compose version

You should see v2. and some numbers. If you get an error, Docker isn’t running yet.

2. Install Ollama and download the DJ’s model.

Install Ollama from ollama.com/download (or, if you use Homebrew, brew install ollama), open it once, then download the model:

ollama pull gemma4:12b

It’s several GB, so let it finish; the model’s page lists the exact size. gemma4:12b is SUB/WAVE’s own pick, and it fits a mini with less memory. I run the bigger gemma4:26b, which is also what my agent tested with. If your mini has the memory, download that one instead and pick it in step 9.

3. Make a folder and download the two SUB/WAVE files.

mkdir -p ~/my-station/music && cd ~/my-station
curl -O https://raw.githubusercontent.com/perminder-klair/subwave/v1.21.0/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/perminder-klair/subwave/v1.21.0/.env.example

This makes a folder called my-station with a music folder inside, then downloads two files: one tells Docker what to run, and the other (.env) holds your settings. Both come from version 1.21.0, the one my agent tested.

4. Fill in your settings.

First, make a random password:

openssl rand -hex 16

Open the settings file (open -e .env opens it in TextEdit). Change these three lines and leave the rest alone. SITE_URL is the address you’ll type into your browser to reach the station.

ADMIN_USER=admin
ADMIN_PASS=<paste the password you just made>
SITE_URL=http://localhost:7700

Then add these two lines at the bottom:

SUBWAVE_VERSION=1.21.0
TZ=America/New_York

Put your own time zone in TZ. If it’s wrong, the DJ gets the time of day wrong; mine once announced the wrong hour after an update reset it to UTC. Write the version without a “v” in front. The project’s own settings file shows a “v”, and that doesn’t work.

5. Put some music in the music folder.

Copy 15 or more songs into ~/my-station/music, ideally sorted as Artist/Album/song.mp3. Do it now, before the first start, so Navidrome finds them straight away.

6. Add Navidrome.

Navidrome isn’t in SUB/WAVE’s file, so you add it with a second one. In the same folder, create a file called docker-compose.override.yml containing exactly this:

services:
  navidrome:
    image: ghcr.io/navidrome/navidrome:latest
    container_name: sub-wave-navidrome
    restart: unless-stopped
    ports:
      - "127.0.0.1:4533:4533"
    volumes:
      - ./music:/music:ro
      - navidrome-data:/data

volumes:
  navidrome-data:

Docker picks up a file with this name automatically, so the next step starts Navidrome with everything else. Navidrome can read your music folder but never change it, and the 127.0.0.1 means only this Mac can reach it.

7. Start everything.

docker compose up -d

The first time, this downloads about 7 GB, so give it ten minutes or more. When it’s done, run docker compose ps to see what’s running. After a minute you should see seven parts: broadcast, controller, web, caddy, analyzer, docker-socket-proxy and navidrome. Controller and broadcast should say “healthy”.

8. Create your Navidrome login.

Open http://localhost:4533 in your browser (localhost means “this Mac”). Navidrome asks you to make an account. Pick any username and password; you’ll need them in the next step. Once you’re in, you should see your albums. If not, wait a minute for Navidrome to finish reading your folder, and reload.

9. Run the setup wizard.

Open http://localhost:7700/onboarding and sign in with the ADMIN_USER and ADMIN_PASS from your settings file. The wizard has five screens:

  1. Connect Navidrome. Address: http://navidrome:4533, not localhost. Each part of the station runs in its own sealed box (a container), and inside SUB/WAVE’s box, localhost would mean SUB/WAVE itself. Use the username and password from step 8, and press the test button. It should say it connected.
  2. Pick a language model. Provider: Ollama. Its address, http://host.docker.internal:11434, is already filled in; that’s how the box reaches your Mac. Choose gemma4:12b from the list. The wizard starts on glm-5.1:cloud, an online model that needs an Ollama account, so change it. Press the test button. Using a cloud model instead? Choose its service here (OpenRouter, for example) and paste your API key, the password that service gave you. You can also set a daily budget so the DJ can’t overspend. If it hits the limit, the music keeps playing; the DJ just stops talking.
  3. Choose a voice engine. Keep the default, Piper (fast, local), and leave the heavier and cloud voices off.
  4. DJ persona. Give the station a name, and search for your city so the DJ knows your weather and time zone.
  5. All set? Check the summary and press Save and finish.

10. Listen, and hear the DJ talk.

Open http://localhost:7700 and press play. Music should start within a few seconds.

You don’t have to wait for the DJ’s next break. Open http://localhost:7700/admin/dash, type a short instruction in the box (“Say hello and name the station” works), and press Send to air. The DJ writes a line, reads it out loud, and it airs shortly after. To double-check, this searches the station’s log for what the DJ said:

docker compose logs controller | grep dj-speak

You should see the DJ’s sentence. That’s your station.

11. Keep it on the air.

A station only works if the Mac never sleeps. Run these two commands (sudo will ask for your Mac’s password):

sudo pmset -a sleep 0
sudo pmset -a autorestart 1

The first stops the Mac from sleeping. The second turns it back on by itself after a power cut. (Both are also in System Settings, under Energy.) Then make sure everything restarts with the Mac: in OrbStack’s settings, turn on Start at login, and check that Ollama is listed under System Settings, General, Login Items. The station’s parts restart on their own once OrbStack is running.

IV. If something goes wrong

Check these first.

Step 7 says “not found”, or sits there downloading nothing. Check the version line in your settings file: SUBWAVE_VERSION=1.21.0, not v1.21.0. Also, Navidrome’s usual source, Docker Hub, froze when my agent tested these steps, which is why step 6 gets it from ghcr.io instead.

The model test passed but the DJ stays silent, or the test fails. SUB/WAVE has to reach Ollama at http://host.docker.internal:11434, never localhost. Check that Ollama is running: curl http://localhost:11434/api/tags should list your models. Check that the model name in the wizard matches ollama list exactly. Leave the Reasoning switch (Settings, then LLM provider) off. On my station, a model that “thinks” before answering used up its whole allowance thinking and never wrote the line. And the DJ’s first line after the model loads can take a minute or two.

Music plays but it’s always the same artist. The DJ picks from songs it has labeled by mood, energy and so on, which SUB/WAVE calls tagging. Open http://localhost:7700/admin/library, run the tagging, and give it a few minutes. On my station, tagging never ran in the first two days, and the DJ played only the obvious songs by the biggest names. A tiny library limits variety too: with 16 songs by four artists, my agent’s test kept coming back to one artist.

http://localhost:7700 doesn’t load. Another app may already be using that address’s number, 7700 (its port). Add CADDY_PORT=7710 to the settings file, change SITE_URL to http://localhost:7710, and run docker compose up -d again. Don’t pick 5000 or 7000; macOS’s AirPlay uses them.

The log shows “missing parameter: ‘u’” before you finish the wizard. That’s normal: the station started before it knew about Navidrome. It stops after step 9.

The voice sounds plain. If the log says voice "bm_george" not found — using built-in default, Piper is using its built-in voice. That’s fine for a first station. The DJ will also mispronounce some names; you can teach it the right sound on the Speech tab of the admin’s Moods page.

If you get stuck, the project has a Discord chat and GitHub Discussions.

V. Mine, and what comes next

Mine has grown well past this. It runs all the time, split across a NAS that holds the music and a Mac mini that does the thinking. The DJ runs on gemma4:26b. I ran a series of bakeoffs between the open models that could run well on my hardware, and Gemma came out on top. I’ll keep testing new ones as they appear. It speaks with Kokoro, a different voice engine, and it plays from about 61,000 tracks instead of fifteen. It is still the same four pieces you just set up.

My advice for day one is to let Claude guide you, while you stay clear about what you want and tell it plainly. There are endless rabbit holes here: hardware, open-model benchmarks, networking tools like Tailscale. Skip them until music is playing. Then start playing with the DJ’s persona, its rules, and the shows.

And have your agent write everything down, every decision and how your hardware and software are set up, so it can look back at them later. Mine does that automatically in every project. If you don’t have that kind of setup yet, a project folder where Claude can save Markdown notes is enough.

A personal station has an unforgiving audience. I know the collection, so I notice when something is off. One Sunday, my DJ played 25 reggae tracks in a row. Its first rule is to keep the flow smooth, and the smoothest move from a reggae record is usually another reggae record. My fix was a points system for the model. As I put it at the time: “a smooth transition is 5 points, but a smooth transition into a new genre is 8. Basically trying to create an incentive to maintain a groove but mix up the styles.”

That’s where the next post will pick up: the tweaks, the customizations, the prompting, and what I measure. It’s too early to say whether the DJ has learned to play the point game. My station is running, but I doubt I’ll ever think of it as done.

Michael Lebowitz is the founder of Big Spaceship, acquired by MSQ Partners in 2024. He is a board director, advisor, and tinkerer in Brooklyn and Kinderhook, NY. More about Michael.