Stay organized with collections Save and categorize content based on your preferences.

Cloud Functions execution environment

Cloud Functions run in a fully-managed serverless environment where Google handles infrastructure, operating systems, and runtime environments completely on your behalf. Each function runs in its own isolated secure execution context, scales automatically, and has a lifecycle independent from other functions.

Runtimes

Cloud Functions supports multiple language runtimes. You'll need the Runtime ID value if you're deploying functions from the command line or through Terraform.

Runtime Base image Runtime ID
Node.js 16 (recommended) Ubuntu 18.04 nodejs16
Node.js 18 (preview) Ubuntu 22.04 nodejs18
Node.js 14 Ubuntu 18.04 nodejs14
Node.js 12 Ubuntu 18.04 nodejs12
Node.js 10 Ubuntu 18.04 nodejs10
Node.js 8 (deprecated) Ubuntu 18.04 nodejs8
Node.js 6 (decommissioned) Debian 8 nodejs6
Python 3.10 (recommended) Ubuntu 22.04 python310
Python 3.9 Ubuntu 18.04 python39
Python 3.8 Ubuntu 18.04 python38
Python 3.7 Ubuntu 18.04 python37
Go 1.16 (recommended) Ubuntu 18.04 go116
Go 1.18 (preview) Ubuntu 22.04 go118
Go 1.19 (preview) Ubuntu 22.04 go119
Go 1.13 Ubuntu 18.04 go113
Go 1.11 Ubuntu 18.04 go111
Java 17 (recommended) Ubuntu 22.04 java17
Java 11 Ubuntu 18.04 java11
.NET Core 6.0 (recommended) Ubuntu 22.04 dotnet6
.NET Core 3.1 Ubuntu 18.04 dotnet3
Ruby 3.0 (recommended) Ubuntu 18.04 ruby30
Ruby 2.7 Ubuntu 18.04 ruby27
Ruby 2.6 Ubuntu 18.04 ruby26
PHP 8.1 (recommended) Ubuntu 18.04 php81
PHP 7.4 Ubuntu 18.04 php74

Updates and security patches are applied to runtimes and their dependencies when you deploy a function, unless otherwise noted. This includes updates and patches made by a language community, which become available after a period of testing for stability. Similarly, Cloud Functions might apply updates to other aspects of the execution environment, such as the operating system or included packages. These updates help keep your function secure.

Auto-scaling behavior

Cloud Functions implements the serverless paradigm, in which you run your code without worrying about the underlying infrastructure, such as servers or virtual machines. Once deployed, your functions are automatically managed and scaled.

Cloud Functions handles incoming requests by assigning them to instances of your function. Depending on the volume of requests, as well as the number of existing function instances, Cloud Functions may assign a request to an existing instance or create a new one.

In cases where inbound request volume exceeds the number of existing instances, Cloud Functions may start multiple new instances to handle requests. This automatic scaling behavior allows Cloud Functions to handle many requests in parallel, each using a different instance of your function.

In some cases, unbounded scaling might be undesirable. To address this, Cloud Functions allows you to configure a maximum number of instances that can coexist at any given time for a particular function.

Statelessness

To enable automatic management and scaling of your functions, functions must be stateless—one function invocation must not rely on in-memory state set by a previous invocation. Invocations might be handled by different function instances, which do not share global variables, memory, file systems, or other state.

If you need to share state across function invocations, your function should use a service such as Memorystore, Datastore, Firestore, or Cloud Storage to persist data. See Google Cloud databases and Google Cloud storage products for more information about database and storage options provided by Google Cloud.

Concurrency

Each instance of a function handles only one concurrent request at a time. This means that while your code is processing one request, there is no possibility of a second request being routed to the same instance. Thus the original request can use the full amount of resources (memory and CPU) that you allocate.

Because concurrent requests are processed by different function instances, they do not share variables or local memory. See Statelessness and Function instance lifespan for more information.

Cold starts

A new function instance is started in two cases:

  • When you deploy your function.

  • When a new function instance is automatically created to scale up to the load, or occasionally to replace an existing instance.

Starting a new function instance involves loading the runtime and your code. Requests that include function instance startup, called cold starts, can be slower than requests routed to existing function instances. If your function receives steady load, however, then the number of cold starts is typically negligible unless your function frequently crashes and requires restarting of the function environment.

If your function code throws an uncaught exception or crashes the current process, the function instance might be restarted. This can lead to more cold starts, resulting in higher latency, so we recommend catching exceptions and otherwise avoiding termination of the current process. See Reporting errors for a discussion of how to handle and report errors in Cloud Functions.

If your function is latency-sensitive, consider setting a minimum number of instances to avoid cold starts.

Function instance lifespan

Function instances are typically resilient and reused by subsequent function invocations, unless the number of instances is being scaled down due to lack of ongoing traffic or your function crashes. This means that when one function execution ends, another function invocation can be handled by the same function instance.

Function scope versus global scope

A single function invocation results in execution of only the body of the function declared as the entry point. The global scope of your function source code is only executed on cold starts, and not on instances that have already been initialized.

Node.js

// Global (instance-wide) scope
// This computation runs at instance cold-start
const instanceVar = heavyComputation();

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
exports.scopeDemo = (req, res) => {
  // Per-function scope
  // This computation runs every time this function is called
  const functionVar = lightComputation();

  res.send(`Per instance: ${instanceVar}, per function: ${functionVar}`);
};

Python

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()


def scope_demo(request):
    """
    HTTP Cloud Function that declares a variable.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Per-function scope
    # This computation runs every time this function is called
    function_var = light_computation()
    return 'Instance: {}; function: {}'.format(instance_var, function_var)

Go


// h is in the global (instance-wide) scope.
var h string

// init runs during package initialization. So, this will only run during an
// an instance's cold start.
func init() {
	h = heavyComputation()
}

// ScopeDemo is an example of using globally and locally
// scoped variables in a function.
func ScopeDemo(w http.ResponseWriter, r *http.Request) {
	l := lightComputation()
	fmt.Fprintf(w, "Global: %q, Local: %q", h, l)
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;

public class Scopes implements HttpFunction {
  // Global (instance-wide) scope
  // This computation runs at instance cold-start.
  // Warning: Class variables used in functions code must be thread-safe.
  private static final int INSTANCE_VAR = heavyComputation();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Per-function scope
    // This computation runs every time this function is called
    int functionVar = lightComputation();

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Instance: %s; function: %s", INSTANCE_VAR, functionVar);
  }

  private static int lightComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).sum();
  }

  private static int heavyComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).reduce((t, x) -> t * x).getAsInt();
  }
}

Ruby

# Global (instance-wide) scope.
# This block runs on cold start, before any function is invoked.
#
# Note: It is usually best to run global initialization in an on_startup block
# instead at the top level of the Ruby file. This is because top-level code
# could be executed to verify the function during deployment, whereas an
# on_startup block is run only when an actual function instance is starting up.
FunctionsFramework.on_startup do
  instance_data = perform_heavy_computation

  # To pass data into function invocations, the best practice is to set a
  # key-value pair using the Ruby Function Framework's built-in "set_global"
  # method. Functions can call the "global" method to retrieve the data by key.
  # (You can also use Ruby global variables or "toplevel" local variables, but
  # they can make it difficult to isolate global data for testing.)
  set_global :my_instance_data, instance_data
end

FunctionsFramework.http "tips_scopes" do |_request|
  # Per-function scope.
  # This method is called every time this function is called.
  invocation_data = perform_light_computation

  # Retrieve the data computed by the on_startup block.
  instance_data = global :my_instance_data

  "instance: #{instance_data}; function: #{invocation_data}"
end

You can use global variables as a performance optimization, but you must not rely on state set in the global scope by previous function invocations - see Statelessness for more information.

You can assume that for each function instance, the global scope has been executed exactly once before your function code is invoked. However, you must not depend on the total number of or timing of global scope executions, as they might vary depending on auto-scaling activity.

Function execution timeline

A function has access to its allocated resources (memory and CPU) only for the duration of function execution. Code run outside of the execution period is not guaranteed to execute, and it can be stopped at any time. Therefore, you should always signal the end of your function execution correctly and avoid running any code beyond it. See HTTP Functions, Background Functions, and CloudEvent Functions for guidance.

Function execution is also subject to the timeout duration of the function. See Function timeout for more information.

Take into account the execution timeline when initializing your application. Background tasks should not be created in global scope during initialization, as they would execute outside of the duration of a request.

Execution guarantees

Your functions are typically invoked once for each incoming event. However, Cloud Functions does not guarantee a single invocation in all cases because of differences in error scenarios.

The maximum or minimum number of times your function could be invoked for a single event depends on the type of your function:

  • HTTP functions are invoked at most once. This is because of the synchronous nature of HTTP calls, and it means that any error that occurs during function invocation will be returned without retrying. The caller of an HTTP function is expected to handle errors and retry if needed.

  • Event-driven functions are invoked at least once. This is because of the asynchronous nature of events, in which there is no caller that waits for the response. The system might, in rare circumstances, invoke an event-driven function more than once in order to ensure delivery of the event. If an event-driven function invocation fails with an error, the function will not be invoked again unless retries on failure are enabled for that function.

To make sure that your function behaves correctly on retried execution attempts, you should make it idempotent by implementing it so that the desired results (and side effects) are produced even if an event is delivered multiple times. In the case of HTTP functions, this also means returning the desired value even if the caller retries calls to the HTTP function endpoint. See Retrying Event-Driven Functions for more information on how to make your function idempotent.

Memory and file system

Each function has a certain amount of memory allocated for its use. You can configure the amount of memory at deployment - see Memory limits for more information.

The function execution environment includes an in-memory file system that contains the source files and directories deployed with your function (see Structuring source code). The directory containing your source files is read-only, but the rest of the file system is writeable (except for files used by the operating system). Use of the file system counts towards a function's memory usage.

Your function can interact with the file system using standard methods in each programming language.

Network

Your function can access the public internet using standard methods in each programming language, whether through built-in libraries offered by the runtime or third-party libraries you include as dependencies.

Try to reuse network connections across function invocations, as described in Optimizing Networking. However, note that a connection that remains unused for 10 minutes might be closed by the system, and further attempts to use a closed connection will result in a "connection reset" error. Your code should either use a library that handles closed connections well, or handle them explicitly if using low-level networking constructs.

Function isolation

Every deployed function is isolated from all other functions—even those deployed from the same source file. In particular, they do not share memory, global variables, file systems, or other state.

To share data across deployed functions, you can use services such as Memorystore, Datastore, Firestore, or Cloud Storage. Alternatively, you can invoke one function from another using their appropriate triggers and passing along the necessary data. For example, make an HTTP request to the endpoint of an HTTP function or publish a message to a Pub/Sub topic to trigger a Pub/Sub function.