How to set up a website on GCP
July 30, 2026. I’m sitting in my Bangalore office, staring at a Cloud Run bill that’s $3.47 for a production website handling 50K requests/day. That’s less than a chai-and-samosa lunch. But I’ve also seen teams blow $8K/month on a WordPress site because they spun up a n1-standard-8 “just in case.”
Setting up a website on GCP isn’t hard. Setting it up right – cost-optimized, scalable, maintainable – that’s the hard part. And most tutorials skip the ugly stuff: hidden egress costs, idle VM charges, and the trap of “free tier” that silently expires.
This guide is what I wish I’d read five years ago. It covers everything from picking the compute type to keeping your bill under $10/month – or scaling to millions of users without panicking.
Why GCP? (And when it’s the wrong choice)
Most people compare cloud providers by counting services. That’s dumb. You compare by where you’re starting from.
GCP has three killer advantages for web hosting in 2026:
-
Network egress costs less. AWS charges $0.09/GB out to internet for the first 10TB. GCP charges $0.085/GB if you’re not in an Asia-Pacific region, and they tier down faster. For a media-heavy site, that difference adds up (Google Cloud Pricing vs AWS).
-
Cloud Run. This is the best serverless container platform on any cloud right now. Period. AWS has App Runner (lagging) and Fargate (complex). Cloud Run abstracts away so much that you can go from
docker pushto production in four commands. We benchmarked it against AWS Lambda for HTTP workloads in May 2026 – Cloud Run cold starts are 150ms vs Lambda’s 300ms for Node.js. For Python, Lambda often hits 800ms. That matters. -
The pricing model is honest. GCP’s sustained-use discounts kick in after 25% of a month, and committed-use discounts don’t require upfront payment for 1-year terms. AWS forces you into Reserved Instances to get sane pricing. GCP vs AWS 2026 covers the nuance, but the short version: for variable traffic, GCP wins.
Downside? GCP’s regional availability isn’t as wide as AWS. If you need a data center in South Africa or South Korea, AWS beats GCP. Also, GCP’s console is slower. I use gcloud for almost everything.
The three paths for “how to set up a website on GCP”
You don’t just pick a service. You pick a trade-off between control and cost, between cold-start latency and operational overhead.
Path 1: Firebase Hosting + Cloud Functions – best for JAMstack and MVPs
If your site is static HTML + JavaScript that calls APIs (React, Vue, Astro, SvelteKit), this is your cheapest option. Firebase Hosting is just Cloud CDN + Cloud Storage with a global anycast edge. Free tier: 10GB storage, 360MB data per day. A personal blog or landing page stays free forever.
Pair it with Cloud Functions for any dynamic endpoint. A contact form, a newsletter sign-up, or even a payment gateway webhook. The cold start on HTTP functions can be annoying, but you can pre-warm with a scheduled pinger.
I use this for SIVARO’s marketing site. Total cost: $0.23/month for DNS and SSL.
Path 2: Cloud Run – best for containerized apps (my default)
This is what I recommend for 80% of web applications. You containerize your app (Node, Python, Go, Rust, even PHP with FrankenPHP or RoadRunner), push to Artifact Registry, and deploy with one command:
bash
gcloud run deploy my-website --image us-central1-docker.pkg.dev/my-project/my-repo/my-image:tag --region us-central1 --allow-unauthenticated --memory 256Mi --cpu 1 --min-instances 0 --max-instances 10 --concurrency 80 --timeout 120
That deploys a production-grade HTTPS endpoint with a Google-managed SSL cert, auto-scaling down to zero when no one visits, and a global load balancer in front. The --min-instances 0 means zero idle cost. Set --min-instances 1 if you can’t tolerate any cold start – it costs ~$3-5/month extra.
I’ve seen teams move from EC2 m5.large instances ($70/month each) to Cloud Run and cut costs by 60% while lowering p95 latency because of autoscaling.
Path 3: Compute Engine – only when you need the full OS
Virtual machines. The old way. Use them when you need a specific Linux kernel module (e.g., custom networking or FUSE filesystems) or a legacy PHP app that can’t containerize.
But don’t default to VMs. I’ve lost count of the projects where someone spun up an e2-medium “temporarily” and forgot about it for a year. That’s $17/month * 12 = $200+ for a machine doing nothing. With GCP pricing 2026 cost breakdown showing sustained-use discounts only kicking in after partial month usage, idle VMs are expensive.
If you must use Compute Engine, set up instance schedules to shut down overnight and use preemptible instances for non-production. And never, ever use n1-standard families – they're deprecated pricing-wise. e2 or c3 are your friends.
Step-by-step: setting up a static website on Cloud Storage
This is the fastest path. I’ll walk through it because even if you use Cloud Run later, understanding the Storage + Load Balancer pattern is foundational.
- Create a bucket. Name must match the domain exactly if using Google-managed SSL: e.g.,
www.example.com.
bash
gsutil mb -l US-CENTRAL1 -p my-project gs://www.example.com
- Make it publicly readable. But don’t set
allUsersas storage object viewer – that lets anyone list the bucket. Use a uniform bucket-level policy:
bash
gsutil iam ch allUsers:objectViewer gs://www.example.com
gsutil defacl set public-read gs://www.example.com
- Upload your static files. Use
rsyncfor incremental updates:
bash
gsutil -m rsync -r ./dist gs://www.example.com
-
Point the domain. Go to Cloud DNS (or any DNS provider). Create an A record pointing to the Load Balancer IP, or a CNAME for
wwwfrom Cloud CDN’sc.storage.googleapis.com. -
Add a Google-managed SSL cert. Create a classic external HTTPS load balancer, attach your bucket as a backend, and request a certificate from Google-managed SSL. It takes 15 minutes to provision.
Total setup time: 30 minutes. Cost: a few cents per month for storage and load balancer.
How to set up a dynamic website (Node.js example on Cloud Run)
Let's deploy a real app. I’ll use a simple Express.js server that serves a page and hits Firestore for content.
Step 1: Write the app
javascript
const express = require('express');
const { Firestore } = require('@google-cloud/firestore');
const app = express();
const port = process.env.PORT || 8080;
const db = new Firestore();
app.get('/', async (req, res) => {
const doc = await db.collection('pages').doc('home').get();
const data = doc.data() || { title: 'Default', body: 'No content yet' };
res.send(`
<html><body>
<h1>${data.title}</h1>
<p>${data.body}</p>
</body></html>
`);
});
app.listen(port, () => console.log(`Listening on ${port}`));
Step 2: Dockerize
dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD ["node", "index.js"]
Step 3: Deploy – but with a twist
Don’t manually push. Use Cloud Build to auto-build on git push:
yaml
# cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/my-website', '.']
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/my-website']
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: gcloud
args: ['run', 'deploy', 'my-website', '--image', 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/my-website', '--region', 'us-central1', '--allow-unauthenticated']
Now every git push to your main branch triggers a rebuild and deploy. No SSH, no manual steps.
Database choices for your GCP website
People overthink this. Here’s my rule:
-
Static content or small dynamic datasets (< 1GB)? Use Firestore. It’s serverless, has a generous free tier (1GB stored, 50K reads/day), and integrates natively with Cloud Run via the SDK. No connection pooling, no scaling worries. But querying is limited – no
GROUP BY, no joins. Keep your data schema document-shaped. -
Relational data, occasional queries? Use Cloud SQL (PostgreSQL). Provision the cheapest tier (db-f1-micro, ~$7/month) and attach a Cloud SQL Auth Proxy through a sidecar container. I’ve run a CRM on a db-f1-micro for a year – it handles 20 concurrent users fine. If you need autoscaling, use Cloud SQL’s read replicas (adds cost) or switch to AlloyDB (but that’s overkill for most websites).
-
High throughput, real-time, or serverless fit? Firestore again. No.
-
Analytics? BigQuery. But don’t run operational queries against it – that’s a cost trap. Use it for dashboards only.
One more thing: always set Firestore indexes in firestore.indexes.json before going to production. Unindexed queries get logged as errors and your performance tanks.
GCP Cloud Functions vs AWS Lambda 2026: which one for your site backend?
I tested both in May 2026 with the same Node.js 20 runtime, the same memory (256MB), and the same HTTP trigger. Results:
- Cold start: Cloud Functions (2nd gen) – 180ms average. AWS Lambda – 320ms.
- Warm response: Both around 3-5ms.
- Concurrency limit: Cloud Functions 2nd gen can handle up to 1000 concurrent requests per function instance (if you set
max_instancesappropriately). AWS Lambda scales per invocation, but the 1000 concurrent limit is per account region, not per function – you can hit it easily with a popular API. - Pricing at 10M requests/month: Cloud Functions ~$2.50, AWS Lambda ~$3.80 (including the free tier). But Lambda’s free tier is more generous for the first 1M requests.
Bottom line: for a website backend that handles variable traffic, Cloud Functions 2nd gen is marginally cheaper and faster. But if you’re already deep in the AWS ecosystem, the difference isn’t big enough to warrant a migration. The real advantage of GCP is the integration: Cloud Functions can be triggered directly from Firebase Hosting or Cloud CDN with minimal latency.
For my own projects, I use Cloud Run instead of Cloud Functions for HTTP workloads. The main reason: Cloud Functions 2nd gen still has a 60-second timeout (though they’ve extended it in preview to 9 minutes). Cloud Run gives you 60 minutes. Also, Cloud Run lets you control concurrency, CPU allocation, and startup probes – things Cloud Functions abstracts away. More control = fewer surprises.
How to use GCP for machine learning (on your website)
If your website includes a recommendation engine, image classifier, or anything ML, GCP is a natural fit. The Vertex AI platform integrates with Cloud Run and Cloud Functions via the Prediction API.
I helped a SaaS client add a “similar products” feature to their e-commerce site. They were using Kubernetes on AWS and spending $400/month on a GPU-backed inference server that ran 4 models 24/7. We moved inference to Vertex AI online prediction, using a prebuilt container for PyTorch models. Cost dropped to $65/month because Vertex auto-scales to zero when traffic is low.
You can call Vertex AI from a Cloud Run service:
python
from google.cloud import aiplatform
aiplatform.init(project='my-project', location='us-central1')
endpoint = aiplatform.Endpoint('projects/.../locations/.../endpoints/...')
prediction = endpoint.predict(instances=[{"text": "user query"}])
That returns results in ~200ms. No GPU billing while idle.
For real-time inference, consider deploying to Cloud Run with an attached GPU (NVIDIA L4, $0.50/hour). It’s pricier than Vertex per request, but you get full control over the environment.
Cost optimization: the hidden killers
Everyone talks about compute costs. The real budget-busters are different.
#1: Cloud NAT
If your Cloud Run service makes outbound requests to external APIs (Stripe, OpenAI, whatever), and you’re on a VPC with Private Google Access disabled, you need a Cloud NAT. A single NAT gateway costs $5.76/month in forwarding charges + $0.045/GB processed. For a low-traffic site, that doubles your infra cost before you’ve served a single user.
Fix: Always enable Private Google Access on your subnets. Google APIs (Storage, Firestore, Vertex) can be reached without NAT. For other APIs, use a regional external IP – Cloud Run gives you one per service for free.
#2: Load balancer fees per forwarding rule
An external HTTPS load balancer costs $18/month per forwarding rule (if you have TLS termination) plus $0.008/GB processed. That’s fine for production. But I’ve seen teams create separate load balancers for staging and dev environments. That’s $54/month wasted.
Fix: Use one load balancer and route to different backends via URL maps. Or for dev, skip the load balancer and access Cloud Run directly via its run.app URL (it has a Google-managed cert).
#3: Egress to same-region services
GCP’s hidden fee – egress within the same region but across zones is free. But if your Cloud Run (us-central1) talks to Cloud SQL in us-central1, there’s no egress charge. However, if you use Cloud CDN and your origin is in a different region, you pay inter-region egress. Cloud Computing Cost: AWS vs. Azure vs. GCP Pricing in 2026 shows GCP is slightly cheaper than AWS for cross-region, but it's still not free.
Fix: Keep your data plane in one region. Use Cloud CDN to cache static assets globally instead of replicating data.
#4: Firestore daily backup charges
Firestore daily backups are free for the first 7 days. After that, they charge by retained data. If you have 10GB of Firestore and keep 30 days of backups, that’s $3.00/month extra. Not huge, but it sneaks up.
DNS and SSL: the boring but mandatory bits
Use Cloud DNS ($0.20 per managed zone per month) or point your domain from any provider. For SSL, the Google-managed certificate via the load balancer is the simplest – it auto-renews. But note: you can’t use a Google-managed cert for Cloud Run directly; Cloud Run gives you a *.run.app domain. If you need a custom domain, you’ll need a load balancer.
I’ve used Cloudflare DNS + GCP backend for years. Works fine, but some Cloudflare features like Argo Tunnel conflict with GCP’s direct routing. Keep it simple: either full GCP stack or Cloudflare’s CDN (which also gives you DDoS protection).
Monitoring: you don’t need PagerDuty for a personal site
For a hobby site, set up a Cloud Monitoring uptime check. It’s free for 100 checks per month. For production, I use a single alert policy: if p95 latency > 800ms for 5 minutes, notify. That’s it. Everything else I review weekly in the Cloud Monitoring dashboard.
FAQ
How much does it cost to host a simple website on GCP?
A static site on Cloud Storage + a load balancer costs about $0.50–$1.00/month. A dynamic site on Cloud Run with a Firestore database and zero traffic costs about $3–$5/month. See the Google Cloud Pricing Calculator for exact numbers based on your region.
Can I host a WordPress site on GCP?
Yes, but it’s not optimal. You can run WordPress on Compute Engine (use a prebuilt image from the marketplace – Bitnami’s is good) or use Cloud Run with FrankenPHP. But WordPress’s database queries don’t play well with serverless. For WordPress, DigitalOcean’s managed WordPress is cheaper and simpler. GCP is better for custom apps.
How do I connect a custom domain to Cloud Run?
Create a global external HTTPS load balancer. Configure it to route to a Cloud Run NEG (network endpoint group). Then add your domain as a forwarding rule with a Google-managed SSL certificate. The GCP documentation has a step-by-step for this – takes 10 minutes.
Is GCP cheaper than AWS for hosting a website?
For most small-to-medium websites (under 1TB egress/month), yes. GCP’s sustained-use discounts and simpler pricing make it cheaper by 10–20%. For huge egress volumes, both drop prices; check AWS vs Azure vs GCP Cost Comparison 2026 for detailed breakdowns.
Should I use Cloud Functions or Cloud Run?
For HTTP endpoints, Cloud Run. It gives you more control over concurrency and timeout. Cloud Functions is better for event-driven tasks (file uploads, Pub/Sub messages, Cloud Scheduler triggers).
How do I handle user uploads (e.g., profile pictures)?
Store files in Cloud Storage. Use a signed URL for uploads – your backend generates a short-lived URL that the user can PUT to directly. That way, your server isn’t handling file buffers. Example using Python:
python
from google.cloud import storage
client = storage.Client()
bucket = client.bucket('my-uploads')
blob = bucket.blob(f'users/{user_id}/avatar.jpg')
url = blob.generate_signed_url(expiration=timedelta(minutes=5), method='PUT')
Does GCP have a free tier for websites?
Yes. Firestore: 1GB storage, 50K reads/day. Cloud Functions: 2M invocations/month. Cloud Run: 2M requests/month with 360K vCPU-seconds and 240K GB-seconds. Cloud Storage: 5GB standard storage. That’s enough for a low-traffic blog. The free tier doesn’t expire, but some credits do (e.g., the initial $300 credit lasts 90 days).
My site is getting DDoS attacked. What should I do?
Enable Cloud Armor on your load balancer. The basic tier ($5/month per policy) blocks layer 7 attacks with preconfigured rules. For serious threats, use Cloud Armor Managed Protection Plus ($3,000/month). Also, put Cloud CDN in front – it absorbs traffic at the edge.
Conclusion
Setting up a website on GCP in 2026 is straightforward – but the devil is in the defaults. Default to Cloud Run. Default to zero-idle instances. Default to Firestore if your data fits a document model. And never trust a tutorial from 2022 that says “just use a VM.”
The real lesson? Treat infrastructure like code from day one. Use Terraform or Pulumi to define your resources. Otherwise, the next time someone asks “how to set up a website on GCP” at your company, you’ll be debugging a $400/month bill for a load balancer you forgot to delete.
Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.