A user uploads a profile picture and you need to resize it into a thumbnail. A new entry gets written to your database and you need to send a notification. Do you really want to provision an entire VM that sits idle 99% of the time, waiting for these events? Do you want to build a full container service for a task that takes 200 milliseconds?
This is the problem Cloud Functions solves. It is the purest form of serverless — you write a small piece of code, upload it, and Google handles everything else. When a trigger fires, Google finds a server, runs your code, and shuts it down. Scales to thousands automatically. Scales to zero when idle. You pay only for the milliseconds your code actually runs.
Understanding Triggers — The “When”
A Cloud Function does nothing until it receives a signal. That signal is a Trigger. There are two categories:
HTTP Triggers — The simplest kind. Google gives you a unique HTTPS URL. When any application, user, or webhook hits that URL, your function runs. This turns your function into a lightweight, serverless API endpoint.
Event-Driven Triggers — This is where Cloud Functions really shines. Your function automatically reacts to events in other Google Cloud services.
Let me walk through a concrete example. We want to generate a thumbnail every time a user uploads an image:
- Event — User uploads
golden-retriever.jpgto a Cloud Storage bucket calleduser-profile-pics - Trigger fires — The function is watching that bucket. The moment the file is finalized, it wakes up
- Function executes — It receives event data:
{ "bucket": "user-profile-pics", "name": "golden-retriever.jpg" } - Action — Downloads the image, resizes it, uploads
thumbnail.jpgto auser-profile-thumbnailsbucket - Function sleeps — Job done. Goes dormant. Costs fractions of a penny.
Other popular event triggers:
- Pub/Sub — Fires when a message is published to a topic. Great for decoupling systems.
- Firestore — Fires when a document is created, updated, or deleted.
- Cloud Audit Logs — Fires in response to specific API calls. Useful for automated security workflows.
Writing the Code
Cloud Functions supports Python, Node.js, Go, Java, Ruby, and more. The structure is simple — a source file with your function logic and a dependency file for external libraries.
Here is a basic HTTP function in Python:
# main.py
import functions_framework
@functions_framework.http
def hello_http(request):
"""HTTP Cloud Function."""
request_json = request.get_json(silent=True)
request_args = request.args
if request_json and 'name' in request_json:
name = request_json['name']
elif request_args and 'name' in request_args:
name = request_args['name']
else:
name = 'World'
return f'Hello, {name}!'
Deploying a Function
One command does it all:
# Deploy an HTTP-triggered function (Gen2)
gcloud functions deploy hello-world-http \
--gen2 \
--runtime=python312 \
--region=us-central1 \
--source=. \
--entry-point=hello_http \
--trigger-http \
--allow-unauthenticated
Let me break down the important flags:
– --gen2 — Use the modern 2nd Generation (built on Cloud Run under the hood)
– --runtime — Language and version
– --entry-point — The function name in your code to execute
– --trigger-http — HTTP trigger. For a storage trigger, you would use --trigger-event=google.storage.object.finalize --trigger-resource=my-bucket
– --allow-unauthenticated — Makes the endpoint public
For a storage-triggered function:
# Deploy a function triggered by Cloud Storage uploads
gcloud functions deploy generate-thumbnail \
--gen2 \
--runtime=python312 \
--region=us-central1 \
--source=. \
--entry-point=resize_image \
--trigger-event=google.storage.object.finalize \
--trigger-resource=user-profile-pics
You can configure memory (128MB to 32GB) and timeout (up to 60 minutes for Gen2).
IAM — Two Sides to Understand
IAM for Cloud Functions has two distinct parts, and they are often confused:
1. The Function’s Identity (Runtime Service Account)
Your function needs permissions to interact with other services. It runs as a service account. By default, it uses the project’s default service account (which has broad permissions — not ideal).
The right approach: create a dedicated service account with minimal permissions.
# Create a dedicated service account
gcloud iam service-accounts create thumbnail-sa \
--display-name="Thumbnail Generator SA"
# Grant it only what it needs
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:[email protected]" \
--role="roles/storage.objectAdmin"
2. Who Can CALL the Function (Invoker Role)
For event-driven functions, this is automatic. For HTTP functions, you control access via the Cloud Functions Invoker role (roles/cloudfunctions.invoker).
- Private (default) — Only authenticated principals with the Invoker role can call it
- Public — Grant
roles/cloudfunctions.invokertoallUsers(that is what--allow-unauthenticateddoes behind the scenes)
VPC Connectivity
By default, Cloud Functions runs in a Google-managed environment outside your VPC. If it needs to reach a Cloud SQL database or Memorystore instance with a private IP, use a Serverless VPC Access Connector — a managed bridge from serverless into your VPC.
Common Pitfalls and Best Practices
Pitfall: Giving the runtime service account the Editor role. Massive security risk.
Best Practice: Create a dedicated service account with the bare minimum permissions.
Pitfall: Writing “fat” functions that do too many things. Slow, hard to debug, inefficient.
Best Practice: Keep functions small, single-purpose, and fast. Chain complex workflows with Pub/Sub.
Pitfall: Forgetting about cold starts. First invocation after idle time is slower.
Best Practice: For latency-sensitive apps, configure a minimum number of warm instances (with cost implications).
Pitfall: Assuming an event will be delivered exactly once. Network issues can cause duplicate deliveries.
Best Practice: Design functions to be idempotent — running them twice with the same input produces the same result. For the thumbnail example, check if the thumbnail exists before creating it.
Quick Reference
# Deploy HTTP function
gcloud functions deploy [NAME] --gen2 --runtime=[RUNTIME] \
--region=[REGION] --source=. --entry-point=[HANDLER] --trigger-http
# Deploy Storage-triggered function
gcloud functions deploy [NAME] --gen2 --runtime=[RUNTIME] \
--region=[REGION] --source=. --entry-point=[HANDLER] \
--trigger-event=google.storage.object.finalize \
--trigger-resource=[BUCKET]
# Deploy Pub/Sub-triggered function
gcloud functions deploy [NAME] --gen2 --runtime=[RUNTIME] \
--region=[REGION] --source=. --entry-point=[HANDLER] \
--trigger-topic=[TOPIC]
# Call an HTTP function directly
gcloud functions call [NAME] --region=[REGION]
# List all functions
gcloud functions list
# View function logs
gcloud functions logs read [NAME] --region=[REGION]
# Grant public access
gcloud functions add-invoker-policy-binding [NAME] --member="allUsers"

