For a while, my sites were just an old repo with an export of my previous blog, plus an idea: split my "code" self from my "music" self. I finally did it, with an AI agent as my pair programmer, and the whole setup costs me a single bill a year.
What I pay for (and what I don't)
| What | Where | Cost |
|---|---|---|
| Domain registration | A domain registrar | Around $10 a year for a .com |
| DNS and HTTPS certificates | Cloudflare | Free |
| Hosting | Cloudflare Workers, static assets | Free |
hey@ email address | Cloudflare Email Routing | Free |
| Contact forms | Cloudflare Turnstile and a small Worker | Free |
No hosting plan, no server to patch, no monthly invoice. Cloudflare's own docs say requests to static assets are free and unlimited, with no extra cost for storing them.
How it's set up
- Three static sites, built with VitePress: I write Markdown and it builds plain HTML, CSS and JavaScript. Any static site generator works the same way: Astro, Hugo, Eleventy, Jekyll...
- One Cloudflare Worker per site, made of static assets that Cloudflare serves from its network, close to whoever is visiting. The only code that runs is a small contact-form handler, and only when someone sends a message.
- Cloudflare DNS for the domain. Each Worker claims its own hostname (
dinartux.com,code.dinartux.comandmusic.dinartux.com), and Cloudflare creates the DNS record and the HTTPS certificate for it. - Email Routing forwards mail sent to my domain to my personal inbox, and lets the contact forms email me without an address on the page.
Cloudflare Pages used to be the go-to for this, and it still works, but Cloudflare now recommends Workers with static assets for new projects, so that's what I went with.
Do it yourself
You'll need a domain, a free Cloudflare account and Node.js.
Move your domain's DNS to Cloudflare. Add the domain on the Free plan, then switch the nameservers at your registrar to the two Cloudflare gives you.
Build your site into a folder of static files, for example with
npm run buildintodist/.Add a
wrangler.jsoncnext to it. This is almost exactly the one behind this blog:jsonc{ "name": "my-site", "compatibility_date": "2025-07-02", "assets": { "directory": "./dist", "not_found_handling": "404-page" }, "routes": [{ "pattern": "example.com", "custom_domain": true }] }Deploy. Log in once, then deploy:
shnpx wrangler login npx wrangler deployWrangler uploads only the files that changed and attaches your domain, and Cloudflare sets up the DNS record and the certificate.
Redirects (optional). A
_redirectsfile in your build output handles them. Mine keeps the links from my old blog alive:txt/be-understood-not-clever https://code.dinartux.com/blog/be-understood-not-clever 301Email (optional). In Email Routing, create an address like
hey@example.comand point it at your inbox. Cloudflare adds the MX records for you.A contact form (optional). Rather than publishing your address, add a form with Cloudflare Turnstile (a free CAPTCHA alternative) and a small Worker that emails you through Email Routing. Sending to your own verified address is free.
More sites? One Worker per subdomain, each with its own
routesentry. That's how I run three.
Things I ran into
- An old Pages project was holding my domain.
wrangler deploycouldn't takedinartux.comover ("externally managed DNS records", error 100117). Removing the domain and its DNS record from the Pages project fixed it. - Static files are free; code is what's metered. Requests that run Worker code count against the Free plan's 100,000 a day. My contact form's Worker only runs for
/api/*(run_worker_firstinwrangler.jsonc), so it only counts when someone sends a message. - Free-plan limits: 20,000 files per deployment and 25 MiB per file. Plenty for a personal site.
- Clean URLs: with VitePress's
cleanUrls, Workers serves/aboutfromabout.htmland redirects/about.htmlto/about. - Self-host your fonts. Not a cost thing, but bundling them with the site means no third-party requests: faster, and more private for your visitors.
How Claude Code helped
I built all of this with Claude Code, Anthropic's AI coding agent, working right in my repo. I'm an AI enthusiast, and this was a good test of using AI responsibly: it did the heavy lifting, and I kept the decisions.
What it did:
- Read the old repo and my blog export, and proposed the plan: three sites on one domain, each an assets-only Worker.
- Migrated my posts and pages, checking them character by character against the export, and set up 301 redirects from the old URLs. It kept the old feed address too, so subscribers didn't notice a thing.
- Designed the look with me, from the beard logo to an accent color per site, and wrote the Latin American Spanish version of my music site.
- Deployed with Wrangler, found the DNS conflict with the old Pages project, and told me exactly what to remove in the dashboard.
- Checked every deploy against the live sites before calling it done.
What I did: decide, review, and approve every deploy, deletion and commit. Claude Code is a paid tool, so it's not part of the "free" here, but it saved me a lot of time. Fittingly, it helped me draft this post too, and I reviewed it before it went out.
When this isn't the right fit
This works because my sites are static. If you need a database, server-side code, a CMS where several people edit content, or real mailboxes (Email Routing only forwards), you'll want proper hosting. For a personal site, a portfolio or a blog, though, the domain might be the only bill you need.