Serve dynamic content and host microservices with Cloud Run

Pair Cloud Run with Firebase Hosting to generate and serve your dynamic content or build REST APIs as microservices.

Using Cloud Run , you can deploy an application packaged in a container image. Then, using Firebase Hosting , you can direct HTTPS requests to trigger your containerized app.

  • Cloud Run supports several languages (including Go, Node.js, Python, and Java), giving you the flexibility to use the programming language and framework of your choice.
  • Cloud Run automatically and horizontally scales your container image to handle the received requests, then scales down when demand decreases.
  • You only pay for the CPU, memory, and networking consumed during request handling.

For example use cases and samples for Cloud Run integrated with Firebase Hosting , visit our serverless overview .


This guide shows you how to:

  1. Write a simple Hello World application
  2. Containerize an app and upload it to Artifact Registry
  3. Deploy the container image to Cloud Run
  4. Direct Hosting requests to your containerized app

Note that to improve the performance of serving dynamic content, you can optionally tune your cache settings .

Before you begin

Before using Cloud Run , you need to complete some initial tasks, including setting up a Cloud Billing account, enabling the Cloud Run API, and installing the gcloud command line tool.

Set up billing for your project

Cloud Run offers free usage quota , but you still must have a Cloud Billing account associated with your Firebase project to use or try out Cloud Run .

Enable the API and install the SDK

  1. Enable the Cloud Run API in the Google APIs console:

    1. Open the Cloud Run API page in the Google APIs console.

    2. When prompted, select your Firebase project.

    3. Click Enableon the Cloud Run API page.

  2. Install and initialize the Cloud SDK.

  3. Check that the gcloud tool is configured for the correct project:

    gcloud config list

Step 1: Write the sample application

Note that Cloud Run supports many other languages in addition to the languages shown in the following sample.

Go

  1. Create a new directory named helloworld-go , then change directory into it:

    mkdir helloworld-go
    cd helloworld-go
  2. Create a new file named helloworld.go , then add the following code:

      package 
      
     main 
     import 
      
     ( 
      
     "fmt" 
      
     "log" 
      
     "net/http" 
      
     "os" 
     ) 
     func 
      
     handler 
     ( 
     w 
      
     http 
     . 
     ResponseWriter 
     , 
      
     r 
      
     * 
     http 
     . 
     Request 
     ) 
      
     { 
      
     log 
     . 
     Print 
     ( 
     "helloworld: received a request" 
     ) 
      
     target 
      
     := 
      
     os 
     . 
     Getenv 
     ( 
     "TARGET" 
     ) 
      
     if 
      
     target 
      
     == 
      
     "" 
      
     { 
      
     target 
      
     = 
      
     "World" 
      
     } 
      
     fmt 
     . 
     Fprintf 
     ( 
     w 
     , 
      
     "Hello %s!\n" 
     , 
      
     target 
     ) 
     } 
     func 
      
     main 
     () 
      
     { 
      
     log 
     . 
     Print 
     ( 
     "helloworld: starting server..." 
     ) 
      
     http 
     . 
     HandleFunc 
     ( 
     "/" 
     , 
      
     handler 
     ) 
      
     port 
      
     := 
      
     os 
     . 
     Getenv 
     ( 
     "PORT" 
     ) 
      
     if 
      
     port 
      
     == 
      
     "" 
      
     { 
      
     port 
      
     = 
      
     "8080" 
      
     } 
      
     log 
     . 
     Printf 
     ( 
     "helloworld: listening on port %s" 
     , 
      
     port 
     ) 
      
     log 
     . 
     Fatal 
     ( 
     http 
     . 
     ListenAndServe 
     ( 
     fmt 
     . 
     Sprintf 
     ( 
     ":%s" 
     , 
      
     port 
     ), 
      
     nil 
     )) 
     } 
      
     
    

    This code creates a basic web server that listens on the port defined by the PORT environment variable.

Your app is finished and ready to be containerized and uploaded to Artifact Registry .

Node.js

  1. Create a new directory named helloworld-nodejs , then change directory into it:

    mkdir helloworld-nodejs
    cd helloworld-nodejs
  2. Create a package.json file with the following contents:

      { 
      
     "name" 
     : 
      
     "knative-serving-helloworld" 
     , 
      
     "version" 
     : 
      
     "1.0.0" 
     , 
      
     "description" 
     : 
      
     "Simple hello world sample in Node" 
     , 
      
     "main" 
     : 
      
     "index.js" 
     , 
      
     "scripts" 
     : 
      
     { 
      
     "start" 
     : 
      
     "node index.js" 
      
     }, 
      
     "author" 
     : 
      
     "" 
     , 
      
     "license" 
     : 
      
     "Apache-2.0" 
     , 
      
     "dependencies" 
     : 
      
     { 
      
     "express" 
     : 
      
     "^4.21.2" 
      
     } 
     } 
      
     
    
  3. Create a new file named index.js , then add the following code:

      const 
      
     express 
      
     = 
      
     require 
     ( 
     'express' 
     ); 
     const 
      
     app 
      
     = 
      
     express 
     (); 
     app 
     . 
     get 
     ( 
     '/' 
     , 
      
     ( 
     req 
     , 
      
     res 
     ) 
      
     = 
    >  
     { 
      
     console 
     . 
     log 
     ( 
     'Hello world received a request.' 
     ); 
      
     const 
      
     target 
      
     = 
      
     process 
     . 
     env 
     . 
     TARGET 
      
     || 
      
     'World' 
     ; 
      
     res 
     . 
     send 
     ( 
     `Hello 
     ${ 
     target 
     } 
     !\n` 
     ); 
     }); 
     const 
      
     port 
      
     = 
      
     process 
     . 
     env 
     . 
     PORT 
      
     || 
      
     8080 
     ; 
     app 
     . 
     listen 
     ( 
     port 
     , 
      
     () 
      
     = 
    >  
     { 
      
     console 
     . 
     log 
     ( 
     'Hello world listening on port' 
     , 
      
     port 
     ); 
     }); 
      
     
    

    This code creates a basic web server that listens on the port defined by the PORT environment variable.

Your app is finished and ready to be containerized and uploaded to Artifact Registry .

Python

  1. Create a new directory named helloworld-python , then change directory into it:

    mkdir helloworld-python
    cd helloworld-python
  2. Create a new file named app.py , then add the following code:

      import 
      
     os 
     from 
      
     flask 
      
     import 
     Flask 
     app 
     = 
     Flask 
     ( 
     __name__ 
     ) 
     @app 
     . 
     route 
     ( 
     '/' 
     ) 
     def 
      
     hello_world 
     (): 
     target 
     = 
     os 
     . 
     environ 
     . 
     get 
     ( 
     'TARGET' 
     , 
     'World' 
     ) 
     return 
     'Hello 
     {} 
     ! 
     \n 
     ' 
     . 
     format 
     ( 
     target 
     ) 
     if 
     __name__ 
     == 
     "__main__" 
     : 
     app 
     . 
     run 
     ( 
     debug 
     = 
     True 
     , 
     host 
     = 
     '0.0.0.0' 
     , 
     port 
     = 
     int 
     ( 
     os 
     . 
     environ 
     . 
     get 
     ( 
     'PORT' 
     , 
     8080 
     ))) 
      
     
    

    This code creates a basic web server that listens on the port defined by the PORT environment variable.

Your app is finished and ready to be containerized and uploaded to Artifact Registry .

Java

  1. Install Java SE 8 or later JDK and CURL .

    Note that we only need to do this to create the new web project in the next step. The Dockerfile, which is described later, will load all dependencies into the container.

  2. From the console, create a new empty web project using cURL then unzip commands:

    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d name=helloworld \
        -d artifactId=helloworld \
        -o helloworld.zip
    unzip helloworld.zip

    This creates a SpringBoot project.

  3. Update the SpringBootApplication class in src/main/java/com/example/helloworld/HelloworldApplication.java by adding a @RestController to handle the / mapping and also add a @Value field to provide the TARGET environment variable:

      package 
      
     com.example.helloworld 
     ; 
     import 
      
     org.springframework.beans.factory.annotation.Value 
     ; 
     import 
      
     org.springframework.boot.SpringApplication 
     ; 
     import 
      
     org.springframework.boot.autoconfigure.SpringBootApplication 
     ; 
     import 
      
     org.springframework.web.bind.annotation.GetMapping 
     ; 
     import 
      
     org.springframework.web.bind.annotation.RestController 
     ; 
     @SpringBootApplication 
     public 
      
     class 
     HelloworldApplication 
      
     { 
      
     @Value 
     ( 
     "${TARGET:World}" 
     ) 
      
     String 
      
     target 
     ; 
      
     @RestController 
      
     class 
     HelloworldController 
      
     { 
      
     @GetMapping 
     ( 
     "/" 
     ) 
      
     String 
      
     hello 
     () 
      
     { 
      
     return 
      
     "Hello " 
      
     + 
      
     target 
      
     + 
      
     "!" 
     ; 
      
     } 
      
     } 
      
     public 
      
     static 
      
     void 
      
     main 
     ( 
     String 
     [] 
      
     args 
     ) 
      
     { 
      
     SpringApplication 
     . 
     run 
     ( 
     HelloworldApplication 
     . 
     class 
     , 
      
     args 
     ); 
      
     } 
     } 
      
     
    

    This code creates a basic web server that listens on the port defined by the PORT environment variable.

Your app is finished and ready to be containerized and uploaded to Artifact Registry .

Step 2: Containerize an app and upload it to Artifact Registry

  1. Containerize the sample app by creating a new file named Dockerfile in the same directory as the source files. Copy the following content into your file.

    Go

      # 
      
     Use 
      
     the 
      
     official 
      
     Golang 
      
     image 
      
     to 
      
     create 
      
     a 
      
     build 
      
     artifact 
     . 
     # 
      
     This 
      
     is 
      
     based 
      
     on 
      
     Debian 
      
     and 
      
     sets 
      
     the 
      
     GOPATH 
      
     to 
      
     / 
     go 
     . 
     FROM 
      
     golang 
     : 
     latest 
      
     AS 
      
     builder 
     ARG 
      
     TARGETOS 
     ARG 
      
     TARGETARCH 
     # 
      
     Create 
      
     and 
      
     change 
      
     to 
      
     the 
      
     app 
      
     directory 
     . 
     WORKDIR 
      
     / 
     app 
     # 
      
     Copy 
      
     local 
      
     code 
      
     to 
      
     the 
      
     container 
      
     image 
     . 
     COPY 
      
     . 
      
     . 
     / 
     # 
      
     Install 
      
     dependencies 
      
     and 
      
     tidy 
      
     up 
      
     the 
      
     go 
     . 
     mod 
      
     and 
      
     go 
     . 
     sum 
      
     files 
     . 
     RUN 
      
     go 
      
     mod 
      
     tidy 
     # 
      
     Build 
      
     the 
      
     binary 
     . 
     # 
      
     - 
     mod 
     = 
     readonly 
      
     ensures 
      
     immutable 
      
     go 
     . 
     mod 
      
     and 
      
     go 
     . 
     sum 
      
     in 
      
     container 
      
     builds 
     . 
     RUN 
      
     CGO_ENABLED 
     = 
     0 
      
     GOOS 
     = 
     $ 
     { 
     TARGETOS 
     } 
      
     GOARCH 
     = 
     $ 
     { 
     TARGETARCH 
     } 
      
     go 
      
     build 
      
     - 
     mod 
     = 
     readonly 
      
     - 
     v 
      
     - 
     o 
      
     server 
     # 
      
     Use 
      
     the 
      
     official 
      
     Alpine 
      
     image 
      
     for 
      
     a 
      
     lean 
      
     production 
      
     container 
     . 
     # 
      
     https 
     : 
     //hub.docker.com/_/alpine 
     # 
      
     https 
     : 
     //docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds 
     FROM 
      
     alpine 
     : 
     3 
     RUN 
      
     apk 
      
     add 
      
     -- 
     no 
     - 
     cache 
      
     ca 
     - 
     certificates 
     # 
      
     Copy 
      
     the 
      
     binary 
      
     to 
      
     the 
      
     production 
      
     image 
      
     from 
      
     the 
      
     builder 
      
     stage 
     . 
     COPY 
      
     -- 
     from 
     = 
     builder 
      
     / 
     app 
     / 
     server 
      
     / 
     server 
     # 
      
     Run 
      
     the 
      
     web 
      
     service 
      
     on 
      
     container 
      
     startup 
     . 
     CMD 
      
     [ 
     "/server" 
     ] 
      
     
    

    Node.js

      # 
      
     Use 
      
     the 
      
     official 
      
     lightweight 
      
     Node 
     . 
     js 
      
     12 
      
     image 
     . 
     # 
      
     https 
     : 
     //hub.docker.com/_/node 
     FROM 
      
     node 
     : 
     12 
     - 
     slim 
     # 
      
     Create 
      
     and 
      
     change 
      
     to 
      
     the 
      
     app 
      
     directory 
     . 
     WORKDIR 
      
     / 
     usr 
     / 
     src 
     / 
     app 
     # 
      
     Copy 
      
     application 
      
     dependency 
      
     manifests 
      
     to 
      
     the 
      
     container 
      
     image 
     . 
     # 
      
     A 
      
     wildcard 
      
     is 
      
     used 
      
     to 
      
     ensure 
      
     both 
      
     package 
     . 
     json 
      
     AND 
      
     package 
     - 
     lock 
     . 
     json 
      
     are 
      
     copied 
     . 
     # 
      
     Copying 
      
     this 
      
     separately 
      
     prevents 
      
     re 
     - 
     running 
      
     npm 
      
     install 
      
     on 
      
     every 
      
     code 
      
     change 
     . 
     COPY 
      
     package 
     * 
     . 
     json 
      
     . 
     / 
     # 
      
     Install 
      
     production 
      
     dependencies 
     . 
     RUN 
      
     npm 
      
     install 
      
     -- 
     only 
     = 
     production 
     # 
      
     Copy 
      
     local 
      
     code 
      
     to 
      
     the 
      
     container 
      
     image 
     . 
     COPY 
      
     . 
      
     . 
     / 
     # 
      
     Run 
      
     the 
      
     web 
      
     service 
      
     on 
      
     container 
      
     startup 
     . 
     CMD 
      
     [ 
      
     "npm" 
     , 
      
     "start" 
      
     ] 
      
     
    

    Python

      # Use the official lightweight Python image. 
     # https://hub.docker.com/_/python 
     FROM 
     python 
     : 
     3.7 
     - 
     slim 
     # Allow statements and log messages to immediately appear in the Knative logs 
     ENV 
     PYTHONUNBUFFERED 
     True 
     # Copy local code to the container image. 
     ENV 
     APP_HOME 
     / 
     app 
     WORKDIR 
     $ 
     APP_HOME 
     COPY 
     . 
     ./ 
     # Install production dependencies. 
     RUN 
     pip 
     install 
     Flask 
     gunicorn 
     # Run the web service on container startup. Here we use the gunicorn 
     # webserver, with one worker process and 8 threads. 
     # For environments with multiple CPU cores, increase the number of workers 
     # to be equal to the cores available. 
     CMD 
     exec 
     gunicorn 
     -- 
     bind 
     : 
     $ 
     PORT 
     -- 
     workers 
     1 
     -- 
     threads 
     8 
     -- 
     timeout 
     0 
     app 
     : 
     app 
      
     
    

    Java

      # 
      
     Use 
      
     the 
      
     official 
      
     maven 
     / 
     Java 
      
     8 
      
     image 
      
     to 
      
     create 
      
     a 
      
     build 
      
     artifact 
     : 
      
     https 
     : 
     //hub.docker.com/_/maven 
     FROM 
      
     maven 
     : 
     3.5 
     - 
     jdk 
     - 
     8 
     - 
     alpine 
      
     AS 
      
     builder 
     # 
      
     Copy 
      
     local 
      
     code 
      
     to 
      
     the 
      
     container 
      
     image 
     . 
     WORKDIR 
      
     / 
     app 
     COPY 
      
     pom 
     . 
     xml 
      
     . 
     COPY 
      
     src 
      
     . 
     / 
     src 
     # 
      
     Build 
      
     a 
      
     release 
      
     artifact 
     . 
     RUN 
      
     mvn 
      
     package 
      
     - 
     DskipTests 
     # 
      
     Use 
      
     the 
      
     Official 
      
     OpenJDK 
      
     image 
      
     for 
      
     a 
      
     lean 
      
     production 
      
     stage 
      
     of 
      
     our 
      
     multi 
     - 
     stage 
      
     build 
     . 
     # 
      
     https 
     : 
     //hub.docker.com/_/openjdk 
     # 
      
     https 
     : 
     //docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds 
     FROM 
      
     openjdk 
     : 
     8 
     - 
     jre 
     - 
     alpine 
     # 
      
     Copy 
      
     the 
      
     jar 
      
     to 
      
     the 
      
     production 
      
     image 
      
     from 
      
     the 
      
     builder 
      
     stage 
     . 
     COPY 
      
     -- 
     from 
     = 
     builder 
      
     / 
     app 
     / 
     target 
     / 
     helloworld 
     -* 
     . 
     jar 
      
     / 
     helloworld 
     . 
     jar 
     # 
      
     Run 
      
     the 
      
     web 
      
     service 
      
     on 
      
     container 
      
     startup 
     . 
     CMD 
      
     [ 
     "java" 
     , 
      
     "-Djava.security.egd=file:/dev/./urandom" 
     , 
      
     "-jar" 
     , 
      
     "/helloworld.jar" 
     ] 
      
     
    
  2. Build your container image using Cloud Build by running the following command from the directory containing your Dockerfile:

    gcloud builds submit --tag gcr.io/ PROJECT_ID 
    /helloworld

    Upon success, you will see a SUCCESS message containing the image name
    ( gcr.io/ PROJECT_ID /helloworld ).

The container image is now stored in Artifact Registry and can be re-used if desired.

Note that, instead of Cloud Build , you can use a locally installed version of Docker to build your container locally .

Step 3: Deploy the container image to Cloud Run

Allowed Cloud Run regions

For the best performance, colocate your Cloud Run service with Hosting using the following regions:

  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1

Rewrites to Cloud Run from Hosting are supported in the following regions:

  • asia-east1
  • asia-east2
  • asia-northeast1
  • asia-northeast2
  • asia-northeast3
  • asia-south1
  • asia-south2
  • asia-southeast1
  • asia-southeast2
  • australia-southeast1
  • australia-southeast2
  • europe-central2
  • europe-north1
  • europe-southwest1
  • europe-west1
  • europe-west12
  • europe-west2
  • europe-west3
  • europe-west4
  • europe-west6
  • europe-west8
  • europe-west9
  • me-central1
  • me-west1
  • northamerica-northeast1
  • northamerica-northeast2
  • southamerica-east1
  • southamerica-west1
  • us-central1
  • us-east1
  • us-east4
  • us-east5
  • us-south1
  • us-west1
  • us-west2
  • us-west3
  • us-west4
  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1
  1. Deploy using the following command:

    gcloud run deploy --image gcr.io/ PROJECT_ID 
    /helloworld
  2. When prompted:

  3. Wait a few moments for the deploy to complete. On success, the command line displays the service URL. For example: https://helloworld- RANDOM_HASH -us-central1.a.run.app

  4. Visit your deployed container by opening the service URL in a web browser.

The next step walks you through how to access this containerized app from a Firebase Hosting URL so that it can generate dynamic content for your Firebase-hosted site.

Step 4:Direct hosting requests to your containerized app

With rewrite rules , you can direct requests that match specific patterns to a single destination.

The following example shows how to direct all requests from the page /helloworld on your Hosting site to trigger the startup and running of your helloworld container instance.

  1. Make sure that:

    For detailed instructions about installing the CLI and initializing Hosting , see the Get Started guide for Hosting .

  2. Open your firebase.json file .

  3. Add the following rewrite configuration under the hosting section:

     "hosting" 
     : 
      
     { 
      
     // ... 
      
     // Add the "rewrites" attribute within "hosting" 
      
     "rewrites" 
     : 
      
     [ 
      
     { 
      
     "source" 
     : 
      
     "/helloworld" 
     , 
      
     "run" 
     : 
      
     { 
      
     "serviceId" 
     : 
      
     "helloworld" 
     , 
      
     // "service name" (from when you deployed the container image 
    ) 
      
     "region" 
     : 
      
     "us-central1" 
     , 
      
     // optional (if omitted, default is us-central1) 
      
     "pinTag" 
     : 
      
     true 
      
     // optional (see note below) 
      
     } 
      
     } 
      
     ] 
     } 
    
  4. Deploy your hosting configuration to your site by running the following command from the root of your project directory:

    firebase deploy --only hosting

How pinTag works within the run block

With this feature, you can ensure that the revision of your Cloud Run service for generating your site's dynamic content is kept in sync with your static Hosting resources and Hosting config. Also, this feature allows you to preview your rewrites to Cloud Run on Hosting preview channels.

If you add "pinTag": true to a run block of the hosting.rewrites config, your static Hosting resources and configuration will be pinned to the most recent revision of the Cloud Run service, at the time of deploy. If you roll back a version of your site, the revision of the "pinned" Cloud Run service is also rolled back.

This feature relies on Cloud Run tags , which have a limit of 1000 tags per service and 2000 tags per region. This means that after hundreds of deploys, the oldest versions of a site may stop working.

Your container is now reachable via the following URLs:

  • Your Firebase subdomains:
    PROJECT_ID .web.app/ and PROJECT_ID .firebaseapp.com/

  • Any connected custom domains :
    CUSTOM_DOMAIN /

Visit the Hosting configuration page for more details about rewrite rules . You can also learn about the priority order of responses for various Hosting configurations.

Test locally

During development, you can run and test your container image locally. For detailed instructions, visit the Cloud Run documentation .

Next steps

Design a Mobile Site
View Site in Mobile | Classic
Share by: