Why I stopped paying SaaS to host my podcast
Nikos Katsikanis - July 9, 2026
I had 257 podcast episodes sitting behind a monthly hosting plan. Once I looked at what the job actually required, I realised I was paying a software subscription to serve a feed and a folder of MP3AcronymMPEG-1 Audio Layer III. I use MP3 to mean the compressed audio file format used for the podcast episodes. files.
I have been publishing The Nikos Show for years. By the time I looked properly at the hosting, the archive had reached 257 episodes and about 12.3 gigabytes of audio.
I was using Simplecast. It did the job, and I do not think $15 a month is an outrageous price for someone who wants its publishing interface, analytics, web players, distribution help, and support. The problem was simpler: I was barely using those things.
For my use, the valuable part had become an RSSAcronymReally Simple Syndication. I use RSS to mean the structured feed that tells podcast apps about a show, its episodes, artwork, and audio files. feed, 257 MP3 files, artwork, and a small website. That is static content. Paying another specialised SaaSAcronymSoftware as a service. I use SaaS to mean software I access as a hosted subscription instead of installing and operating it myself. subscription forever started to feel wasteful.
So I built aws-hosted-podcast: Python for the migration pipeline, Terraform for the infrastructure, Amazon S3AcronymSimple Storage Service. I use S3 to mean Amazon’s object storage service, often used for static assets, uploads, backups, and deployment artifacts. for the files, and CloudFront as the CDNAcronymContent delivery network. I use CDN to mean globally distributed edge infrastructure that caches and serves content closer to users. in front of them.
The podcast host was doing less magic than I assumed
A podcast host feels like a specialised platform because the interface combines several jobs. Underneath, the delivery side is quite plain:
- the feed describes the show and every episode
- each episode points to an audio file
- podcast directories remember the feed address and episode identities
- listeners download the audio over an encrypted web connection
I did not need a database, server process, queue, or function invocation for each download. Adding any of those would have been architecture cosplay. A static bucket and a CDN are enough.
That is my wider test for undifferentiated SaaS: if I am paying mainly for storage and delivery, while ignoring the workflow wrapped around them, I should at least price the boring infrastructure underneath.
The risky part was identity, not uploading files
Copying MP3 files is easy. Moving an established podcast without making 257 old episodes appear again as new ones is the real migration.
Every episode has a GUIDAcronymGlobally unique identifier. I use a GUID as the stable identity of a podcast episode so listening apps do not mistake a moved audio file for a new episode.. I preserved those values exactly. I changed the audio enclosure addresses, but not the episode identities.
<item>
<guid isPermaLink="false">a0a33715-25cb-4435-89dc-864fde80426d</guid>
<enclosure
url="https://show.nikoskatsikanis.com/episodes/001-257-mark-savant.mp3"
type="audio/mpeg"
/>
</item>
The migration order mattered too. I downloaded the source feed, copied and verified the audio and artwork, generated the replacement feed, tested the public addresses, and only then prepared the feed redirect. I kept the old host available during the handover.
I am fairly relaxed about rewriting code. I am not relaxed about breaking subscriber continuity. This was one of those cases where a conservative migration was much more important than clever code.
That also connects to how I think about rollback and explicit infrastructure ownership. A migration is much safer when the old path stays intact until the new path has proved itself.
Why I used Python instead of my usual Node.js
I normally reach for Node.js and JavaScript. For this job, I deliberately used Python and mostly stayed inside its standard library.
The main script reads the Simplecast feed, records a snapshot of the metadata, downloads or streams missing audio, uploads objects with the AWSAcronymAmazon Web Services. I use AWS to mean Amazon’s cloud platform for compute, storage, networking, databases, deployment, and managed infrastructure services. command-line tool, rewrites enclosure addresses, generates the new feed and website, and verifies the bucket contents. A line-based manifest makes the work resumable, which mattered when moving 257 files rather than five test episodes.
with open_url_with_retry(episode["audioUrl"], context) as response:
upload_stream_to_s3(
response=response,
bucket_name=bucket_name,
bucket_key=bucket_key,
content_type="audio/mpeg",
)
Node.js could do all of this perfectly well. Its streams are excellent, and I know the ecosystem better. Python was pleasant here because pathlib, urllib.request, concurrent.futures, metadata handling, and subprocess calls gave me the whole pipeline without installing a framework or a dependency tree.
I would still prefer Node.js for a long-lived web service with shared TypeScript types and application code. For a finite migration tool that is mostly files, web requests, feed parsing, and shelling out to the AWS command-line interface, Python felt direct. It reads like an operations script, which is what it is.
Why I moved the infrastructure from CDK to Terraform
The repo initially used the AWS CDKAcronymCloud Development Kit. I use CDK to mean defining cloud infrastructure in a programming language instead of only using hand-written templates or console changes.. I later converted the infrastructure to Terraform.
I like CDK when I am building an application stack with repeated constructs and useful higher-level abstractions. AWS describes it as a framework that defines infrastructure in a programming language and provisions it through CloudFormation. That is productive when the abstraction is earning its place.
This stack was only a bucket, versioning, encryption, a public delivery policy, CloudFront, a certificate, and a deployment role. Terraform made that small resource graph very obvious. Its normal write, plan, and apply workflow also suited an existing live bucket because I could import the resource before asking Terraform to change anything.
The trade-off is state. CDK delegates deployment state to CloudFormation; Terraform maintains its own state and I have to operate that properly. Terraform is also more verbose than a good CDK construct. I did not switch because Terraform is universally better. I switched because explicit resources and a readable plan were more useful than a programming-language abstraction for this particular stack.
The official descriptions are useful if you want the non-opinionated versions: Terraform calls its model declarative infrastructure as code, while AWS explains how CDK synthesises and deploys through CloudFormation.
What it should cost me
I checked these prices on 9 July 2026. I am using United States dollars because both suppliers quote the relevant plans that way.
| Option | Likely monthly cost | What I get |
|---|---|---|
| Simplecast Basic | $15 monthly, or $13.50 when prepaid annually | Hosting, publishing interface, basic analytics, a show site, players, support, and 20,000 included downloads |
| AWS static hosting | About $0.17 for storage, with a sensible working estimate below $0.50 | Static feed, site, artwork, and audio delivery. I operate the publishing and migration workflow myself. |
Simplecast’s current Basic plan is $15 per month or $13.50 per month prepaid annually.
My audio archive is about 12.3 gigabytes. The CloudFront Free flat-rate plan includes 5 gigabytes of S3 storage and is designed for up to 100 gigabytes of data transfer and one million requests per month. With S3 Standard at $0.023 per gigabyte in AWS’s Northern Virginia region, the remaining 7.3 gigabytes is about $0.17 a month. Requests for a library this size should be fractions of a cent, and the non-exportable public certificate used by CloudFront has no additional charge.
I am rounding the AWS estimate up to “under $0.50” because storage versioning, feed checks, new episodes, and small request charges exist. I also assume this distribution is attached to the Free flat-rate plan and that its storage credit is available to this bucket.
The 100 gigabyte monthly transfer allowance is roughly 2,000 complete downloads at my current average episode size of 49 megabytes. Real podcast downloads include partial transfers and range requests, so that is a planning number, not listener analytics. If the show grows beyond that baseline, I would reassess instead of pretending the free allowance lasts forever. CloudFront Pro is currently $15 a month and includes far more transfer, at which point the cost argument becomes less interesting.
At the present scale, I expect to save roughly $158 to $177 a year, depending on whether I compare against Simplecast’s annual or monthly billing. That is not life-changing money. It is still money I would rather not spend on a layer I am no longer using.
What I gave up
The cheap AWS number is not a free replacement for the whole Simplecast product. I gave up, or now have to replace:
- a friendly browser interface for publishing episodes
- podcast-specific analytics and listener reports
- managed embeddable players
- platform support when a feed behaves strangely
- someone else owning the boring delivery details
My own engineering time is the largest cost by a ridiculous margin. If I only wanted to publish a podcast and never touch infrastructure, self-hosting would be false economy.
But I already work on AWS systems, I wanted exact control over the archive, and I enjoyed building the migration. The ongoing operational surface is tiny: upload an audio file, regenerate one feed file, deploy, and verify it.
My rule after doing this
I am not going to self-host every subscription I pay for. Good SaaS earns its fee by removing work I do not want.
I will question a subscription when three things are true: the data is portable, the runtime is basically static, and I no longer use the product’s differentiating features. Podcast delivery met all three.
The important bit was not squeezing the AWS bill down to a few cents. It was reducing the system to its honest shape.
A feed. Some audio files. A careful migration. Nothing running when nobody is listening.