← vchk blog

Free Static Sites with GCS + Cloudflare ($0/month)

You can host a fully functional static site for exactly $0/month using Google Cloud Storage (GCS) free tier and Cloudflare's free plan. No servers, no databases, no hidden costs. Just flat files served over HTTPS with global CDN caching.

GCS's free tier gives you 5GB of storage, 1GB of egress per month, and 100K operations per month — more than enough for a personal blog, landing page, or documentation site. Cloudflare adds unlimited DNS management, a global CDN, free SSL certificates, and DDoS protection at no cost. Your only recurring bill is the domain name (~$10/year).

Step 1: Set up the GCS bucket

Create a bucket named after your domain (e.g., your-domain.com). GCS bucket names are global, so yours needs to be unique. Then make objects publicly readable and configure website serving:

gsutil mb -l us-central1 gs://your-domain.com
gsutil iam ch allUsers:objectViewer gs://your-domain.com
gsutil web set -m index.html gs://your-domain.com

That's it. The bucket is now serving index.html as the default page on its storage.googleapis.com endpoint. The allUsers:objectViewer binding makes files publicly readable without authentication.

Step 2: DNS with Cloudflare

Add your domain to Cloudflare and update your registrar's nameservers to the ones Cloudflare provides. Once Cloudflare is managing DNS:

  1. Add a CNAME record from @ (your root domain) to c.storage.googleapis.com.
  2. Toggle the proxy (orange cloud) — this enables Cloudflare's CDN, free SSL termination, and DDoS protection.
  3. Wait for DNS propagation (usually a few minutes).

A CNAME to c.storage.googleapis.com is the standard way to point a custom domain to a GCS bucket. The proxy (orange cloud) gives you a free SSL certificate — Cloudflare handles it automatically.

Step 3: Website structure

No build tools, no bundlers, no frameworks. The src/ directory is the final artifact — everything in it goes straight to production:

src/
├── index.html
├── style.css
└── posts/
    └── hello-world.html

Edit a file, push, deployed. That's the entire workflow.

Step 4: GitHub Actions for deploy

Automate deploys with a simple GitHub Actions workflow. Create .github/workflows/deploy.yaml in your repository:

name: deploy to cloud storage
on:
  push:
    paths: ['src/**']
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - id: auth
        uses: google-github-actions/auth@v1
        with:
          credentials_json: '${{ secrets.GOOGLE_CREDENTIALS }}'
      - id: upload
        uses: google-github-actions/upload-cloud-storage@v1
        with:
          path: src
          destination: your-domain.com
          parent: false

Set the GOOGLE_CREDENTIALS secret in your GitHub repository settings to a GCP service account key with storage.objectAdmin permissions on the bucket. The workflow triggers only when files under src/ change. Since parent: false, the src/ directory itself is stripped during upload — your files land at the bucket root.

Step 5: Cost breakdown

Service Free Tier Real Cost
GCS 5GB storage, 1GB egress $0
Cloudflare DNS, CDN, SSL $0
GitHub Actions 2000 min/month $0
Domain ~$10/year $0.83/mo
Total $0.83/mo

The domain is the only line item that costs anything. You can get a .eu or .dev domain for around $10/year at most registrars. Everything else — storage, bandwidth, DNS, SSL, CI/CD — is free.

When should you upgrade?

The GCS free tier handles low-traffic sites comfortably. If you exceed 1GB of monthly egress (roughly 10-20K page views with overhead), you'll pay ~$0.12/GB after the free tier — still dirt cheap. For most personal blogs and side projects, you'll never hit the limits.

GCS + Cloudflare is the cheapest way to host a static site. No servers, no databases, no maintenance. Just write, push, done.