“Just use a static site generator” is advice that sounds clean until you need search, auth, or anything that changes without a deploy.

Server racks and network cables

The hidden costs

Static does not mean zero infrastructure. You still need hosting, CDN configuration, preview environments, and a build pipeline that runs on every content change.

“There is no cloud. It’s just someone else’s computer.” — Unknown

Astro, Eleventy, and Hugo all shift complexity left — they do not eliminate it.

Build pipeline in practice

Even a “simple” blog has a CI graph:

yaml
# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install --frozen-lockfile
      - run: bun run build
      - uses: actions/upload-pages-artifact@v3
        with:
          path: apps/web/dist

That’s four moving parts before a single visitor hits your homepage. The bill moved from runtime to build time — it didn’t disappear.

Where static wins

Content that changes rarely and is read by many:

  1. Blogs and personal sites
    • High read-to-write ratio
    • CDN cache hits are nearly free
  2. Documentation
    • Versioned with git
    • Preview deploys per PR
  3. Marketing pages
    • No auth, no personalization
    • Ship once, serve forever

Where static loses

Personalized dashboards, real-time data, user-generated content. Fighting the model costs more than adopting a server. Know which side of the line you are on before you commit.

typescript
// This does not belong in SSG — it needs a server
export async function GET({ cookies }) {
  const session = cookies.get("session");
  if (!session) return new Response(null, { status: 401 });
  const user = await db.user.find(session.value);
  return Response.json(user.preferences);
}

If you’re writing handlers like that, you’ve already left static territory.