A curated, open-source directory of cybersecurity podcasts — built so defenders, researchers, CISOs, and operators can quickly discover the shows worth listening to. Live at cybersecpods.com.
50+ shows, 20,000+ episodes indexed, hourly RSS refresh, daily Apple ratings refresh, full search, category filtering, audio + video player, JSON-LD SEO. Static site, hosted on Cloudflare Pages.
I love cybersecurity podcasts. They're how I keep up with a field that moves faster than any blog or newsletter can. So I wanted a good way to find them.
There isn't one. The Apple Podcasts and Spotify apps are fine for shows you already know, but the browse experience is awkward, the categories are too broad, and finding new shows feels like luck. The "best cybersecurity podcasts" listicles you find on Google are mostly years out of date, still recommending shows that quietly stopped publishing in 2021.
I wanted one place that answered: which cybersecurity podcasts are still going, what are they about, and when did they last publish. So I built it.
- Always fresh. Every podcast's RSS feed is re-fetched hourly. Apple ratings refresh daily. If a show goes quiet for 60 days it's flagged inactive, but stays searchable.
- Built for browsing. Filter by topic, search across every episode title, and click straight through to Apple, Spotify, YouTube, or the show's own RSS feed. ⌘K opens search from anywhere.
- Open and community-shaped. The directory is a folder of small JSON files in this public repo. Anyone can add a show or suggest edits. The in-browser form does most of the work, and a bot validates the submission and opens the PR for you.
- No tracking, no accounts. The site is fully static. No cookies, no analytics on you, no login. Audio and video stream directly from each podcast's host. We never proxy or store media.
The nudge to actually build this came from Zack Korman. Zack went looking for cybersecurity podcasts, started listening to them, and then started posting absolutely hilarious reviews on X. Watching that thread grow made me realise how much the community wants this kind of curation, and how hard it is to find. So I started building the directory I wished existed.
I'm Merill Fernando. I work in identity and cloud security, and I host my own podcast, Entra.Chat, about Microsoft Entra and modern identity. Running my own podcast made me want a better way for cybersecurity shows to be discovered — not just mine, but everyone's. CyberSecPods is that place.
Each podcast lives in a tiny JSON file under @data/podcasts/. The build job pulls each show's RSS feed, hydrates the rest of the metadata, and the site rebuilds.
The only required field is applePodcastId — everything else (RSS URL, website, host, image, description) is auto-filled from Apple Podcasts and the RSS feed.
The easiest way is at cybersecpods.com/submit/.
- Paste the Apple Podcasts ID — the form looks the show up live and confirms it found the right one.
- Fill in the details (categories, optional cadence/format, hosts, social links). Categories are picked from a checkbox grid grouped by topic.
- Click Open GitHub issue. You're sent to a pre-filled GitHub Issue with a one-click submit button.
- A bot validates the JSON, opens a PR for review, and links it back on the issue. Maintainers merge.
To suggest edits to an existing podcast, click "Suggest edits" on the podcast page (or visit /submit/?slug=<slug>). The form pre-fills from the current entry.
You need a free GitHub account to submit (the issue is filed under your account so you get notifications when the PR opens / merges). No coding or git knowledge required.
If you'd rather skip the form:
- Fork the repo.
- Create
@data/podcasts/<your-slug>.json. The slug must be kebab-case and becomes the URL:cybersecpods.com/podcasts/<your-slug>/. - Open a PR. The validate-pr workflow checks your submission and posts results as a PR comment within ~30s. Once it's green, a maintainer will merge.
{
"applePodcastId": "1296350360",
"tags": ["threat-intel", "incident-response"]
}That's it — the title, description, image, RSS URL, hosts, and Apple ratings are all derived automatically.
{
"applePodcastId": "1296350360",
"spotifyUrl": "https://open.spotify.com/show/4XPl3uEEL9hvqMkoZrzbx5",
"websiteUrl": "https://darknetdiaries.com/",
"rssUrl": "https://feeds.megaphone.fm/darknetdiaries",
"youtubeUrl": "https://www.youtube.com/@JackRhysider",
"twitterUrl": "https://twitter.com/JackRhysider",
"linkedinUrl": "https://www.linkedin.com/company/darknet-diaries/",
"tags": ["storytelling", "incident-response", "threat-intel"],
"cadence": "biweekly",
"format": "narrative",
"authors": [
{
"name": "Jack Rhysider",
"twitterUrl": "https://twitter.com/JackRhysider",
"linkedinUrl": "https://www.linkedin.com/in/jackrhysider/",
"websiteUrl": "https://jackrhysider.com"
}
],
"submittedBy": "your-github-handle"
}| Field | Required | Description |
|---|---|---|
applePodcastId |
Yes | Apple Podcasts numeric ID (digits only, no id prefix). E.g. 1296350360. |
tags |
Yes | 1–5 canonical category slugs from the 26-category taxonomy. E.g. ["threat-intel","incident-response"]. Anything outside the enum is rejected at build time. |
cadence |
No | Publishing frequency. One of daily, weekly, biweekly, monthly, irregular. |
format |
No | Show format. One of interview, narrative, panel, solo, news-brief. |
spotifyUrl |
No | Full https://open.spotify.com/show/... URL. Used for deep-link only — Spotify ratings are not used. |
websiteUrl |
No | Podcast homepage. Falls back to RSS <link>. |
rssUrl |
No | Override the RSS feed (Apple's feedUrl is used by default). |
youtubeUrl |
No | YouTube channel/show URL. |
twitterUrl |
No | X/Twitter URL for the show (host links go in authors). |
linkedinUrl |
No | LinkedIn URL. |
authors |
No | Array of { name, twitterUrl?, linkedinUrl?, websiteUrl? }. |
submittedBy |
No | Your GitHub handle (for credit). |
- Filename must be kebab-case (
darknet-diaries.json, notDarknetDiaries.jsonordarknet_diaries.json). applePodcastIdmust be 6–12 digits only, and must resolve via the Apple iTunes Lookup API.tagsmust contain 1–5 unique values, each one of the 26 canonical category slugs inlib/categories.ts.cadenceandformatmust each be one of the enum values listed above (or omitted).- Social URLs are host-restricted (Twitter/X, LinkedIn, YouTube, Spotify, Apple).
- Duplicate
applePodcastIdacross files is rejected. - The RSS feed must successfully parse and have a non-empty
<title>.
A podcast is automatically marked inactive if no episode has been published in the last 60 days. Inactive shows are hidden by default but stay listed under their categories.
# Install deps
npm install
# Refresh all podcast metadata + episodes (network calls)
npm run update:podcasts
# Refresh Apple ratings (network calls)
npm run update:ratings
# Validate input files (set SKIP_NETWORK=1 for offline dry-run)
SKIP_NETWORK=1 npm run validate:input
# Dev server
npm run dev
# Production build (refreshes podcasts then exports the static site)
npm run buildOutputs:
@data/podcasts.json— hydrated podcast metadata (gitignored intermediate? actually committed for static export)@data/episodes/<slug>.json— per-podcast episode list@data/ratings.json— Apple ratings cachepublic/podcasts-index.json— slim search index served to the browserout/— static site output
| Concern | Implementation |
|---|---|
| Hosting | Cloudflare Pages (auto-deploys on every push to main via Git integration — no API tokens). |
| Framework | Next.js 14 App Router, output: "export", trailingSlash: true. |
| RSS / Apple lookups | fast-xml-parser + node:fetch, with on-disk cache and 429 backoff (@build/lib/). |
| Search | fuse.js over a slim JSON index served from /podcasts-index.json. |
| Audio + video player | media-chrome custom elements (audio + video, with chapters). |
| Theme | next-themes (light + dark + system). |
| Carousel | embla-carousel-react + embla-carousel-autoplay. |
| Animations | framer-motion. |
| Command palette | cmdk (⌘K). |
| Validation | zod schemas under @build/lib/zod-schemas.ts. |
| SEO | Per-page metadata + JSON-LD (PodcastSeries, PodcastEpisode, WebSite). |
| Workflow | Cron | What it does |
|---|---|---|
.github/workflows/update-episodes.yml |
15 * * * * (hourly) |
Re-runs update:podcasts, commits @data/* if changed → CF Pages auto-deploys. |
.github/workflows/update-ratings.yml |
0 6 * * * (daily) |
Re-fetches Apple ratings, then re-bakes them into podcasts.json. |
.github/workflows/validate-pr.yml |
on PR | Validates changed @data/podcasts/*.json files and posts results as a sticky comment. |
.github/workflows/process-submission.yml |
on issue | Parses submissions filed via the in-browser form, validates, and opens a PR. |
The Pages project is wired to this GitHub repo with the following settings:
- Framework preset: None
- Build command:
npm run build:site - Build output directory:
out - Node version:
20
Note: build:site runs only next build and assumes the @data/* files are already committed (refreshed by the hourly Action). For local npm run build (which both refreshes data AND builds), use npm run build.
@build/ # Build-time TypeScript: RSS parser, Apple lookup, validators
@data/
podcasts/ # ← User-submitted JSON (PR target)
podcasts.json # ← Generated: hydrated metadata for all podcasts
episodes/ # ← Generated: per-podcast episode JSON (gitignored)
ratings.json # ← Generated: Apple ratings cache (gitignored)
app/ # Next.js routes
components/ # React components
lib/ # Runtime helpers (data accessors, SEO, search, utils)
public/ # Static assets, including podcasts-index.json
No tracking, no cookies, no accounts. Audio and video stream directly from each podcast's host — we never proxy or store media.
Built and maintained by Merill Fernando. Contributions welcome — open an issue or PR.