Cloud Functions in Google Cloud: Serverless Code That Reacts to Everything

Cloud Functions in Google Cloud: Serverless Code That Reacts to Everything

A deep, plain-English tour of how Google Cloud runs your small pieces of code on demand, from triggers and scaling to security, reliability, cost, and the design habits that keep production systems calm.

Imagine a doorbell that, the moment someone presses it, wakes a helper who was sleeping in a back room. The helper opens the door, does one small job, and goes back to sleep. Nobody pays the helper to sit by the door all day, and if a hundred people press the bell at once, a hundred helpers appear. That is the core idea of Cloud Functions: you hand Google a small piece of code, tell it what should wake the code up, and Google handles everything else, from machines and operating systems to scaling and patching. This guide explains how that magic actually works, why it behaves the way it does, and how to use it with the calm confidence of someone who has seen it run in production.

1What Cloud Functions Really Is

Before diving deep, we need a clear picture of what this service is, what problem it solves, and what its name means today.

The problem it solves

Traditionally, running even a tiny piece of logic meant renting a server, installing an operating system, installing a language runtime, opening network ports, applying security patches, and watching the machine around the clock. If the logic only needed to run when a file was uploaded or a message arrived, the server sat idle most of the time and you still paid for it. Cloud Functions removes that burden. You supply only the logic. Google supplies and manages the rest.

This style is called serverless computing. The word is a little misleading, because servers absolutely exist. The point is that you never see them, never log into them, and never decide how many there should be. You think in terms of events and code, not machines.

Simple Analogy

Think of a taxi app versus owning a car. Owning a car means paying for insurance, parking, and maintenance even when it sits in the garage. A taxi app gives you a ride only when you need one and charges only for the trip. Cloud Functions is the taxi app for computing: you pay for the ride, not the garage.

A note on naming

Google has been merging the identity of Cloud Functions with its container platform. The newer generation of the product is now presented in Google Cloud documentation under the name Cloud Run functions, and the older generation is described as Cloud Run functions (1st gen). Many engineers still say “Cloud Functions” out of habit, and the tooling, the console, and a great deal of existing material still use that name. In this guide we say Cloud Functions and note the generation whenever the difference matters. If you meet either name in documentation, you are looking at the same family of ideas.

i
Good to Know

The rename is not just cosmetic. It reflects the fact that the second generation is built directly on top of Cloud Run and Eventarc. Understanding that relationship explains most of the behavior you will see later, including scaling, concurrency, and networking.

What a function actually is

A function is a single-purpose unit of code with an entry point. Google Cloud calls that entry point when something happens. The something might be a web request, a new message on a queue, a file landing in storage, or a scheduled tick of a clock. The function receives information about what happened, does its work, and finishes. It is meant to be short-lived, stateless, and focused on one job.

0
Servers for you to manage
Scale to zero
No traffic, no running instances
Per-use
Billed for what you consume

2Core Concepts and Vocabulary

Every new field has its own words. Here are the ones you will hear constantly, each explained from scratch.

Term 1

Function

A small block of your code with one entry point that Google Cloud calls on demand. It exists to do one job and finish.

Term 2

Trigger

The thing that wakes the function up. It can be an HTTP request, a Pub/Sub message, a Cloud Storage change, a Firestore change, and more.

Term 3

Event

A small record describing that something happened, such as “file report.pdf was created in bucket X”. It is the message the trigger hands to your code.

Term 4

Instance

One running copy of your function that Google Cloud starts to handle work. Many instances can run side by side.

Term 5

Cold Start

The extra delay when a brand new instance must start before it can handle a request, because nothing was warm and ready.

Term 6

Concurrency

How many requests a single instance may handle at the same time. A higher number means fewer instances for the same traffic.

Term 7

Service Account

The identity your function uses when it talks to other Google Cloud services. It works like an employee badge with specific permissions.

Term 8

Idempotency

The property that doing the same job twice leaves the same result as doing it once. It is the safety net for retries.

Why these words matter

Each term above describes a decision you will make or a behavior you will observe. A trigger decides how work reaches you. An instance and its concurrency decide how much work one copy of your code can juggle. A cold start decides how snappy the first request feels. A service account decides what your code is allowed to touch. Idempotency decides whether a retry quietly heals a failure or quietly corrupts your data. Learn these eight words well and the rest of the service becomes a matter of combining them sensibly.

Simple Analogy

A restaurant helps here. The trigger is the customer ringing the order bell. The event is the order slip. The instance is a cook. Concurrency is how many dishes one cook can prepare at once. A cold start is the time it takes to wake a cook, tie an apron, and light the stove. The service account is the cook’s keycard that opens the pantry but not the cash register.

3Architecture and Components: Two Generations

Cloud Functions exists in two generations. Knowing how they differ is the single most useful architectural fact about the service.

First generation

The first generation was the original design. It used its own purpose-built infrastructure, its own event system for background triggers, and a simple model in which each instance handled exactly one request at a time. It is straightforward and well understood, but it carries tighter limits on memory, request size, and duration, and it cannot do things like handle several requests in one instance.

Second generation

The second generation is a rebuild on top of two other Google Cloud products. Your code is packaged into a container and executed by Cloud Run, which provides the serving, scaling, and networking engine. Event triggers are delivered by Eventarc, which provides a single, standardized way to route events from many sources to a destination. Because it sits on these foundations, the second generation inherits larger limits, better concurrency, and a much wider set of event sources.

flowchart TD
    A["Your function source code"] --> B["Cloud Build"]
    B --> C["Container image"]
    C --> D["Artifact Registry"]
    D --> E["Cloud Run service (serving engine)"]
    F["Eventarc (event routing)"] --> E
    G["HTTP clients"] --> E
    E --> H["Function instances"]
        
FIG 1 — How a second generation function is assembled and served

Comparing the generations

Aspect1st generation2nd generation
FoundationPurpose-built function infrastructureCloud Run for serving, Eventarc for events
Requests per instanceOne at a timeMany at once through concurrency
Longest HTTP run9 minutesUp to 60 minutes
Longest event-driven run9 minutes9 minutes
Maximum memory per instance8 GiB32 GiB
Maximum HTTP request size10 MB32 MB
Event sourcesA limited, fixed setA broad range through Eventarc
Minimum warm instancesSupportedSupported, with finer control
!
Check Current Limits

Limits such as duration, memory, and request size are adjusted by Google Cloud from time to time and some can be raised on request. Always confirm the numbers against the official quota page before you design around an edge value.

The main moving parts

1

Source and build

Your code is uploaded and turned into a runnable container image using standard build tooling, without you writing a container file.

2

Image storage

The finished image is stored in a registry so that new instances can be started from it quickly and repeatedly.

3

Serving engine

The serving layer receives requests, starts instances, spreads load, and stops instances when they are no longer needed.

4

Event routing

For background work, an event router listens to sources and delivers matching events to your function as standardized messages.

4Internal Working: From Source to Running Instance

What happens between the moment you click deploy and the moment your code answers its first request?

The build pipeline

When you deploy, the platform takes your source and detects the language and its dependency list. It then uses buildpacks, which are standard recipes that know how to assemble a clean, minimal container for a given language. Your dependencies are downloaded, your code is combined with a small framework that knows how to call your entry point, and the result is sealed into an image. Because this runs on managed build infrastructure, the build uses its own identity and its own permissions, and build failures show up as deployment errors rather than runtime errors.

This is an important distinction. A function can fail in two completely different phases: at build time, where the problem is usually a missing dependency or a permission on the build identity, and at run time, where the problem is usually your own logic or a missing permission on the function identity. Learning to ask “which phase failed?” saves hours of confusion.

Simple Analogy

Building a function image is like preparing a lunchbox. A separate kitchen assembles the box with your recipe and the right ingredients. Only afterwards is the box handed to whoever eats it. If the box is packed wrong, you learn that in the kitchen, not at the lunch table.

The framework inside the container

Your code does not run alone. Inside the container, Google supplies a small framework for each supported language. That framework starts a tiny web server, listens on the expected port, translates incoming requests or events into the shape your function expects, calls your entry point, and then translates your return value back into a response. This is why you can write very little code and still get a fully working web service. It is also why a function must be careful about what it does after returning, a topic we return to in the chapter on common mistakes.

Starting an instance

1

Capacity is found

The platform picks a machine with room for the requested CPU and memory in your chosen region.

2

Image is fetched

The container image is pulled to that machine. Smaller images generally arrive faster.

3

Runtime boots

The language runtime and the framework start up inside a sandbox that isolates your code from other customers.

4

Global code runs

Anything written outside the entry point, such as creating clients or loading settings, runs once at this stage.

5

First request served

Only now does your entry point run for the first time. Everything before this step is the cold start.

Why global code is powerful

Because step four runs once per instance, anything expensive that you set up there is reused by every later request that lands on the same instance. Database connections, API clients, and loaded configuration are classic examples. This is the main tool you have for making functions fast. The flip side is that this reuse is a convenience, not a promise. Instances can disappear at any time, so you may use them as a cache but never as a source of truth.

i
Practical Tip

Put expensive, reusable setup in global scope and keep the entry point lean. Treat anything stored in memory as a disposable optimization that may vanish between two requests.

Sandboxing and isolation

Your function runs inside a hardened sandbox. The sandbox restricts what the code can do on the host: it limits system calls, isolates the file system, and keeps neighboring workloads from seeing each other. For you this means two practical facts. First, the writable file system is temporary and is held in memory, so writing files consumes the memory you pay for. Second, you cannot count on any particular host-level behavior, because the platform deliberately hides it.

5Triggers and Event Sources

A function sleeps until something wakes it. The kind of trigger you pick shapes almost everything about how the function behaves.

The two big families

Triggers fall into two families. The first is HTTP, where a caller sends a web request and waits for a response. The second is event-driven, where something happens elsewhere in the cloud and a message is delivered to your function in the background, with nobody waiting for an answer. The two families have different timeouts, different retry behavior, and different expectations about what a “successful” run means.

Trigger 1

HTTP

Gives your function a web address. A caller sends a request and waits. Ideal for APIs, webhooks, and anything a person or another system calls directly.

Trigger 2

Pub/Sub

Runs when a message is published to a topic. Excellent for decoupling producers from consumers and absorbing bursts of work.

Trigger 3

Cloud Storage

Runs when an object is created, deleted, or changed in a bucket. A natural fit for processing uploaded files.

Trigger 4

Firestore

Runs when a document is created, updated, or deleted. Useful for reacting to data changes without polling.

Trigger 5

Audit Log Events

Through Eventarc, functions can react to many Google Cloud administrative actions recorded in audit logs.

Trigger 6

Scheduler and Tasks

A scheduler can call a function on a clock, and a task queue can call it with controlled rate and retry settings.

CloudEvents: one shape for every event

In the second generation, events arrive in a standard envelope called CloudEvents. Instead of each source inventing its own message layout, every event carries the same kind of header fields: what type of event it is, where it came from, when it happened, and a unique identifier, followed by a data payload specific to the source. This consistency means you can write generic handling and logging once and reuse it across many triggers.

Simple Analogy

CloudEvents is like a standard shipping label. Whether the parcel holds books, shoes, or medicine, the label always shows sender, destination, date, and tracking number in the same places. The contents differ, but the sorting machinery can read every label.

Choosing the right trigger

SituationBetter triggerWhy
A mobile app needs an answer nowHTTPThe caller waits and receives a response
Bursty background jobsPub/SubThe queue absorbs spikes and smooths the load
Process each uploaded fileCloud Storage eventNo polling needed, runs per object
Nightly cleanupScheduler calling HTTP or Pub/SubTime-based, predictable, easy to audit
React to a database changeFirestore eventDirectly tied to the data change
Rate-limited calls to a fragile serviceTask queueDispatch rate and retries are tunable
!
Common Misconception

Many beginners assume an event is delivered exactly once. In reality, event delivery is at-least-once, which means a duplicate is possible. Your function must be prepared to see the same event twice. We cover this in the reliability chapter.

6Data Flow and Lifecycle of a Request

Follow one piece of work from the outside world all the way through your function and back.

The HTTP path

A client sends a request to the function address. The request first reaches Google’s front end, which terminates the secure connection, checks whether the caller is allowed to invoke the function, and applies ingress rules. The request is then routed to an instance that has room. If no instance has room, a new one starts, which is the cold start described earlier. The framework hands the request to your code, your code responds, and the response travels back to the client.

sequenceDiagram
    participant C as Client
    participant F as Google front end
    participant R as Serving engine
    participant I as Function instance
    C->>F: Send request
    F->>F: Check identity and ingress rules
    F->>R: Forward allowed request
    R->>I: Route to instance with free capacity
    I->>I: Run function logic
    I-->>R: Return response
    R-->>C: Deliver response
        
FIG 2 — Life of an HTTP request

The event path

For event-driven functions, there is no client waiting. A source such as Pub/Sub or Cloud Storage produces an event. Eventarc picks it up, wraps it as a CloudEvent, and delivers it to the function using an internal HTTP call. From the function’s point of view, the event simply arrives like a request, and a success signal tells the router that the work is done. A failure signal tells the router that it may need to try again, depending on how retries are configured.

flowchart LR
    S["Event source (storage, topic, database)"] --> E["Eventarc router"]
    E --> T["Internal delivery with identity"]
    T --> F["Function instance"]
    F -->|"Success"| D["Event marked done"]
    F -->|"Failure"| R["Retry or give up"]
    R --> T
        
FIG 3 — Life of an event-driven invocation

The lifecycle of an instance

1

Created

Started because demand exceeded the capacity of existing instances, or because you asked for a minimum number of warm ones.

2

Active

Handling one or more requests. CPU is fully available while a request is in progress.

3

Idle

Kept alive for a while with no work, ready to serve the next request quickly. CPU may be throttled in this stage by default.

4

Retired

Shut down when demand falls, and scaled all the way to zero if nothing needs it.

!
Work After Return

Once your function returns its response, background work you started may be paused or stopped because CPU can be throttled. Finish all important work before you return, or move it into a separate event or task.

“Treat every instance as a guest who might leave at any moment, and your design will always be safe.”

7Scaling, Concurrency, and Cold Starts

The thing people love most about serverless is automatic scaling. Understanding how it decides what to do lets you tune it instead of fearing it.

How automatic scaling decides

The platform watches how much work is arriving and how much capacity exists. When requests pile up faster than current instances can serve them, it starts more instances. When demand falls, it retires instances, down to zero if there is no traffic at all. Several factors influence how fast and how far this can go: your minimum and maximum instance settings, your concurrency setting, how long your function takes to finish, how long it takes to start cold, your error rate, and the regional capacity that happens to be available at that moment.

HTTP functions generally scale up very quickly. Background functions fed by event sources tend to scale more gradually, because the event system ramps delivery in a measured way to avoid overwhelming your code. This means a sudden flood of events may be processed with a short, smooth ramp instead of an instant jump.

Simple Analogy

Picture a supermarket that opens a new checkout lane whenever the queue gets long and closes lanes when it empties. Hiring cashiers takes a little time, so a sudden rush causes a brief wait before new lanes open. That brief wait is a cold start, and the policy of how many lanes may ever be open is your maximum instance setting.

Concurrency: the biggest second-generation advantage

In the first generation, one instance handled exactly one request at a time, so a thousand simultaneous requests needed a thousand instances. In the second generation, one instance can handle many requests at once. The default is high when the instance has at least one full CPU, and the upper bound is much higher still. The effect is dramatic: the same traffic can be served by far fewer instances, which means fewer cold starts and a lower bill.

There is a catch. When one instance handles several requests together, those requests share the same memory and the same global variables. There is no isolation between them. If your code keeps request-specific data in a shared global variable, two requests can overwrite each other’s data and produce bugs that appear only under load. Concurrency is therefore a trade between efficiency and the discipline of writing code that is safe to run in parallel.

i
Concurrency Rule of Thumb

If your function is mostly waiting on network calls, high concurrency is a big win. If it is heavy on CPU or uses a lot of memory per request, lower the concurrency so requests do not starve each other. A fractional CPU allocation also forces concurrency back to one.

The four dials you control

Dial 1

Minimum instances

Keeps a number of instances always warm. This removes most cold starts for steady traffic but you pay for idle time.

Dial 2

Maximum instances

Caps how far the function can scale. It protects downstream systems and your budget from runaway growth.

Dial 3

Concurrency

Sets how many requests one instance may handle together. Tune it to match the nature of your workload.

Dial 4

CPU and memory

Decides how powerful each instance is. More memory also usually brings more CPU, which speeds up startup and work.

Understanding cold starts

A cold start is the sum of the startup steps described in the internal working chapter: finding capacity, fetching the image, booting the runtime, and running global code. Several things make it longer: large container images, heavy dependencies, languages with slower startup, and global code that does a lot of work such as opening many connections. Several things make it shorter: lean dependencies, lazy loading of rarely used modules, generous CPU, and keeping a few minimum instances warm.

flowchart TD
    A["Request arrives"] --> B{"Warm instance with free capacity?"}
    B -->|"Yes"| C["Handle immediately"]
    B -->|"No"| D["Start new instance"]
    D --> E["Fetch image and boot runtime"]
    E --> F["Run global code"]
    F --> C
    C --> G["Return response"]
        
FIG 4 — Warm path versus cold path

Protecting what lies behind your function

Automatic scaling is wonderful until your function scales faster than the database behind it. A thousand instances each opening ten connections can overwhelm a database that was designed for a few hundred. The maximum instance setting, sensible connection reuse, connection pooling at a managed layer, and queues placed in front of fragile systems are the standard protections. Scaling the front is easy; scaling the whole chain safely is the real design task.

8Networking: Who Can Reach Your Function, and What It Can Reach

Networking has two directions. Ingress is traffic coming in to your function. Egress is traffic going out from it.

Ingress control

By default an HTTP function has a public address on the internet. You can tighten this. Ingress settings let you allow all traffic, allow only traffic that comes from inside your own project or private network and certain Google services, or allow only internal traffic combined with a load balancer. Choosing a stricter setting is the first step toward making a function private. Even a public address does not mean a public function, though, because the caller must also pass the identity check described in the security chapter.

Egress and private resources

By default your function reaches the internet and public Google services directly. Many real systems, however, keep databases, caches, and internal services inside a private network with no public address. To reach those, a function needs a path into the private network. Google Cloud offers a managed connector for this purpose, and newer direct options that attach the function’s traffic to a network without a separate connector resource. You also choose whether all outbound traffic goes through that private path or only traffic bound for private addresses.

Simple Analogy

Think of an office building. Ingress rules are the front desk deciding who may enter the lobby. Egress rules are the employee side door that decides whether staff leave to the street or use a private tunnel to the warehouse next door. A private network connector is that tunnel.

Fixed outbound addresses

Some partners only accept calls from known, fixed addresses. Functions normally use a wide pool of changing addresses, so they cannot satisfy such a rule on their own. The standard solution is to route outbound traffic through a managed network address translation service that owns a reserved static address. All function traffic then appears to come from that single, stable address, which the partner can safely allow.

flowchart LR
    U["Internet caller"] --> LB["Load balancer or public address"]
    LB --> FN["Function instance"]
    FN --> VPC["Private network path"]
    VPC --> DB["Private database"]
    VPC --> NAT["Managed NAT with static address"]
    NAT --> P["Partner service"]
        
FIG 5 — Inbound control and private outbound paths
i
Design Tip

Combine ingress restriction with identity checks. Restricting where traffic may come from is a network control. Restricting who may call is an identity control. Strong designs use both because each covers a gap the other leaves.

Connections and limits

Every instance has limits on how many outbound connections it can open and how fast. Creating a new connection for every request wastes time and can hit these limits. Reusing a connection created in global scope is the healthy habit. Closing connections carelessly or leaking them across thousands of instances leads to exhaustion errors that are confusing to diagnose.

9Security: Identity, Secrets, and Boundaries

Serverless does not mean security-free. Google secures the machines, but you remain responsible for who can call your code, what your code can touch, and how it handles secrets.

Two directions of identity

Security in Cloud Functions has two directions, and mixing them up is a classic mistake. The first is who may invoke your function. The second is what your function may do once it is running.

For invocation, the key concept is the invoker permission. A function that requires authentication will reject callers who cannot prove their identity and who lack permission to invoke it. A function can be deliberately made public, but that should be a conscious, reviewed decision, not an accident. For service-to-service calls, the calling service presents a signed identity token, and the function verifies it before running any logic.

For runtime behavior, the function runs as a service account. That identity is what other Google Cloud services see when your function calls them. If the function reads a storage bucket, the service account needs read permission on that bucket, and nothing more.

Control 1

Invoker permission

Decides which people or services may call the function. Keep it narrow and review any public access carefully.

Control 2

Runtime service account

Decides what the function may access. Give each function its own account with only the permissions it needs.

Control 3

Build identity

Decides what the build process may read and write. It is separate from the runtime identity.

Control 4

Ingress settings

Decides which network sources may reach the function at all.

Control 5

Secret management

Keeps passwords and keys in a dedicated service, injected at run time instead of stored in code.

Control 6

Perimeters and encryption

Service perimeters and customer-managed encryption keys add boundaries for regulated environments.

Least privilege in practice

The default runtime identity for a project is often broad, because it exists for convenience. Leaving a function on a broad default identity means a vulnerability in that one function could expose much more than it ever needed. The healthier habit is to create a dedicated service account per function, or per closely related group of functions, and grant only the specific roles required on only the specific resources needed.

Simple Analogy

Imagine a hotel. A housekeeper’s keycard opens guest rooms on assigned floors but not the manager’s office or the safe. If every employee carried a master key, one lost card would be a disaster. Per-function service accounts are the housekeeper’s limited keycard.

Handling secrets

Never place passwords, tokens, or keys inside source code or plain configuration text, because source code is copied, reviewed, and stored in many places. Use a dedicated secret manager. The function can be configured so that a secret appears either as an environment value or as a mounted file at start time, and the platform fetches it using the function’s own identity. This also makes rotation easier, because you update the secret in one place instead of redeploying every consumer.

!
Public by Accident

Granting the invoker role to everyone makes a function callable by anyone on the internet. This is the most common serious configuration error. Always confirm whether a function is meant to be public before granting that access.

Supply chain and input safety

Your function depends on libraries written by others. Keep dependencies minimal, pin their versions, scan them for known problems, and update them regularly. Also treat every input as untrusted. Whether a request comes from the internet or an event comes from a bucket someone else can write to, validate its shape and size before acting. An attacker can often influence event contents indirectly, for example by choosing the name of an uploaded file.

10Reliability: Retries, Idempotency, and Failure Handling

Distributed systems fail in small, strange ways all the time. A reliable function is one designed with that expectation.

How retries work

For HTTP functions, retrying is the caller’s job. If the call times out or returns an error, the client decides whether to try again. For event-driven functions, retrying is configurable. You may leave retries off, in which case a failed event is dropped after the first attempt, or turn retries on, in which case the event system keeps trying with growing delays for a limited period. Choosing between these is a business decision about whether losing an event is worse than seeing it more than once.

Simple Analogy

A courier delivering a parcel who finds nobody home can either leave a note and stop, or come back tomorrow and the day after. Retries on means the courier keeps coming back. The risk is that if the first delivery actually succeeded but the confirmation got lost, the courier may deliver a second identical parcel.

Idempotency: your safety net

Because the same event can arrive more than once, the function should produce the same final result whether it runs once or three times. This property is called idempotency. A function that adds a record, charges a card, or sends an email is not naturally idempotent. It must be made so.

The usual approach is to use the unique identifier carried by each event, or a key supplied by the caller. Before doing the sensitive work, the function checks a durable store to see whether that identifier has already been processed. If it has, the function skips the work and returns success. If not, it does the work and records the identifier, ideally in a single atomic step so that two parallel attempts cannot both pass the check.

1

Read the identifier

Take the unique event identifier or the caller’s idempotency key from the incoming message.

2

Check the ledger

Look in a durable store to see whether this identifier was already processed.

3

Act or skip

If new, perform the work. If seen before, skip it and report success so the router stops retrying.

4

Record the outcome

Store the identifier atomically with the result so that a duplicate in parallel cannot slip through.

Poison messages and dead letters

Some events can never succeed, for example a malformed message that always makes your code crash. With retries enabled, such a message would be retried again and again, wasting resources and hiding real problems. The cure is a dead-letter path: after a limited number of attempts, the failing message is moved to a separate holding place where people or tools can inspect it later. Designing this path up front turns a mysterious production incident into a calm review of a short list.

Timeouts and the shape of long work

Every function has a maximum duration. HTTP functions in the second generation may run up to an hour, event-driven functions are limited to a much shorter window, and scheduled or task-queue functions sit in between. When work could exceed these limits, do not stretch the function. Break the work into smaller steps, each triggered by the completion of the last, or hand long jobs to a service built for long-running batch work. A function that regularly runs near its limit is a warning sign, not a success.

i
Return Codes Matter

For event-driven functions, a success response tells the router the event is finished, and an error response asks for a retry when retries are on. Swallowing every error and always reporting success hides failures, while reporting an error for a permanently bad message causes endless retries. Distinguish temporary problems from permanent ones.

flowchart TD
    A["Event delivered"] --> B{"Already processed?"}
    B -->|"Yes"| C["Skip and report success"]
    B -->|"No"| D["Do the work"]
    D --> E{"Worked?"}
    E -->|"Yes"| F["Record id and report success"]
    E -->|"Temporary error"| G["Report error for retry"]
    E -->|"Permanent error"| H["Send to dead letter and report success"]
        
FIG 6 — A resilient event-handling decision path

11Monitoring, Logging, and Metrics

You cannot fix what you cannot see. Because you never log into the machine, observability is delivered through managed tools.

Logs

Anything your function writes to its standard output and error streams is collected automatically and sent to Cloud Logging. Platform-generated entries, such as request records and errors, appear alongside your own. The most useful habit is structured logging: writing log entries as structured records with named fields instead of free text. Structured entries can be filtered, grouped, and counted far more easily, and they let you attach a correlation identifier so that every log line belonging to one request or one event can be found together.

Metrics

Cloud Monitoring collects metrics automatically for every function. The most useful ones answer a few basic questions: how many invocations are happening, how long they take, how much memory and CPU they use, how many instances are running, and how many executions end in errors. You can build dashboards from these and, more importantly, create alerts so that a person is told when something drifts out of its healthy range.

Rate
How many invocations per second
Errors
Share of invocations that fail
Duration
How long each run takes

Tracing

When one user action flows through several functions and services, a single log line tells only a fragment of the story. Distributed tracing links the pieces together into one timeline, so you can see that a slow response was caused by a particular downstream call. Passing the trace context along with each outgoing call is what stitches the timeline together.

Simple Analogy

Logs are like a diary of each worker, metrics are the dashboard on a factory wall showing counts and speeds, and traces are the tracking history of one parcel as it moves from station to station. You need all three to understand a busy factory.

What to alert on

Alert 1

Error ratio

Alert when the proportion of failing invocations rises above a normal level for several minutes.

Alert 2

Latency

Alert on slow tail latency, not only averages, because a few very slow requests hurt real users.

Alert 3

Instance count

Alert when instances approach the maximum, since the function may start rejecting or delaying work.

Alert 4

Dead-letter depth

Alert when messages accumulate in the holding place, because that means real work is failing.

!
Sensitive Data in Logs

Logs are widely readable and long-lived. Never write passwords, tokens, or personal data into them. Decide what is safe to log before an incident forces you to find out the hard way.

12Cost Model, Limits, and Trade-offs

Serverless pricing is simple in spirit but rewards understanding. The goal is to know what drives the bill and where the hard limits sit.

What you pay for

The bill is built from a few components: the number of invocations, the compute time consumed measured against the CPU and memory you allocated, outbound network data, and any minimum instances you keep warm. A generous monthly free allowance covers small workloads. Because the exact rates and free tiers change, always use the official pricing page and the pricing calculator rather than relying on remembered numbers.

Two design choices have an outsized effect on cost. First, concurrency: because one instance can serve many requests, high concurrency spreads the cost of an instance across more work. Second, minimum instances: they remove cold starts but convert part of the pay-per-use model into a steady cost. Neither is right for everything, so measure rather than guess.

Limits to design around

LimitWhy it matters
Maximum run time per invocationDecides whether long jobs must be split or moved elsewhere
Maximum memory and CPU per instanceDecides which workloads fit comfortably
Maximum request and response sizeLarge uploads should go to object storage instead of through the function
Maximum event sizeLarge payloads should be passed by reference, not by value
Maximum instances and project quotasProtects you and your neighbors, and may need a quota increase
Deployment sizeEncourages small, focused functions with lean dependencies

Advantages and trade-offs

Advantages

  • No servers to patch, provision, or monitor
  • Automatic scaling from zero to very large volumes
  • Pay only for what is actually used
  • Natural fit for event-driven designs
  • Fast path from idea to working service
  • Deep integration with other Google Cloud services

Disadvantages / Trade-offs

  • Cold starts can add delay to the first request
  • Hard limits on duration, memory, and payload size
  • Statelessness forces state into external services
  • Many small functions can become hard to track and govern
  • Debugging distributed event flows takes discipline
  • Heavy, steady workloads may be cheaper on always-on compute

Where functions sit among Google Cloud compute options

OptionBest forYou manage
Cloud FunctionsSmall, single-purpose, event-driven codeOnly the function code
Cloud Run servicesContainerized apps and APIs needing full control of the containerThe container and its service settings
App EngineTraditional web applications on a managed platformApplication code and configuration
Kubernetes EngineLarge, complex, multi-service platformsCluster design and workloads
Compute EngineFull virtual machines with custom operating system needsAlmost everything above the hardware
i
Choosing Wisely

Because the second generation runs on Cloud Run, moving a function that outgrows its simple shape to a full container service is a natural, low-friction step. Start simple and graduate only when a real need appears.

13Design Patterns and Anti-patterns

Good patterns turn small functions into dependable systems. Anti-patterns turn them into puzzles at three in the morning.

Patterns worth knowing

Fan-out with a queue

One incoming job is split into many small messages on a topic, and many function instances process them in parallel. The queue smooths bursts and the maximum instance setting protects downstream systems.

Event-carried reference

Instead of passing a large payload inside an event, the event carries only an identifier or a storage location, and the function fetches the data itself. This keeps events small and avoids size limits.

Orchestrated workflow

When a business process has several ordered steps with branches and error handling, a workflow service coordinates the sequence and calls small functions for each step. The functions stay simple and the process logic lives in one visible place.

Backend for a thin client

A mobile or web application calls a handful of authenticated functions that wrap sensitive logic and secrets, so the client never holds credentials or business rules directly.

Scheduled housekeeping

A scheduler triggers a function on a clock to rotate data, clean up temporary items, or send reports, replacing a server that would otherwise idle between jobs.

flowchart LR
    U["Upload to bucket"] --> E1["Storage event"]
    E1 --> F1["Splitter function"]
    F1 --> T["Topic of small jobs"]
    T --> W1["Worker instance A"]
    T --> W2["Worker instance B"]
    T --> W3["Worker instance C"]
    W1 --> R["Results store"]
    W2 --> R
    W3 --> R
        
FIG 7 — Fan-out processing with a queue in the middle

Anti-patterns to avoid

ANTI-PATTERN-01 Avoid
Problem

Functions call each other directly over HTTP in long chains, each waiting for the next to answer.

Why It’s Harmful

A slowdown anywhere stalls the whole chain, timeouts stack up, costs rise because every function waits while billed, and a single failure cascades backward.

Correct Approach

Decouple steps with queues or events, or use a workflow service to coordinate. Let each function finish its own short job and hand off.

ANTI-PATTERN-02 Avoid
Problem

The function stores important state in memory or on its temporary disk and expects it to be there later.

Why It’s Harmful

Instances come and go without warning, and different instances do not share memory, so data silently disappears or differs between requests.

Correct Approach

Keep durable state in a database, object store, or cache service. Use memory only as a disposable speed-up.

ANTI-PATTERN-03 Avoid
Problem

One giant function handles many unrelated jobs behind a single entry point, chosen by a flag in the request.

Why It’s Harmful

Dependencies bloat, cold starts lengthen, permissions become broad, and a bug in one job risks all the others.

Correct Approach

Split by responsibility. Each function gets its own small dependency set, its own identity, and its own scaling settings.

ANTI-PATTERN-04 Avoid
Problem

A function that writes to a bucket is triggered by changes to that very same bucket.

Why It’s Harmful

Each write triggers another run, which writes again, creating an infinite loop that can burn budget extremely fast.

Correct Approach

Write results to a different location than the one being watched, or filter events so the function ignores its own output.

ANTI-PATTERN-05 Avoid
Problem

Every function runs under one broad shared identity with wide permissions.

Why It’s Harmful

A flaw in any single function exposes everything the shared identity can reach, and audit trails cannot tell functions apart.

Correct Approach

Use a dedicated, minimal service account for each function or tightly related group.

“The best function is boring: small, stateless, safe to repeat, and easy to explain in one sentence.”

14Real-World and Industry Examples

The following scenarios show how the ideas in this guide combine in typical production systems. They are illustrative patterns drawn from common industry practice.

Media processing for an online learning platform

Instructors upload lecture videos to a storage bucket. A storage event triggers a function that does not process the video itself, but publishes a small job message carrying the file location. Worker functions or a dedicated media service pick up jobs, create thumbnails, extract audio, and write results to a different bucket. The two-bucket design avoids loops, and the queue lets hundreds of uploads arrive after a class ends without overwhelming anything.

Payment webhook receiver for a retail business

A payment provider calls a public HTTP function whenever a payment succeeds or fails. The function first verifies the provider’s signature to confirm the call is genuine, then checks an idempotency record keyed by the provider’s event identifier, then updates the order and publishes an internal event. Retries from the provider are harmless because of the idempotency check, and the heavy follow-up work is done asynchronously so the provider receives a fast reply.

Data hygiene for a healthcare analytics team

Files arrive from partner organizations. A function validates each file’s structure and size, rejects anything unexpected, removes or masks sensitive fields according to policy, and writes clean data to an analytics store. The function runs with a narrow identity, sits behind private networking, uses customer-managed encryption keys, and logs only non-sensitive metadata. This combination reflects the stricter security controls that regulated industries typically require.

Chat and notification bot for an engineering team

A function receives slash-command calls from a messaging tool, acknowledges them within the quick response window the tool demands, and then does longer work in a follow-up task. Secrets for the messaging tool live in the secret manager. Because the traffic is small and spiky, scale-to-zero keeps cost close to nothing.

Nightly reconciliation for a logistics company

A scheduler triggers a function every night. The function splits the day’s shipments into batches, publishes one message per batch, and lets worker functions compare records between systems in parallel. Discrepancies are written to a review table, and a final summary function emails a report. A dead-letter path captures batches that repeatedly fail so humans can investigate in the morning.

What these examples share

Look at the common threads. Each design keeps functions small and focused. Each uses a queue or event to decouple steps. Each protects against duplicates, uses a limited identity, and treats failure as a normal, planned-for condition. None relies on in-memory state. These habits, more than any specific feature, are what make a serverless system calm to operate.

15Best Practices and Common Mistakes

A checklist of habits that experienced teams build into every function, and the traps that catch newcomers.

Best practices

1

Keep functions small and single-purpose

One job per function keeps dependencies light, startup fast, permissions narrow, and failures easy to explain.

2

Reuse expensive resources

Create clients and connections once in global scope and reuse them, but never assume they last forever.

3

Design for duplicates

Assume every event may arrive twice and make the handling safe to repeat.

4

Use least-privilege identities

Give each function its own service account with only the permissions it truly needs.

5

Cap scaling deliberately

Set a maximum instance count that matches what your downstream systems can safely handle.

6

Keep secrets out of code

Store them in a secret manager and let the platform deliver them at start time.

7

Log with structure and correlation

Write structured entries with an identifier that ties together everything belonging to one request or event.

8

Test locally, deploy through automation

Run the function on a developer machine using the same framework, then deploy through a repeatable pipeline instead of manual steps.

9

Pin and review dependencies

Lock versions, scan for known issues, and prune anything you do not use to shorten cold starts.

Common mistakes

!
Mistake: Background work after returning

Starting work and returning immediately, expecting it to continue. After a response, CPU may be throttled and the work may stall or be cut off. Finish the work first, or pass it to another event or task.

!
Mistake: Unbounded scaling

Leaving the maximum instances unrestricted in front of a database or a rate-limited partner. A traffic spike becomes an outage elsewhere.

!
Mistake: Unsafe shared globals

Storing per-request data in global variables on a function that handles many requests at once. Parallel requests overwrite each other and bugs appear only under load.

!
Mistake: Treating warm instances as guaranteed

Designing as if an instance will always be warm and holding the data you stored. It will not always be, so cache only what you can afford to lose.

!
Mistake: Ignoring the build phase

Debugging runtime logic when the real problem is a failed build or a missing permission on the build identity. Always identify which phase failed first.

!
Mistake: Heavy lifting in the request path

Making a person wait while the function does slow work. Acknowledge quickly and finish the heavy work asynchronously whenever the caller does not need the result immediately.

A short pre-release review

QuestionWhat a good answer looks like
Who can invoke this?Only named callers, with public access a conscious decision
What can this touch?Only the specific resources it needs, via its own identity
What if the event repeats?The result is identical, protected by an idempotency check
What if it scales ten times?Downstream systems are protected by caps and queues
What if it keeps failing?A dead-letter path and an alert exist
How will we see it?Structured logs, metrics, alerts, and traces are in place

16Frequently Asked Questions

Short, direct answers to questions that come up again and again.

Q1Is “Cloud Functions” the same thing as “Cloud Run functions”?

They describe the same family of products. Google Cloud now presents the newer generation under the Cloud Run functions name because it is built on Cloud Run, while the older name remains common in tools and conversation.

Q2Should I choose the first or second generation for new work?

For new work, the second generation is generally the better starting point because of concurrency, larger limits, longer HTTP duration, and broader event sources. Check current documentation for any feature you specifically depend on.

Q3Can I completely avoid cold starts?

You can greatly reduce them with minimum instances, lean dependencies, and light global code, but you cannot promise to eliminate every one, because scaling out always involves starting new instances at some point.

Q4Is event delivery exactly once?

No. Delivery is at-least-once, so duplicates are possible. Make your function idempotent so that a repeated event does no harm.

Q5How long can a function run?

It depends on the generation and trigger type. In the second generation, HTTP functions can run much longer than event-driven ones, with scheduled and task-queue functions in between. Confirm the current figures on the official quota page and split work that approaches the limit.

Q6Can a function keep data between calls?

Only as a temporary speed-up in memory. Instances can disappear at any time and different instances do not share memory, so durable data belongs in a database, a cache service, or object storage.

Q7How do I let a function reach a private database?

Give the function a path into your private network using a managed connector or a direct network option, and send private-bound traffic through it. Keep the database free of public addresses.

Q8How do I stop strangers from calling my function?

Require authentication, grant the invoker permission only to specific identities, and consider stricter ingress settings. Never grant invoker access to everyone unless the function is truly meant to be public.

Q9Why is my bill higher than I expected?

Common causes include a trigger loop, a very high invocation count, minimum instances left on, low concurrency forcing many instances, long waits on slow downstream calls, and large outbound data. Metrics and billing reports help pinpoint which one applies.

Q10When should I stop using functions and use something bigger?

When you need long-lived processes, full control of the container, heavy steady computation, or a large multi-service application. Moving to a container service is a natural next step, especially because the second generation already runs on that foundation.

Q11Can many functions share one codebase?

Yes, but deploy them as separate functions so each has its own settings, identity, and scaling. Shared helper code is fine; one giant entry point doing everything is not.

Q12What is the best way to handle long jobs?

Break the work into smaller steps connected by events or a workflow service, or hand the job to a service designed for long-running batch work, instead of stretching a function toward its time limit.

17Summary and Key Takeaways

Cloud Functions lets you run small pieces of code in response to events or web requests without ever touching a server. The second generation, now surfaced as Cloud Run functions, rests on Cloud Run for serving and Eventarc for events, which brings higher concurrency, larger limits, and a wide range of event sources. Behind the simplicity sits a clear pipeline: your source is built into a container, stored in a registry, started as instances on demand, and called through a small framework. Scaling is automatic but tunable through minimum instances, maximum instances, concurrency, and resource size. Security depends on getting two identities right, one for who may call the function and one for what the function may do. Reliability depends on assuming duplicates and failures, using idempotency, retries, and dead-letter paths. Observability comes from structured logs, metrics, alerts, and traces. And the healthiest designs keep functions small, stateless, and decoupled by queues and events. Hold on to those ideas and you can build systems that are cheap when quiet, strong when busy, and calm when something goes wrong.

Key Takeaways

  • Think in events, not servers — a function is a small job woken by a trigger, and the platform handles machines, patching, and scaling.
  • Know your generation — the second generation runs on Cloud Run and Eventarc, giving concurrency, larger limits, and broader triggers.
  • Tune the four dials — minimum instances, maximum instances, concurrency, and CPU with memory shape performance, cost, and downstream safety.
  • Secure both directions — control who can invoke the function and give it a narrow runtime identity, with secrets kept in a secret manager.
  • Expect duplicates and failures — design for at-least-once delivery with idempotency, sensible retries, and a dead-letter path.
  • Stay stateless — treat instance memory as a disposable cache and keep durable state in external services.
  • Observe everything — structured logs, metrics, alerts, and traces turn a hidden system into one you can understand and trust.