跪拜 Guibai
← Back to the summary

A 5-Month Side Project on Free Tiers: $0 Cloud Bill Beyond a Domain

Want to start a side hustle with small products? Do you really need to buy a cloud server? Is it going to be a significant monthly expense?

I've been tinkering with side projects on and off for over five months. All told, I spent zero on cloud services. My only cost was 6 yuan, for one domain. Frontend, backend, database, and image storage all run on Vercel and Cloudflare's free tiers.

First, let me clarify the role of each component I actually used, so you don't mix them up:

Keep the two storage types separate: D1 stores tabular business data; R2 is specifically for binary files like images and attachments.

Quick comparison of the two frontend platforms: For Next.js projects, prefer Vercel — natively adapted, fewer pitfalls. For regular static Vue or H5 pages, Cloudflare Pages is better — no worries about traffic spikes generating huge bills.

Frontend deployment: Cloudflare Pages in practice

Cloudflare Pages hosts frontend code. First, go to the Pages feature. image.png

Screenshot note: This is the entry to Cloudflare's sidebar "Workers & Pages"; click in to create a new Pages frontend project.

Next, link your GitHub repository and choose the repo holding your frontend code. image.png

Screenshot note: After authorizing GitHub, the page lists all your repositories; select the frontend repo to deploy.

After my first deployment, the page threw an error and wouldn't open. image.png

Screenshot note: Deployment build succeeded but visiting the page gives a 404/error — in most cases the build command or output folder settings are wrong.

Find the project settings, modify deployment configuration, adjust build command and output directory. image.png

Screenshot note: Fill in the build command for the framework, and the folder that gets the built resources, e.g. dist.

Save the config and trigger a rebuild. image.png

Screenshot note: Commit code or trigger manually; Pages will rerun the build.

When the build status turns green, deployment succeeded — you can visit the default domain.

image.png

After deployment, you can bind your own subdomain. For example, if the root domain is example.com, the main site www.example.com goes to Vercel, and I create a subdomain vue.example.com assigned to Cloudflare Pages. image.png

Screenshot note: The Pages custom domain config page; fill in the subdomain already hosted on Cloudflare, and the DNS records generate automatically. Once configured, you can access the page with your own domain.

Backend: Workers + D1, a lot of real pitfalls

This backend section had the most pitfalls. I used better-sqlite3 for SQLite locally — everything worked fine locally, but as soon as I pushed the code to a production Worker, the endpoint returned 500. image.png

Screenshot note: The Worker project management UI; here we upload the express backend code and manage bound resources.

The production endpoint returned a 500 error page. image.png

Screenshot note: Browsing the endpoint returns a 500 internal error, but the page doesn't show what the problem is — need to look at logs.

Workers can also bind a custom subdomain; I bound the api subdomain to the Worker service. image.png

Screenshot note: Worker custom domain config; fill in the api subdomain, if the domain is hosted on the current Cloudflare account, binding succeeds.

When production fails, go to the Observability logs page to see event logs — here you see console logs and exception stack traces. image.png

Screenshot note: Events log list; every request is recorded here. Filter for error-level logs to get the error stack trace.

Metrics overview page — statistics on Worker runtime metrics. image.png

Screenshot note: This only counts Worker runtime low-level crashes; business code returning 500 doesn't count into the Errors metric here — don't be misled by 0 Errors.

D1 database monitoring panel shows query count, rows read/written, storage space, number of tables, query latency. image.png

Screenshot note: D1 database overview — if the table count looks wrong, go to the Console tab and run SQL directly to create tables or view data.

Looking at the production log stack trace, I finally understood: better-sqlite3 depends on OS-native binaries, which can't run in the Worker environment. You have to write code to differentiate environments — the production environment only uses the bound D1 instance, and only in local development do you load the local sqlite file.

I also hit a cognitive pitfall in production debugging: the Worker overview page showing Errors=0 does not mean the business endpoints have no errors. That metric only counts low-level runtime crashes. Cases where Express catches an exception and actively returns 500 are business-level errors and don't count into this metric. In that case, the log won't automatically print the error stack — you need to write a global error-handling middleware yourself, manually print the error object, and only then can you see the real error in Observability Events.

My daily habit: when an endpoint fails, check the Events log first, filter error level. Prefer wrangler dev for local debugging — many problems reproduce locally, far easier than reading production logs.

Supporting storage

R2 object storage

Compare it to Alibaba Cloud OSS; good for images and user uploads, its biggest advantage is that outbound traffic isn't billed. image.png

Screenshot note: R2 bucket list page; create a bucket here, then bind R2 in Worker/Pages for file upload and read.

K2 Streams, Basin

I haven't used either at this stage. K2 is a message queue, Basin is a data lake for massive data analytics, and K2 requires a paid plan. For a small side-project MVP, these are unreachable, so no need to spend energy on them.

Domain config in practice, CNAME alias

CNAME is not a page redirect; it's just a domain alias resolution record. If the cloud icon on the record turns orange, traffic goes through Cloudflare first, then forwards to the backend service; if it's grey, it's only domain resolution — traffic goes directly to the origin server, and CDN/protection don't take effect. One small detail: root domains can't directly use CNAME.

Assume:

Screenshot note: DNS record config screen — add a CNAME record; set the proxy cloud to orange (Proxied). SSL/TLS encryption mode — set to Full(strict), otherwise you get HTTPS redirect errors.

Another critical point: after configuring CNAME in Cloudflare, you also need to add this custom domain in the Vercel project for verification — otherwise the site still won't open.

My usual production debugging process

After hitting enough problems, I've put together my own order:

  1. If the frontend page acts up, open the Vercel Observability panel and check for error spikes in CDN or Serverless functions. image.png

Screenshot note: Vercel project overview page; enter Observability from here.

image.png

Screenshot note: Vercel Observability monitoring charts; look at error curves for CDN and Functions to locate frontend issues.

  1. For a backend endpoint returning 500:
    • Don't be fooled by Errors=0 on the Worker overview page;
    • Switch to Observability's Events page, filter error level, filter POST requests;
    • If you don't see a stack trace, check whether you wrote a global error handler to print the err object;
    • Watch out for whether new version traffic hasn't switched over yet; use an endpoint version number to confirm the code actually running.
  2. If you suspect a database issue, open the D1 console, check the table count, run SQL in the Console to verify tables.

Caveats about free quotas

Vercel and Cloudflare's free plans can support side-project idea validation, but each component has its own free quota limits. D1, R2, and Workers all have call limits — enough for low-traffic MVP projects.

An MVP architecture you can copy directly

A ready reference for friends who want to do side projects:

Total cost: only the domain fee. Suitable for small tools, personal SaaS, quickly validating side-project ideas.

Ending

Many people want to try a personal side project but before they start, they worry about server costs being too high, so they never begin.

From my own experience, during the idea-validation phase, there's no need to buy a cloud server at the start. The Vercel + Cloudflare free stack is enough to get a complete product online.

Of course, if you have more useful features or other products, feel free to share in the comments!

Comments

Top 1 of 2 from juejin.cn, machine-translated. The original thread is authoritative.

striveLei

The hard part isn't deployment; without a good idea, nothing works.

三只萌新

[嘿哈] Yes, with a good idea, I'd have time to chat on Juejin instead of going out to make money [偷笑]