Building a side project that can handle real traffic used to mean picking a cheap VPS and hoping it would survive a spike. Today the modern web stack lets you assemble a production‑grade architecture from free‑tier services, and the cost is literally zero until you outgrow the limits.
The Big Picture
A typical modern stack consists of three layers:
- Static Frontend – a React/Next.js or SvelteKit site compiled to static assets and served from a CDN.
- Edge Functions – lightweight JavaScript/TypeScript functions that run at the edge, perfect for request‑time transformations, auth, or cheap API endpoints.
- Serverless Backend – managed databases, authentication providers, and background workers that spin up on demand.
When each layer lives on a free tier, you get:
- Near‑zero operational overhead (no servers to patch).
- Automatic scaling – the provider adds capacity behind the scenes.
- Predictable cost: you stay within the generous free quotas.
The challenge is stitching these pieces together without creating a tangled mess of providers. Below is a practical, step‑by‑step guide that shows how to do exactly that.
1. Choose a Free‑Tier Static Frontend Host
Three services dominate the free static‑site market, and all of them give you a global CDN out of the box:
- Vercel – 100 GB bandwidth/month, unlimited serverless function invocations (up to 125 ms per execution).
- Netlify – 100 GB bandwidth/month, 125 k function invocations per month on the free plan.
- Cloudflare Pages – 500 k builds/month, 100 GB bandwidth, and built‑in support for Cloudflare Workers.
Pick one based on your preferred CI workflow. For this guide we’ll use Vercel because its integration with Next.js makes edge‑function deployment seamless.
2. Edge Functions for Dynamic Logic
Edge functions run on the CDN’s edge nodes, giving you sub‑second latency and free‑tier request limits that are usually generous enough for side projects. Use them for:
- Request‑time authentication checks.
- A/B testing or feature flags.
- Simple CRUD operations that hit a serverless backend.
Example: A Tiny Auth Guard
Create a file pages/api/_middleware.js (Next.js 13+ uses middleware.ts). The function runs on every request that matches the folder.
import { NextResponse } from 'next/server'
export function middleware(request) {
const token = request.cookies.get('auth')?.value
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
// In a real app you would verify the JWT here, but keep it cheap.
return NextResponse.next()
}
Deploying this to Vercel automatically places the middleware at the edge. No server, no cost beyond the free request quota.
3. Serverless Backend Options
You need persistent storage and possibly background processing. The following services offer a usable free tier:
- Supabase – Postgres with 500 MB storage, 2 GB bandwidth, and auto‑generated APIs.
- Firebase – Firestore (1 GB stored data, 50 k reads/day) and Functions (125 k invocations/month).
- AWS Free Tier – Lambda (1 M requests/month), DynamoDB (25 GB storage), and API Gateway (1 M calls).
Using Supabase as a Primary Database
Supabase gives you a Postgres instance with a RESTful API and real‑time subscriptions. Here’s how to connect from a Next.js edge function:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'https://YOUR-PROJECT.supabase.co',
'public-anon-key'
)
export default async function handler(req, res) {
const { data, error } = await supabase
.from('todos')
.select('*')
.order('created_at', { ascending: false })
if (error) return res.status(500).json({ error: error.message })
return res.status(200).json(data)
}
Deploy this as a Vercel edge function (/api/todos) and you have a zero‑maintenance API that scales automatically.
4. Authentication Without a Server
Managing auth is often the most complex part of a side project. Two free‑tier services make it painless:
- Auth0 – 7,000 active users, unlimited logins, social connections.
- Supabase Auth – Built‑in JWT auth, email/password, OAuth providers.
Quick Auth0 Integration
Add the Auth0 SDK to your frontend:
import createAuth0Client from '@auth0/auth0-spa-js'
let auth0 = null
export async function initAuth() {
auth0 = await createAuth0Client({
domain: 'YOUR_DOMAIN',
client_id: 'YOUR_CLIENT_ID',
redirect_uri: window.location.origin,
})
}
export async function login() {
await auth0.loginWithRedirect()
}
export async function logout() {
await auth0.logout({ returnTo: window.location.origin })
}
The SDK stores a JWT in a cookie, which your edge‑function middleware can read and verify using the Auth0 JWKS endpoint. This pattern avoids any custom server code.
5. Background Jobs & Cron‑like Tasks
Free tiers also give you ways to run scheduled jobs:
- GitHub Actions – 2,000 free minutes per month, great for nightly builds or data syncs.
- Cloudflare Workers Cron Triggers – up to 1 M triggers/month on the free plan.
- Supabase Edge Functions – can be invoked via HTTP; combine with a free GitHub Action that calls the endpoint on a schedule.
Example: Daily Cache Warm‑up with Cloudflare Workers
addEventListener('scheduled', event => {
event.waitUntil(warmCache())
})
async function warmCache() {
const urls = [
'https://example.com/api/todos',
'https://example.com/api/stats',
]
await Promise.all(urls.map(url => fetch(url)))
}
Deploy this script on Cloudflare Workers and set a cron trigger (0 0 *) via the dashboard. The free tier covers 1 M triggers, which is more than enough for most hobby projects.
6. CI/CD – Keep Deploys Free and Fast
All three static hosts (Vercel, Netlify, Cloudflare Pages) integrate directly with GitHub, GitLab, or Bitbucket. The workflow looks like:
- Push to
main→ provider builds the site. - Build logs appear in the provider’s dashboard (free on all three).
- Optional: a GitHub Action that runs linting, unit tests, and a security audit before the push.
Minimal GitHub Action for Lint + Test
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: '20'
- run: npm ci
- run: npm run lint
- run: npm test
The free tier gives you 2,000 minutes per month, which is plenty for a small repo.
7. Monitoring Costs and Usage
Even free tiers have hard limits. Exceeding them can result in throttling or unexpected charges. Adopt a habit of checking usage daily:
- Vercel – Dashboard → Usage → Bandwidth & Function Invocations.
- Supabase – Project → Settings → Usage.
- Cloudflare – Workers → Analytics.
Set up a simple webhook that alerts you via Slack when you cross 80 % of a quota. The code is trivial:
import fetch from 'node-fetch'
export async function checkUsage() {
const resp = await fetch('https://api.vercel.com/v9/usage', {
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
})
const data = await resp.json()
if (data.bandwidth > 0.8 * data.bandwidthLimit) {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
body: JSON.stringify({ text: '⚠️ Vercel bandwidth > 80%' }),
headers: { 'Content-Type': 'application/json' },
})
}
}
Run this as a daily GitHub Action or a Cloudflare Worker to stay ahead of surprise limits.
8. Putting It All Together – A Sample Architecture
- Frontend – Next.js static export on Vercel.
- Edge API – Vercel Edge Functions for auth guard and thin CRUD wrappers.
- Database – Supabase Postgres (free tier) with row‑level security.
- Auth – Auth0 (or Supabase Auth) for JWT issuance.
- Background Jobs – Cloudflare Workers cron to pre‑warm caches.
- CI – GitHub Actions for lint, test, and cost‑monitoring webhook.
The data flow looks like:
1. User visits the static site (served from Vercel’s edge CDN). 2. Browser requests /api/todos; Vercel edge middleware checks the JWT cookie. 3. If valid, the edge function forwards the request to Supabase via its generated REST endpoint. 4. Supabase returns JSON; the edge function relays it to the client. 5. A nightly Cloudflare Worker hits /api/todos to keep the edge cache warm.
All of this lives under the free tier limits for a modest traffic level (a few thousand daily visitors). If you ever need more, the upgrade paths are straightforward: bump Vercel to Pro, increase Supabase storage, or switch to a paid Cloudflare Workers plan.
9. When the Free Tier Is Not Enough
Even the most generous free tiers have ceilings. Keep an eye on these warning signs:
- Bandwidth consistently > 80 GB/month.
- Function execution time spikes beyond the free‑tier limit (e.g., Vercel’s 125 ms).
- Database rows approaching the 500 MB limit.
If you hit a wall, consider these low‑cost upgrades:
- Vercel Pro – $20/month for 1 TB bandwidth and higher function limits.
- Supabase Pro – $25/month for 8 GB storage and increased API rate limits.
- Cloudflare Workers Bundled – $5/month for 10 M requests.
Because the architecture is already modular, swapping a component for a paid tier is usually a matter of changing an environment variable.
Conclusion
The modern web stack no longer forces you to choose between "cheap" and "scalable". By leveraging free‑tier static hosting, edge functions, and serverless backends, you can launch a side project that behaves like a production service while keeping the bill at zero. The key is to pick services with generous free limits, keep each layer thin, and monitor usage proactively. With this recipe in hand, your next hobby app can handle real users without ever needing to spin up a traditional VM.