Ich habe den technischen Stack für ⌘+P: The App Podcast so gebaut, wie ein App-Entwickler eine App bauen würde – nicht wie ein Podcaster, der Spotify for Podcasters notdürftig zusammenflickt. Das passiert, wenn man RSS als Produkt behandelt.
Die ersten Folgen von ⌘+P habe ich bei Podspace in Paris aufgenommen – in einer echten Kabine, mit echten Mikrofonen und einem echten Toningenieur, der nicht so tut, als würde er verstehen, wenn man von AES-Bus und Monitor-Ausgang spricht. Den Schnitt mache ich auf meinem Mac in Final Cut Pro. Seien wir ehrlich: Ein Apple-Entwickler, der seinen Podcast in Premiere schneidet, wäre wie ein italienischer Koch, der Carbonara mit Sahne macht. Man merkt es, und die Leute urteilen (zu Recht).
Was ich unterschätzt hatte: Aufnehmen und Schneiden machen ungefähr 20 % der Arbeit aus. Der Rest ist Distribution. Und Podcast-Distribution ist 2026 ein deutlich verminteres Terrain, als es zunächst wirkt.
Dieser Artikel ist die technische Autopsie des Stacks, den ich für ⌘+P gebaut habe: drei verschiedene RSS-Feeds, ein Headless-CMS, ein eigenes CDN, ein adaptiver Video-Encoder und eine bewusste Besessenheit von Apple Podcasts. Wenn du einen ernsthaften Podcast starten willst, ohne Acast oder Spotify for Podcasters zu nutzen, ist das wahrscheinlich der ausführlichste französischsprachige Text, den du dieses Jahr dazu findest.
Die Wette: RSS als Produkt behandeln
Die Frage, die mir sechs Monate Umweg erspart hat: Soll mein Podcast auf der Plattform eines anderen leben oder bei mir?
Spotify for Podcasters (früher Anchor) bietet scheinbar einen unschlagbaren Deal: Du lädst deine .wav-Datei hoch, Spotify erzeugt den RSS-Feed, hostet das Audio und verteilt es an Apple Podcasts. Kostenlos. Die Falle liegt natürlich darin, dass der Feed Spotify gehört. Wenn du später den Hosting-Anbieter wechseln willst, kannst du dein Archiv mitnehmen, verlierst aber deine Statistiken, kanonischen Links und manchmal deine Glaubwürdigkeit bei Verzeichnissen, die dich über den ursprünglichen Anchor-Feed indexiert haben. Acast folgt derselben Logik, nur mit professionellerer UX und monatlicher Rechnung.
Ich habe mich für das Gegenteil entschieden. Der RSS-Feed ist ein Endpoint, den ich kontrolliere, auf meiner Domain, von meinem Code aus meiner einzigen verlässlichen Datenquelle erzeugt. Apple, Spotify, Pocket Casts, Castro, Overcast – alle abonnieren meinen Feed. Wenn Spotify morgen unfreundlich wird, ändere ich zwei XML-Zeilen, nicht den Hosting-Anbieter.
Diese Entscheidung zieht einiges nach sich: Wenn ich RSS verwalte, muss ich auch alles andere verwalten. CMS, Speicherung von Audio- und Videodateien, Encoding, CDN, Kapitel, Podcasting-2.0-Metadaten und Varianten pro Plattform. Das ist mehr Arbeit. Es ist auch unendlich lehrreicher, und der daraus entstandene Stack ist erstaunlich schlank.
Die einzige Datenquelle: Hygraph
Die Inhalte liegen in Hygraph, einem Headless-GraphQL-CMS. Mein Modell PodcastEpisode enthält alles, was man erwartet: Staffel, Episodennummer, Gäste, Kapitel, Audio- und Video-URLs, Rich-Text-Show-Notes, Termine sowie die Flags isPrivate und showOnHomepage. Gäste sind eine eigene Entität PodcastGuest mit ihrer Rich-Text-Bio und linkedinUrl. Derselbe Gast kann wiederkommen, und eine Bio in zwei Einträgen zu duplizieren ist eine Sünde, die man sechs Monate später bereut. Das Video liegt in einer wiederverwendbaren Entität Video, auf die PodcastEpisode verweist – dazu später mehr, denn das wurde zentral.
Die Website ist Next.js 16 App Router mit Turbopack und Tailwind v4, deployt auf Clever Cloud mit automatischem Redeploy nach einem Git-Push. Die GraphQL-Schicht nutzt Apollo Client. Die Internationalisierung läuft über next-intl mit sieben Locales (en, fr, de, it, nl, ar-AE, ar-SA) und localePrefix: 'as-needed'. Ein wichtiger Punkt: Der Podcast ist französischsprachig. Selbst wenn jemand /de/podcast aufruft, erzwingt der RSS-Feed auf der Hygraph-Seite locales: ['fr', 'en']. Der Feed ist nicht mehrsprachig, sondern von Grund auf zweisprachig – und Apple Podcasts erwartet das, egal wo man klickt.
Speicherung: Clever Cloud Cellar hinter Cloudflare
Mediendateien (Audio, Video, Cover) liegen auf Clever Cloud Cellar, dem S3-kompatiblen Speicher, hinter einem Cloudflare-CDN unter cdn2.clementsauvage.me. Die Wahl ist nicht selbstverständlich, also hier die Begründung.
Mux ist verlockend, wenn man alles an einem Ort will: Encoding, Streaming, Distribution. Aber bei einem Video-Podcast mit bis zu 250 MB pro Folge werden Mux-Kosten schnell unvernünftig, wenn sich RSS-Downloads vervielfachen. Cellar bietet vorhersehbare Kosten pro Gigabyte, natives Byte-Range (geprüft: Range: bytes=0-1023 liefert HTTP 206) und die App läuft bereits auf Clever Cloud – die Latenz zwischen den Services ist vernachlässigbar. Byte-Range ist unverzichtbar, denn Podcast-Clients laden eine Datei nie vollständig auf einmal herunter. Sie streamen sie schrittweise und erwarten, dass sie fortsetzen können.
Mux nutze ich weiterhin, aber nur für Web-Streaming: adaptives HLS über playbackId, abgespielt mit mux-player-react im Browser auf der Episodenseite. Das eingebettete Video auf der Website bekommt ein professionelles Encoding-Ladder von Mux; die H.264-MP4-Masterdatei auf Cellar dient ausschließlich der RSS-Verteilung. Zwei Anwendungsfälle, zwei Dateien, zwei Strategien. Das kostet mehr Speicherplatz, aber jede Datei erfüllt ihre Aufgabe ohne Kompromisse.
Das Import-Admin: erst der Kontext, dann der Inhalt
Ich habe einen privaten Endpoint – ein Tool, das eine rohe .mp3 aus Final Cut und ein JPEG-Cover entgegennimmt und daraus eine Episode als Entwurf in Hygraph erstellt, die nur noch freigegeben werden muss.
Die UX-Lektion, die mich einen Umbau gekostet hat: Der Kontext muss vor dem Inhalt kommen. In der ersten Version standen die Dateien oben, die Felder Staffel / Episode / Typ / Sprache unten. Für ein Formular logisch. Für die Pipeline katastrophal, denn diese vier Felder bestimmen den Upload-Pfad auf Cellar (cmd-p-podcast/s{n}/episode-{m}/audio.wav). Wenn du die Dateien ablegst, bevor du eine Staffel gewählt hast, weist der Server dich entweder ab oder rät – und dann liegt Episode 12 im Ordner von Episode 11. Ich habe es umgedreht: zuerst ein 2×2-Raster für den Kontext, dann die Drop-Zone. Das Formular lässt Uploads erst zu, wenn alle vier Felder ausgefüllt sind. Nie wieder dieser Fehler.
Der Upload selbst ist signierter S3-Multipart-Upload im Browser: Der Server erstellt signierte URLs für 64-MB-Teile, der Browser sendet die PUTs direkt an Cellar und der Server bekommt am Ende die ETags, um den Multipart-Upload zusammenzusetzen. Ein eigener Endpoint konfiguriert das Bucket-CORS mit ExposeHeaders: ETag. Ohne das kann der Browser die ETags nicht auslesen, und der Abschluss schlägt unbemerkt fehl. Ein Detail, das in keiner allgemeinen S3-Doku steht.
Für Teaser gibt es ein eigenes Benennungsschema: ein siebenstelliges alphanumerisches Suffix aus einem Alphabet mit 55 Zeichen (ohne 0, 1, I, O, i, l, o, damit man die Zeichen nicht verwechselt). Der Schlüssel wird bei jedem Wechsel in den TEASER-Modus neu erzeugt und zwischen den drei Dateien desselben Teasers geteilt (Audio/Video/Cover). So können mehrere Teaser derselben Staffel/Episode nebeneinander bestehen, ohne sich zu überschreiben, und die URL lässt sich am Telefon buchstabieren. Drei Codezeilen, die verhindern, dass man einen Gast anruft, weil sein Teaser von einem anderen überschrieben wurde.
Die vollständige Pipeline nach dem Upload: Mux nimmt das Video entgegen und erstellt ein Asset, eine Playback-ID und ein Thumbnail zu einem gewählten Zeitpunkt. AssemblyAI transkribiert das Audio und erzeugt automatische Kapitel. Im Admin erscheint eine redaktionsfreundliche Vorschau mit Kurzbeschreibung, Show-Notes und Kapiteln (Titel / Start / Ende). Vor dem Schreiben nach Hygraph ist alles bearbeitbar. Nach der Freigabe erstellen wir einen PodcastEpisode-Entwurf mit einer stabilen UUID als episodeGuid, dem referenzierten Mux-Asset, dem AssemblyAI-Transkript und Kapiteln getrennt vom Show-Notes-Block. Die Website-UI und das <podcast:chapters>-JSON liefern sie unabhängig voneinander aus. Sie zusammenzuführen wäre ein Architekturfehler, für den wir bei jedem Update bezahlen würden.
RSS: drei Feeds, drei Strategien
Jetzt wird es interessant. Apple Podcasts hat Priorität. Es ist der Hauptfeed, der einzige, der sofort neue Folgen ausliefert und alle Podcasting-2.0-Funktionen erhält. Spotify und die anderen kommen danach.
Konkret gibt es drei Endpoints:
/podcast/rss– Hauptfeed, neue Folgen sofort, vollständiges Podcasting 2.0/podcast/rss/late– derselbe Inhalt mit drei Tagen Verzögerung/podcast/rss/spotify– nur Audio, drei Tage Verzögerung, strikte Spotify-Kompatibilität
Die drei Tage sind eine bewusste redaktionelle Entscheidung. Apple Podcasts erhält die Folge am Dienstagmorgen. Spotify und die sekundären Verzeichnisse bekommen sie am Freitag. In diesen 72 Stunden hat das Publikum von Apple Podcasts einen exklusiven zeitlichen Vorsprung. So belohne ich die Plattform, die das Podcast-Ökosystem zwanzig Jahre getragen hat, und lenke meine Hörer nebenbei zu dem Client, der am besten versteht, was ein Podcast ist: eine RSS-Datei, keine algorithmische Playlist.
Im Code lautet der Filter !episode.isPrivate && isVisibleInDelayedFeed(...). Die Bedeutung von isPrivate hat mich eine halbe Stunde gekostet: Eine private Episode ist im RSS versteckt, auf der Website aber sichtbar. Genau das Gegenteil dessen, was mein Gehirn zuerst daraus liest. Deshalb steht es als Kommentar im Code und jetzt auch hier, damit ich nicht wieder durcheinanderkomme. So kann ich einen Teaser oder eine nur für Unterstützer gedachte Folge auf der Website zeigen, ohne Apple oder Spotify damit zu füllen.
Was in den Apple-Feed kommt
Der Hauptfeed nutzt Podcasting 2.0 ohne Zurückhaltung. Der Namespace xmlns:podcast steht auf <channel>, und wir geben Folgendes aus:
<podcast:guid>– eine stabile UUID für die Sendung, unabhängig von der Feed-URL<podcast:updateFrequency rrule="FREQ=WEEKLY;INTERVAL=2;BYDAY=TU" dtstart="2026-05-05T05:00:00Z">Un mardi sur deux</podcast:updateFrequency>– der offizielle, maschinenlesbare Rhythmus<podcast:person>für den Host (Clément Sauvage, role="host", href, Cellar-Avatar) und dynamisch für jeden Gast (role="guest", Hygraph-Bild, Name und Firma als Text)<podcast:chapters>mit Verweis auf externes JSON vom Typapplication/json+chapters<podcast:alternateEnclosure>, um Audio und Video im selben<item>anzubieten
Im Apple-iTunes-Namespace kommt alles dazu: itunes:author, itunes:type auf episodic, itunes:image (das obligatorische 3000×3000-Cover – dazu gleich mehr), itunes:category auf Technology, itunes:explicit und itunes:owner mit itunes:name und vor allem itunes:email. Für Items gibt es itunes:episodeType (full oder trailer), itunes:title, itunes:episode, itunes:season, itunes:duration in Sekunden und ein Episodenbild mit Fallback auf das Kanalbild.
Das primäre <enclosure> verweist auf das H.264-MP4-Video, wenn die Folge eines hat; das Audio kommt dann in podcast:alternateEnclosure. Ohne Video wird das Audio zum primären <enclosure>. Der MIME-Typ wird aus der URL über eine Funktion mimeTypeFromUrl abgeleitet, die die Dateiendung im Pfad erkennt und Query-Strings ignoriert. Apple verlangt eine gültige Endung, und signierte Cellar-URLs können Query-Parameter enthalten, die den Validator ohne diesen Schutz scheitern ließen.
Die Cover-Falle: 3000×3000 oder gar nicht
Das Cover habe ich in Figma gestaltet. Es ist mein Standardwerkzeug für fast alle statischen visuellen Assets: saubere Vektoren, einfache Exporte in mehreren Auflösungen und Varianten (Cover der Sendung, Episodencover, Kapitel-Thumbnails), die über Komponenten synchron bleiben. Damit kommen wir zur Falle.
Apple Podcasts verlangt ein quadratisches JPEG mit mindestens 3000×3000 Pixeln. Erster Figma-Export, erster Upload – Apple lehnt ab: 3000×3004. Vier Pixel zu viel in der Höhe. Ein Figma-Frame mit einer nicht ganz geraden Innenabstandszahl sieht man nicht mit bloßem Auge, aber sie reicht, um den Validator abstürzen zu lassen. Mein Feed wurde abgelehnt. Ich habe den Ausschnitt in Figma exakt auf 3000×3000 gesetzt, erneut exportiert und unter cdn2.clementsauvage.me/cmd-p-podcast/cmdp-cover.jpg hochgeladen, mit curl geprüft, dass accept-ranges: bytes zurückkommt, und es noch einmal eingereicht. Diesmal akzeptiert.
Wenn du aus diesem Artikel nur eine Sache mitnimmst: Prüfe die Abmessungen pixelgenau, bevor du etwas einreichst. Apple zeigt keine hilfreiche Fehlermeldung, nur eine Ablehnung, und du darfst suchen. Stelle außerdem deinen Figma-Frame explizit auf 3000×3000 statt auf „ungefähr quadratisch“.
Kapitel als externes JSON
Statt Kapitel direkt in XML einzubetten, empfiehlt der Podcasting-2.0-Standard eine externe JSON-Datei. Mein Endpoint /podcast/{slug}/chapters.json liefert die PodcastIndex-Spezifikation 1.2.0:
{
"version": "1.2.0",
"title": "...",
"podcastName": "⌘+P: The App Podcast",
"author": "Clément Sauvage",
"chapters": [
{
"startTime": 0,
"endTime": 90,
"title": "Intro",
"img": "...",
"url": "..."
}
]
}
Fallback für Kapitelbilder in dieser Reihenfolge: chapter.imageUrl aus Hygraph, sonst ein Mux-Thumbnail bei t=startTime, sonst das Episodencover. Der Content-Type ist ausdrücklich application/json+chapters; charset=utf-8, denn Next liefert standardmäßig application/json, was nicht alle Clients erkennen. Der Cache lautet s-maxage=900, stale-while-revalidate=3600, damit Castro nicht bei jedem Antippen Hygraph erneut abfragt.
Das doppelte <description> und <content:encoded>
Die meisten Hosting-Anbieter begnügen sich mit einem minimalistischen <description>. Ich liefere beide Varianten: ein <description> als Plaintext für universelle Kompatibilität (auch mit alten Clients, die HTML nicht parsen) und ein <content:encoded> mit einer sauberen HTML-Version desselben Inhalts. Beide enthalten Show-Notes, einen Zeitplan mit Zeitstempeln im Format MM:SS oder ab einer Stunde H:MM:SS, eine kurze Host-Bio sowie für jeden Gast eine Bio mit der Überschrift Avec {nom} — {role} chez {company}.
Ein Detail, das mich Zeit gekostet hat: Aus Hygraph als .text ausgelesene Rich-Text-Felder enthielten wörtliche \n-Zeichenfolgen (Backslash plus n, kein echter Zeilenumbruch), weil sie JSON-kodiert gespeichert waren. Eine Funktion unescapeNewlines wandelt sie vor der Ausgabe um. Ohne sie zeigte mein Feed in allen Podcast-Clients sichtbare \n. Das macht Eindruck – den falschen.
Der Spotify-Feed: alles, was Apple mag, weglassen
Spotify ist anspruchsvoll und eigen. Die offizielle Spezifikation verbietet oder ignoriert einige Dinge, die Apple liebt. Mein Feed /podcast/rss/spotify ist deshalb eine strenge Variante:
- Kein
podcast:*-Namespace – Spotify weiß damit nichts anzufangen und möchte ihn lieber gar nicht sehen - Kein
podcast:alternateEnclosure– aus demselben Grund - Kein
atom:link– Spotify bevorzugt das so itunes:ownermititunes:email– vom Support verlangt, um den Feed zu verifizieren- Kein redundantes
itunes:titlebei den Items - Nur Audio – Spotify unterstützt Video nicht über RSS, daher enthält
<enclosure>immer MP3 oder WAV - Kapitel im Format Podlove Simple Chapters, nicht Podcasting 2.0:
<psc:chapters version="1.1">
<psc:chapter start="0:00" title="..." href="..." image="..." />
</psc:chapters>
Der Namespace xmlns:psc="http://podlove.org/simple-chapters" wird nur dann hinzugefügt, wenn die Episode Kapitel hat. Ein ungenutzter Namespace lässt manche Validatoren nörgeln.
Das alles steuert der Parameter mediaMode: 'default' | 'audio', der an getPodcastRss(...) übergeben wird. Der Builder bleibt derselbe, die Feeds sind unterschiedliche Projektionen. Kein duplizierter Code und keine Abweichungen zwischen den Varianten.
Video-Encoding für RSS: H.264 und nichts anderes
Ich wollte HEVC verwenden, weil es effizienter ist, mein Mac Studio es hardwarebeschleunigt kodiert und die Dateien 40 % kleiner sind. Fehler. HEVC funktioniert nicht im RSS-Pfad. Pocket Casts, Overcast und Castro dekodieren HEVC in einem Podcast-Kontext nicht. Apple Podcasts akzeptiert es beim direkten Upload in Podcasts Connect, aber nicht über RSS. Das Ergebnis: Eine Datei, die die Hälfte deiner Hörer nicht abspielen kann – ohne klare Fehlermeldung.
Die RSS-Regel lautet: MP4 mit H.264, AAC-Stereo, fertig. Hier ist mein ffmpeg-Rezept:
ffmpeg -i source.mov \
-c:v libx264 -profile:v high -preset slow -crf 23 \
-vf "scale=1280:720" -pix_fmt yuv420p -movflags +faststart \
-c:a aac -b:a 256k -ar 48000 \
out.mp4
Ein paar Entscheidungen, die nicht offensichtlich sind:
- 720p, nicht 1080p oder 4K. Für Gespräche mit wenig Bewegung ist 1280×720 der beste Kompromiss. 4K ist Verschwendung, 1080p macht die Datei größer, ohne sichtbaren Vorteil.
- CRF 23 + preset slow statt fester Bitrate. Das Encoding ist langsamer, aber die Rate-Control besser und statische Szenen werden automatisch kleiner.
- AAC mit 256 kbit/s in Stereo – das ist Apples Empfehlung; bei Sprache hört man den Unterschied zu 128 kbit/s.
-movflags +faststartverschiebt das moov-Atom an den Dateianfang. Ohne die Option muss der Podcast-Client die komplette Datei laden, bevor die Wiedergabe starten kann. Damit funktioniert progressives Streaming ab den ersten Kilobytes.-pix_fmt yuv420p– ohne dieses Flag kann ffmpeg yuv444p ausgeben, das manche Player ablehnen. Sechs Zeichen ersparen dir Stunden Fehlersuche.
Ein konkretes Ergebnis: Ein Teaser schrumpfte von 120 MB (FCP-Export als H.264 mit 9,8 Mbit/s) auf 26,4 MB – ohne sichtbaren Unterschied. Eine 22-minütige Folge landet bei ungefähr 250 MB, was für einen RSS-Download über Mobilfunk vertretbar ist.
Wenn du Permute für Encoding-Batches verwendest: Deaktiviere immer „Allow copying of HEVC tracks“. Sonst kopiert das Tool den Stream unverändert, und du erhältst eine MP4-Datei mit HEVC – genau das, was du vermeiden wolltest.
Dateigröße im RSS und warum Video.sourceSizeBytes
Ein kleines Detail mit großer Wirkung für Podcast-Clients: Das Attribut length des <enclosure> muss die exakte Dateigröße in Bytes angeben. Apple Podcasts braucht sie, um den Fortschrittsbalken des Downloads zu berechnen. Ist der Wert falsch oder fehlt er, zeigen manche Clients einen kaputten Fortschritt an oder verweigern den Download.
audioSizeBytes gab es bereits auf PodcastEpisode und wurde beim Upload berechnet. Als ich Video zum primären <enclosure> machte, brauchte ich das Gegenstück in der Entität Video: ein neues, optionales Feld Video.sourceSizeBytes. PodcastEpisode hat bereits eine video-Beziehung zu Video, also legte ich auch Video.sourceUrl dort ab, statt die URL auf PodcastEpisode zu duplizieren. Der RSS-Generator verwendet video.sourceUrl und video.sourceSizeBytes, wenn eine Episode Video hat, ansonsten die Audio-URL und audioSizeBytes.
Nach jeder Hygraph-Migration generiere ich die GraphQL-Typen neu (src/gql/graphql.ts und src/gql/gql.ts), und der RSS-Code behandelt beide Fälle sauber. Das sind fünf zusätzliche Zeilen im Builder – genau die Art von fünf Zeilen, die den Unterschied machen zwischen „läuft bei mir“ und „läuft in Castro über 4G“.
KI-gestützte Produktion: Video-Intro, Trailer, RSS
An drei Stellen habe ich generative Modelle die Arbeit machen lassen – in dem Wissen, dass genau das den Stack für eine einzelne Person tragfähig macht.
Das Video-Intro der Sendung – die kurze Sequenz vor jeder Folge – begann mit einem einfachen iPhone-Foto, das ich mit dem Videomodell Happy Horse 1.0 animiert habe. Das Ergebnis dauert nur wenige Sekunden, gibt der Sendung aber eine eigene visuelle Signatur. Mit After Effects hätte ich an einem Wochenende nie ein vergleichbares Motion Design hinbekommen. Die zusätzlichen Kosten sind praktisch null, das Ergebnis passt zur Identität, und niemand fragt „Wer hat dein Intro gemacht?“ – genau darum geht es.
Der Audio-Trailer, der in sozialen Netzwerken läuft und über <itunes:episodeType>trailer</itunes:episodeType> im Feed ausgezeichnet wird, entstand mit Suno. Nicht für die übliche Hintergrundmusik (die komponiere oder lizenziere ich lieber sauber), sondern für einen kurzen musikalischen Teaser im Podcast-Trailer-Format: prägnant, wiedererkennbar, mit geklärten Rechten. Das Haupt-Intro folgt einer anderen, handwerklicheren Logik. Für einen Trailer, der nur wenige Wochen aktuell ist, passt Suno genau.
Und vor allem: Codex. Der RSS-Teil dieses Stacks – besonders das Zusammenspiel der drei Feeds, mediaMode, die unterschiedlichen Kapitel-Varianten von Podcasting 2.0 und Podlove Simple Chapters, der dynamische Namespace je nach Episode und das Entschlüsseln der Zeilenumbrüche – entstand gemeinsam mit Codex im Pair Programming. Nicht generiert und eingefügt, sondern gemeinsam geschrieben: Ich lege den Vertrag fest (was jeder Feed ausgeben muss, was Podbase akzeptiert und was Spotify ablehnt), Codex schlägt die XML-Mechanik vor, und ich prüfe die Sonderfälle, die nur jemand kennt, dessen Feed schon einmal abgelehnt wurde. Den fertigen Code lese ich vollständig und stehe dafür gerade, aber allein hätte ich ihn nicht so schnell geschrieben. Das Gegenteil zu behaupten wäre unehrlich. Und ich verliere langsam die Geduld mit Entwicklern, die auch 2026 noch behaupten, KI würde sie bremsen.
Auch deshalb passt der Stack in drei Wochenenden und kostet 12 € im Monat. Es geht nicht darum, intelligenter als früher zu sein, sondern um Werkzeuge, die Ein-Personen-Handwerk dort möglich machen, wo vor fünf Jahren ein ganzes Team nötig gewesen wäre.
Validierung und Debugging: Was mich gerettet hat
Drei Werkzeuge empfehle ich ohne Zögern:
Podbase prüft deinen Feed gegen die Spezifikationen von Apple, Spotify und Podcasting 2.0. Am Anfang meldete es den fehlenden podcast:-Namespace (den ich vergessen hatte), einen falschen positiven Befund zu angeblich „nicht unterstütztem“ Byte-Range (der Validator testete einen leeren Feed, während alle Folgen isPrivate waren) und ebenso fälschlich „0 % Enclosures mit gültiger Dateiendung“. Die Lehre: Prüfe mit mindestens einer öffentlichen Episode, sonst suchst du Fehler, die gar nicht existieren.
curl + ffprobe + MediaInfo zur Prüfung der Enclosures: HTTP 200, Content-Type, Accept-Ranges, Video-Codecs (H.264 statt HEVC) und genaue Abmessungen (1280×720 statt irgendetwas). Ein curl -I auf die Enclosure-URL verrät in drei Sekunden, ob Cellar korrekt antwortet. ffprobe -v error -show_entries stream=codec_name,width,height zeigt in zwei Sekunden, ob die Datei passt.
Clever-Cloud-Logs + lokales bun build, um Runtime-Abstürze nachzustellen. Zwei Beispiele, die mich jeweils einen Abend gekostet haben:
- Ein
DYNAMIC_SERVER_USAGE-Fehler von Apollo führte während des Renderns einer SSG-Route zu einem nicht gecachten Fetch. Die Lösung:Promise.allSettledfür die Hygraph-Abfrage in der OG-Image-Route, damit ein Hygraph-5xx die Erzeugung der Grafik nicht abstürzen lässt. - Ein Satori-Absturz mit
Cannot read properties of undefined (reading '256')beim Rendern des dynamischen OG-Bildes mit der variablen Schrift Rethink Sans. Satori unterstützt variable Fonts nicht. Die Lektion fürs Langzeitgedächtnis: Keine variablen Fonts innext/og. Ich ersetzte das als Text gerenderte ⌘ durch ein Inline-SVG, und der Absturz war weg.
Grundlegende Entscheidungen im Rückblick
Wenn ich die Philosophie auf fünf Punkte reduzieren müsste:
SSG überall außer beim Player. Die Seite /podcast und jede /podcast/[slug]-Seite werden über generateStaticParams statisch erzeugt. Der Mux-Videoplayer und das <audio>-Element sind die einzigen Client-Komponenten, die Inhalte auf Abruf streamen. Bestmögliche SEO, im CDN cachebare Seiten und eine Time-to-First-Byte unter 100 ms.
Hygraph als einzige Quelle. Keine separate Datenbank, keine Schema-Migrationen, kein eigenes Admin-Panel, das gewartet werden muss. Das Import-Admin speichert Entwürfe, ich gebe jede Episode manuell frei, Hygraph veröffentlicht. Ein einziger kanonischer Zustand.
Cellar als primärer Speicher, Mux als optionaler Luxus. Die H.264-MP4-Masterdatei liegt auf Cellar und versorgt den RSS-Feed. Mux erstellt ein eigenes adaptives HLS-Encoding desselben Videos fürs Web. Falls Mux morgen verschwindet, verliert die Website adaptives Streaming, der Podcast läuft aber ohne Unterbrechung weiter.
Podcasting 2.0 als Grundlage, nicht als Option. Namespace podcast:, Person-Tags, Kapitel-JSON, updateFrequency und alternateEnclosure. Der Aufwand ist gering, wenn wir das XML ohnehin selbst erzeugen. Der Nutzen in Castro, Pocket Casts und Fountain ist enorm.
Apple Podcasts bewusst und konkret zuerst. Drei Tage Vorsprung vor Spotify. Alle Podcasting-2.0-Tags. Ein pixelgenaues Cover mit 3000×3000. Die Plattform, die das Format erfunden hat, verdient echte technische Arbeit und nicht nur einen netten Tweet (Cupertino, das kostet dich ein Ticket für die Dubdub. Küsschen).
Wohin das führt
⌘+P ist kein Marketing-Experiment, um Beratung zu verkaufen, auch wenn ich Berater bin. Es ist das Projekt eines Handwerkers, der wissen wollte, ob er mit Werkzeugen von 2026 statt von 2018 eine moderne, saubere Podcast-Pipeline bauen kann – ohne von jemandem abhängig zu sein. Nach zwei Monaten Tunnelarbeit lautet die Antwort ja, aber nur, wenn man zwei Dinge akzeptiert: RSS ist selbst ein Produkt, keine Datei, die man am Ende erzeugt. Und KI-gestützte Produktion ist keine peinliche Abkürzung, sondern ein wesentlicher Teil dessen, was einen Solo-Betrieb möglich macht: Figma für Visuals, Happy Horse für das Video-Intro aus einem iPhone-Foto, Suno für den Trailer, Codex für die Hälfte des RSS-Codes. Ohne diese Werkzeuge würde ich den Podcast nicht machen. Mit ihnen mache ich ihn gut.
Als Nächstes kommen wahrscheinlich Transkripte über <podcast:transcript> – die SRT-Dateien von AssemblyAI liegen schon vor, das sind drei Zeilen XML –, eine englische Feed-Lokalisierung für ein englischsprachiges Publikum und ein Analytics-Dashboard, das Range-Anfragen an Cellar auswertet, um Hörkurven pro Episode zu rekonstruieren. Kein Spotify Wrapped, kein Chartable, nur meine eigenen Logs.
Wenn du gerade einen Podcast aufbaust und zwischen Acast und DIY schwankst: Der Stack hier hat ungefähr drei Programmier-Wochenenden gekostet und kostet rund 12 € Infrastruktur im Monat (Clever Cloud + Cellar + Hygraph-Free-Tier + Mux-Free-Tier bis zu einem gewissen Umfang). Das ist mehr als null. Es ist auch der Unterschied zwischen deinen Feed zu mieten und ihn zu besitzen.
Die erste Folge von ⌘+P erscheint am Dienstag, dem 5. Mai 2026. Wenn dich Technik, Produktentwicklung, Software-Handwerk oder einfach Menschen interessieren, die gute Arbeit machen, abonniere den Podcast zuerst bei Apple Podcasts – nicht weil es besser ist, sondern weil du ihn dort drei Tage früher bekommst.
Und wenn du ein außergewöhnliches Projekt hast, melde dich. Innovation ist das, wofür ich brenne.
