Introduction
Google Cloud offers several ways to host web applications — from fully managed services like App Engine and Cloud Run to infrastructure-level tools like Compute Engine. In this post, I’ll walk you through deploying a sample Node.js app (from my GitHub repo) on Compute Engine, using Google Cloud Shell, managed instance groups, load balancers, autoscaling, and Cloud CDN.
This guide uses my own minimal app. It’s a hands-on tutorial designed for developers or DevOps engineers looking to learn Google Cloud from the ground up.
If you're interested in a fully managed, container-based approach, check out my previous post: Deploying Your Website on Cloud Run.
What You’ll Learn
By the end of this guide, you’ll know how to:
- Create Compute Engine instances
- Use startup scripts to configure VMs
- Create instance templates and managed instance groups
- Set up health checks and HTTP(S) load balancers
- Use autoscaling and autohealing
- Enable Cloud CDN for caching
- Roll out code updates via rolling replacements
Prerequisites
- A Google Cloud account
- Basic knowledge of the terminal and Git
- Familiarity with Node.js and Docker helps, but isn’t required
Step 1: Activate Cloud Shell
Click the Activate Cloud Shell button in the GCP Console. Cloud Shell gives you a pre-configured virtual machine with the gcloud CLI already installed.
Set your region and zone (you can change them later if needed):
gcloud config set compute/zone us-east4-b
gcloud config set compute/region us-east4
Step 2: Enable Compute Engine API
gcloud services enable compute.googleapis.com
Step 3: Create a Cloud Storage Bucket
This bucket will hold your startup script and application code:
gsutil mb gs://sample-app-$DEVSHELL_PROJECT_ID
Step 4: Clone Your GitHub Repo
Clone your app from GitHub:
git clone https://github.com/niiiixd/sampla-app.git
cd sampla-app
Remove any node_modules to keep the upload lightweight:
rm -rf node_modules
Upload the source code to the bucket:
gsutil -m cp -r . gs://sample-app-$DEVSHELL_PROJECT_ID/
Step 5: Create the Startup Script
Create a file called startup-script.sh:
nano startup-script.sh
Paste the following:
#!/bin/bash
apt-get update
apt-get install -y ca-certificates git build-essential supervisor curl
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
apt-get install -y nodejs
mkdir -p /opt/app
gsutil -m cp -r gs://sample-app-<YOUR_PROJECT_ID>/* /opt/app/
cd /opt/app
npm install
cat >/etc/supervisor/conf.d/node-app.conf <<EOF
[program:nodeapp]
directory=/opt/app
command=npm start
autostart=true
autorestart=true
stderr_logfile=syslog
stdout_logfile=syslog
user=root
EOF
supervisorctl reread
supervisorctl update
Replace <YOUR_PROJECT_ID> with your actual project ID.
Then upload it:
gsutil cp startup-script.sh gs://sample-app-$DEVSHELL_PROJECT_ID
Step 6: Create Compute Engine VM
Deploy the first VM (this will serve both frontend and backend for simplicity):
gcloud compute instances create sample-instance \
--zone=$ZONE \
--machine-type=e2-standard-2 \
--metadata=startup-script-url=https://storage.googleapis.com/sample-app-$DEVSHELL_PROJECT_ID/startup-script.sh \
--tags=http-server
Step 7: Create a Firewall Rule
Allow access to port 3000:
gcloud compute firewall-rules create allow-http3000 \
--allow tcp:3000 \
--target-tags=http-server
Step 8: Get External IP and Test App
gcloud compute instances list
Open http://[EXTERNAL_IP]:3000 in your browser — you should see:
Hello, world!
This is Sample app version 3🚀
Step 9: Create an Instance Template and Managed Instance Group
Stop the VM:
gcloud compute instances stop sample-instance --zone=$ZONE
Create a template from it:
gcloud compute instance-templates create sample-template \
--source-instance=sample-instance \
--source-instance-zone=$ZONE
Create a Managed Instance Group:
gcloud compute instance-groups managed create sample-mig \
--zone=$ZONE \
--base-instance-name sample \
--size 2 \
--template sample-template
Step 10: Set Up Load Balancer and Health Checks
Create a health check:
gcloud compute http-health-checks create sample-health-check \
--port 3000 \
--request-path /
Create a backend service:
gcloud compute backend-services create sample-backend \
--http-health-checks sample-health-check \
--port-name http \
--global
Add your MIG to the backend:
gcloud compute backend-services add-backend sample-backend \
--instance-group sample-mig \
--instance-group-zone=$ZONE \
--global
Create a URL map and target proxy:
gcloud compute url-maps create sample-map --default-service sample-backend
gcloud compute target-http-proxies create sample-proxy --url-map=sample-map
Reserve an IP and set up forwarding:
gcloud compute forwarding-rules create sample-http-rule \
--global \
--target-http-proxy=sample-proxy \
--ports=80
Check the external IP:
gcloud compute forwarding-rules list --global
Step 11: Enable Autoscaling and CDN
Enable autoscaling:
gcloud compute instance-groups managed set-autoscaling sample-mig \
--zone=$ZONE \
--max-num-replicas 3 \
--target-load-balancing-utilization 0.6
Enable CDN:
gcloud compute backend-services update sample-backend --enable-cdn --global
Step 12: Push Code Updates
Make a change in app.js, then repeat the following:
gsutil -m cp -r . gs://sample-app-$DEVSHELL_PROJECT_ID/
Then roll out changes:
gcloud compute instance-groups managed rolling-action replace sample-mig \
--zone=$ZONE \
--max-unavailable=100%
Step 13: Simulate Failure (Optional)
SSH into one instance and stop the app:
gcloud compute ssh [INSTANCE_NAME] --zone=$ZONE
sudo supervisorctl stop nodeapp
exit
Then monitor:
watch -n 2 gcloud compute operations list \
--filter='operationType~compute.instances.repair.*'
Your MIG should automatically recreate the unhealthy instance.
Conclusion
Congratulations! 🎉 You just deployed, scaled, load-balanced, autohealed, and updated a Node.js app using Google Compute Engine.
You’ve now touched some of the most important GCP building blocks for production-grade deployments.
Subscribe for More
If you found this tutorial useful, consider subscribing to NotSoStatic for more deep-dive DevOps and cloud engineering guides.