Why I Chose a Static Site for a Financial Data Platform (And What I Gave Up)

Astro Static Site AWS CloudFront Architecture

Series: Building a Side Project That Runs Itself

  1. The Origin Story
  2. The Static Site Bet (this post)
  3. Kotlin for a CLI Backend
  4. Multi-Cloud Without the Drama
  5. The Pipeline That Runs While I Game
  6. Three Months In: Retrospective

The first question I usually get when I explain Stockadora’s architecture is some version of “wait, why would you use a static site for that?” It’s a fair question. Financial data platforms run on databases. They have APIs. They serve personalized content based on what you searched for last. They are, by definition, dynamic.

Stockadora is none of those things. It’s a static site with over 5,000 pre-rendered HTML pages, deployed to an S3 bucket, served through CloudFront. And the more I’ve run it, the more I think this was the right call — for a specific set of reasons that I’ll try to be honest about.

The Obvious Alternative

The “normal” way to build something like this would be to reach for a framework like Next.js or a traditional server-side stack with a database. You’d have a Postgres instance holding all the filing summaries, an API that the frontend calls, and a CDN caching responses. That’s a reasonable architecture. It’s what most people would build.

Here’s the problem: that stack has operational overhead. There’s a database to patch, back up, and keep running. There’s a server (or a Lambda function cold-start problem) handling every page request. If traffic spikes, something has to scale. If the database goes down, the site goes down.

I have a full-time job. I needed the site to run itself. A server that’s always on is a server that can always break.

The Key Insight: SEC Data Has Natural Batch Boundaries

The reason a static approach works here is a property of the underlying data: SEC filings don’t change after they’re published. Once a 10-K is filed, it’s filed. Once a Form 4 is submitted, those shares were bought or sold. The data is immutable.

More importantly, the EDGAR index — the master list of what’s been filed — is updated a handful of times per day, and the filings that matter for daily summaries are all published within a predictable window. Nobody needs to see a filing summary update in real time. For a free casual research tool, being up to 24 hours behind is completely fine.

That means I can think about the data pipeline as a batch job rather than a streaming system:

SEC EDGAR
    ↓ (Kotlin CLI crawls and downloads)
S3 (structured JSON files per company/filing)
    ↓ (Astro build reads JSON at build time)
5,000+ pre-rendered HTML pages
    ↓ (deployed to S3 + CloudFront)
Users

Every day, the CLI runs, downloads new filings, generates summaries, writes JSON to S3. Then the Astro build runs, reads all that JSON, and renders a new version of the entire site. CloudFront serves the static files.

There is no runtime query. There is no database. There is no server responding to requests.

Why Astro Specifically

I was already using Astro for this blog, so the tooling was familiar. But there’s a more substantive reason it works well here: Astro’s content collections model maps cleanly onto the structure of SEC filing data.

Filing types are like content types. Each company page is a collection entry. The build system handles routing automatically based on the data structure. Zero runtime JavaScript is shipped by default, which keeps pages fast and cheap to serve.

The other thing I appreciated: Astro doesn’t force you into any particular rendering model. I could start fully static and add server-side rendering to specific routes later if I needed to. I haven’t needed to. But it’s not a dead end.

The Real Cost Comparison

This is where the static site bet pays off most clearly.

Running an always-on server — even a small one — costs something. A t3.small on AWS runs about $15-20/month. Add RDS for Postgres (even the smallest instance), and you’re looking at $30-50/month before you’ve served a single page. Managed platforms like Vercel or Render can be comparable or worse once you’re out of the free tier.

The Stockadora stack:

  • S3: Storing several gigabytes of JSON data and static HTML files. Costs a few dollars a month.
  • CloudFront: Serving the static files globally. Pricing is per-request and per-GB transferred. For a side project with modest traffic, this is minimal.
  • Route 53: DNS. About $0.50/month per hosted zone.

The total infrastructure cost for the static hosting side is under $10/month. To put that in perspective: I pay $29.99/month for Xbox Game Pass Ultimate, $24.99/month for Netflix, and I don’t even want to think about my Steam library-to-hours-played ratio. The entire serving layer for a financial data platform costs less than a third of my gaming subscriptions. That’s not the full cost story — I’ll cover Gemini and the full breakdown in a later post — but for the serving layer, it’s essentially a rounding error on my entertainment budget.

The Trade-offs I Made Knowingly

I want to be honest about what this architecture gives up, because there are real cons.

Data is up to 24 hours stale. If a major 8-K drops at 5pm ET, it won’t appear on Stockadora until the next day’s batch runs. That’s a real limitation. For a professional trader, it’s a dealbreaker. For the casual investor audience I’m targeting, it’s fine. You’re researching companies, not front-running earnings releases.

No personalization. There’s no “my watchlist,” no saved searches, no user accounts. Every page is identical for every visitor. That’s a product limitation I’d eventually want to address, but it requires architecture that I explicitly chose not to build yet.

Build time is non-trivial. Rendering 5,000+ pages every day takes time. A full Astro build over a dataset this size is meaningful — measured in minutes, not seconds. This affects how fast a new filing can get from “published on EDGAR” to “live on the site.” I’ve optimized it somewhat, but it’s a real cost you pay for pre-rendering at this scale.

Search needs a different approach. Without a server, there’s no database to query for full-text search at runtime. The solution: Pagefind, a static search library that runs as a post-build step. It crawls the built HTML, builds a search index, and ships it as static files alongside the site. The search UI loads the index on-demand when a user first types in the search box. No server involved at any point. It only indexes company pages (the content that actually matters for search), keeps the index size manageable, and the whole thing lives in S3 alongside the rest of the site. Honestly, it works better than I expected.

No Database, No Server, No Saturday Pages

The thing I keep coming back to is what this architecture eliminates. There’s no database to go down. There’s no server to patch. There’s no memory leak to investigate at 2am. There’s no autoscaling configuration to get wrong.

The maintenance story for the hosting layer is essentially: S3 and CloudFront just work. I have never had a “site is down” incident caused by the hosting infrastructure. That’s not luck — that’s the direct result of having no servers to fail.

For a solo developer who wants to run something in production without it consuming their evenings, that matters enormously.

Next up: the backend that does all the actual work — a Kotlin CLI that crawls EDGAR, talks to Gemini, and writes JSON files to S3. A personal choice, not a manifesto.