Using system packages tutorial

This tutorial shows how to build a customCloud Run servicethat transforms a graph description input parameter into a diagram in thePNGimage format. It usesGraphviz andis installed as a system package in the service's container environment.Graphviz is used via command-line utilities to serve requests.

Objectives

  • Write and build acustom container with aDockerfile
  • Write, build, and deploy a Cloud Run service
  • UseGraphviz dot utility to generate diagrams
  • Test the service by posting a DOT syntax diagram from the collection or your own creation

Costs

In this document, you use the following billable components of Google Cloud:

To generate a cost estimate based on your projected usage, use thepricing calculator.

New Google Cloud users might be eligible for afree trial.

Before you begin

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.create permission.Learn how to grant roles.
    Note: If you don't plan to keep the resources that you create in this procedure, create a project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.create permission.Learn how to grant roles.
    Note: If you don't plan to keep the resources that you create in this procedure, create a project instead of selecting an existing project. After you finish these steps, you can delete the project, removing all resources associated with the project.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

  6. Enable the Cloud Run Admin API
  7. Install and initialize the gcloud CLI.
  8. Update components:
    gcloudcomponentsupdate

Required roles

To get the permissions that you need to complete the tutorial, ask your administrator to grant you the following IAM roles on your project:

For more information about granting roles, seeManage access to projects, folders, and organizations.

You might also be able to get the required permissions throughcustom roles or otherpredefined roles.

Note:IAM basic roles might also contain permissions to complete the tutorial. You shouldn't grant basic roles in a production environment, but you can grant them in a development or test environment.

Setting up gcloud defaults

To configure gcloud with defaults for your Cloud Run service:

  1. Set your default project:

    gcloudconfigsetprojectPROJECT_ID

    ReplacePROJECT_ID with the name of the project you created forthis tutorial.

  2. Configure gcloud for your chosen region:

    gcloudconfigsetrun/regionREGION

    ReplaceREGION with the supported Cloud Runregionof your choice.

Cloud Run locations

Cloud Run is regional, which means the infrastructure thatruns your Cloud Run services is located in a specific region and ismanaged by Google to be redundantly available acrossall the zones within that region.

Meeting your latency, availability, or durability requirements are primaryfactors for selecting the region where your Cloud Run services are run.You can generally select the region nearest to your users but you should considerthe location of theother Google Cloudproducts that are used by your Cloud Run service.Using Google Cloud products together across multiple locations can affectyour service's latency as well as cost.

Cloud Run is available in the following regions:

Subject toTier 1 pricing

  • asia-east1 (Taiwan)
  • asia-northeast1 (Tokyo)
  • asia-northeast2 (Osaka)
  • asia-south1 (Mumbai, India)
  • asia-southeast3 (Bangkok)
  • europe-north1 (Finland)leaf iconLow CO2
  • europe-north2 (Stockholm)leaf iconLow CO2
  • europe-southwest1 (Madrid)leaf iconLow CO2
  • europe-west1 (Belgium)leaf iconLow CO2
  • europe-west4 (Netherlands)leaf iconLow CO2
  • europe-west8 (Milan)
  • europe-west9 (Paris)leaf iconLow CO2
  • me-west1 (Tel Aviv)
  • northamerica-south1 (Mexico)
  • us-central1 (Iowa)leaf iconLow CO2
  • us-east1 (South Carolina)
  • us-east4 (Northern Virginia)
  • us-east5 (Columbus)
  • us-south1 (Dallas)leaf iconLow CO2
  • us-west1 (Oregon)leaf iconLow CO2

Subject toTier 2 pricing

  • africa-south1 (Johannesburg)
  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seoul, South Korea)
  • asia-southeast1 (Singapore)
  • asia-southeast2 (Jakarta)
  • asia-south2 (Delhi, India)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Warsaw, Poland)
  • europe-west10 (Berlin)
  • europe-west12 (Turin)
  • europe-west2 (London, UK)leaf iconLow CO2
  • europe-west3 (Frankfurt, Germany)
  • europe-west6 (Zurich, Switzerland)leaf iconLow CO2
  • me-central1 (Doha)
  • me-central2 (Dammam)
  • northamerica-northeast1 (Montreal)leaf iconLow CO2
  • northamerica-northeast2 (Toronto)leaf iconLow CO2
  • southamerica-east1 (Sao Paulo, Brazil)leaf iconLow CO2
  • southamerica-west1 (Santiago, Chile)leaf iconLow CO2
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

If you already created a Cloud Run service, you can view theregion in the Cloud Run dashboard in theGoogle Cloud console.

Retrieving the code sample

To retrieve the code sample for use:

  1. Clone the sample app repository to your local machine:

    Node.js

    gitclonehttps://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Python

    gitclonehttps://github.com/GoogleCloudPlatform/python-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Go

    gitclonehttps://github.com/GoogleCloudPlatform/golang-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Java

    gitclonehttps://github.com/GoogleCloudPlatform/java-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

  2. Change to the directory that contains the Cloud Run samplecode:

    Node.js

    cdnodejs-docs-samples/run/system-package/

    Python

    cdpython-docs-samples/run/system-package/

    Go

    cdgolang-samples/run/system_package/

    Java

    cdjava-docs-samples/run/system-package/

Visualizing the architecture

The basic architecture looks like this:

Diagram showing request flow from user to web service to graphviz dot    utility.
For the diagram source, see the DOT Description

The user makes an HTTP request to the Cloud Run service which executesa Graphviz utility to transform the request into an image. That image isdelivered to the user as the HTTP response.

Understanding the code

Defining your environment configuration with theDockerfile

Key Point: Use yourDockerfile to customize your environment with libraries,utilities, environment variables, configuration files, and startup processes.This is the container-based approach for "server configuration".

YourDockerfile is specific to the language and base operating environment,such as Ubuntu, that your service will use.

This service requires one or more additional system packages notavailable by default.

  1. Open theDockerfile in an editor.

  2. Look for aDockerfileRUN statement. This statement allows running arbitrary shell commands to modifythe environment. If theDockerfile has multiple stages, identified byfinding multipleFROM statements, it will be found in the last stage.

    The specific packages required and the mechanism to install them varies bythe operating system declared inside the container.

    To get instructions for your operating system or base image, click theappropriate tab.

    Debian/Ubuntu
    RUNapt-getupdate-y &&apt-getinstall-y\graphviz\  &&apt-getclean
    Alpine
    Alpine requires a second package for font support.
    RUNapk--no-cacheaddgraphviz

    To determine the operating system of your container image, check the name intheFROM statement or a README associated with your base image. For example,if you extend fromnode, you can find documentation and the parentDockerfile onDocker Hub.

  3. Test your customization by building the image, usingdocker build locally orCloud Build.

Handling incoming requests

The sample service uses parameters from the incoming HTTP request to invoke asystem call that executes the appropriatedot utility command.

In the HTTP handler below, a graph description input parameter is extracted fromthedot querystring variable.

Graph descriptions can include characters which must beURL encoded for use in a querystring.

Node.js

app.get('/diagram.png',(req,res)=>{try{constimage=createDiagram(req.query.dot);res.setHeader('Content-Type','image/png');res.setHeader('Content-Length',image.length);res.setHeader('Cache-Control','public, max-age=86400');res.send(image);}catch(err){console.error(`error:${err.message}`);consterrDetails=(err.stderr||err.message).toString();if(errDetails.includes('syntax')){res.status(400).send(`Bad Request:${err.message}`);}else{res.status(500).send('Internal Server Error');}}});

Python

@app.route("/diagram.png",methods=["GET"])defindex():"""Takes an HTTP GET request with query param dot and    returns a png with the rendered DOT diagram in a HTTP response.    """try:image=create_diagram(request.args.get("dot"))response=make_response(image)response.headers.set("Content-Type","image/png")returnresponseexceptExceptionase:print(f"error:{e}")# If no graphviz definition or bad graphviz def, return 400if"syntax"instr(e):returnf"Bad Request:{e}",400return"Internal Server Error",500

Go

// diagramHandler renders a diagram using HTTP request parameters and the dot command.funcdiagramHandler(whttp.ResponseWriter,r*http.Request){ifr.Method!=http.MethodGet{log.Printf("method not allowed: %s",r.Method)http.Error(w,fmt.Sprintf("HTTP Method %s Not Allowed",r.Method),http.StatusMethodNotAllowed)return}q:=r.URL.Query()dot:=q.Get("dot")ifdot==""{log.Print("no graphviz definition provided")http.Error(w,"Bad Request",http.StatusBadRequest)return}// Cache header must be set before writing a response.w.Header().Set("Cache-Control","public, max-age=86400")input:=strings.NewReader(dot)iferr:=createDiagram(w,input);err!=nil{log.Printf("createDiagram: %v",err)// Do not cache error responses.w.Header().Del("Cache-Control")ifstrings.Contains(err.Error(),"syntax"){http.Error(w,"Bad Request: DOT syntax error",http.StatusBadRequest)}else{http.Error(w,"Internal Server Error",http.StatusInternalServerError)}}}

Java

get("/diagram.png",(req,res)->{InputStreamimage=null;try{Stringdot=req.queryParams("dot");image=createDiagram(dot);res.header("Content-Type","image/png");res.header("Content-Length",Integer.toString(image.available()));res.header("Cache-Control","public, max-age=86400");}catch(Exceptione){if(e.getMessage().contains("syntax")){res.status(400);returnString.format("Bad Request: %s",e.getMessage());}else{res.status(500);return"Internal Server Error";}}returnimage;});

You'll need to differentiate between internal server errors and invalid userinput. This sample service returns an Internal Server Error for all dotcommand-line errors unless the error message contains the stringsyntax, whichindicates a user input problem.

Generating a diagram

The core logic of diagram generation uses the dot command-line tool to processthe graph description input parameter into a diagram in the PNG image format.

Node.js

// Generate a diagram based on a graphviz DOT diagram description.constcreateDiagram=dot=>{if(!dot){thrownewError('syntax: no graphviz definition provided');}// Adds a watermark to the dot graphic.constdotFlags=['-Glabel="Made on Cloud Run"','-Gfontsize=10','-Glabeljust=right','-Glabelloc=bottom','-Gfontcolor=gray',].join(' ');constimage=execSync(`/usr/bin/dot${dotFlags} -Tpng`,{input:dot,});returnimage;};

Python

defcreate_diagram(dot):"""Generates a diagram based on a graphviz DOT diagram description.    Args:        dot: diagram description in graphviz DOT syntax    Returns:        A diagram in the PNG image format.    """ifnotdot:raiseException("syntax: no graphviz definition provided")dot_args=[# These args add a watermark to the dot graphic."-Glabel=Made on Cloud Run","-Gfontsize=10","-Glabeljust=right","-Glabelloc=bottom","-Gfontcolor=gray","-Tpng",]# Uses local `dot` binary from Graphviz:# https://graphviz.gitlab.ioimage=subprocess.run(["dot"]+dot_args,input=dot.encode("utf-8"),stdout=subprocess.PIPE).stdoutifnotimage:raiseException("syntax: bad graphviz definition provided")returnimage

Go

// createDiagram generates a diagram image from the provided io.Reader written to the io.Writer.funccreateDiagram(wio.Writer,rio.Reader)error{stderr:=new(bytes.Buffer)args:=[]string{"-Glabel=Made on Cloud Run","-Gfontsize=10","-Glabeljust=right","-Glabelloc=bottom","-Gfontcolor=gray","-Tpng",}cmd:=exec.Command("/usr/bin/dot",args...)cmd.Stdin=rcmd.Stdout=wcmd.Stderr=stderriferr:=cmd.Run();err!=nil{returnfmt.Errorf("exec(%s) failed (%w): %s",cmd.Path,err,stderr.String())}returnnil}

Java

// Generate a diagram based on a graphviz DOT diagram description.publicstaticInputStreamcreateDiagram(Stringdot){if(dot==null||dot.isEmpty()){thrownewNullPointerException("syntax: no graphviz definition provided");}// Adds a watermark to the dot graphic.List<String>args=newArrayList<>();args.add("/usr/bin/dot");args.add("-Glabel=\"Made on Cloud Run\"");args.add("-Gfontsize=10");args.add("-Glabeljust=right");args.add("-Glabelloc=bottom");args.add("-Gfontcolor=gray");args.add("-Tpng");StringBuilderoutput=newStringBuilder();InputStreamstdout=null;try{ProcessBuilderpb=newProcessBuilder(args);Processprocess=pb.start();OutputStreamstdin=process.getOutputStream();stdout=process.getInputStream();// The Graphviz dot program reads from stdin.Writerwriter=newOutputStreamWriter(stdin,"UTF-8");writer.write(dot);writer.close();process.waitFor();}catch(Exceptione){System.out.println(e);}returnstdout;}

Designing a secure service

Any vulnerabilities in thedot tool are potential vulnerabilities ofthe web service. You can mitigate this by using up-to-date versions of thegraphviz package through re-building the container image on a regular basis.

If you extend the current sample to accept user input as command-lineparameters, you should protect againstcommand-injection attacks. Some ofthe ways to prevent injection attacks include:

  • Mapping inputs to a dictionary of supported parameters
  • Validating inputs match a range of known-safe values, perhaps using regularexpressions
  • Escaping inputs to ensure shell syntax is not evaluated

You can further mitigate potential vulnerabilities by deploying the servicewith a service account that has not been granted any permissions to use Google Cloudservices, rather than using the default account, which has commonly usedpermissions. For that reason, the steps in this tutorialcreate and use a new service account.

Shipping the code

To ship your code, you build with Cloud Build, and upload toArtifact Registry, and deploy to Cloud Run:

  1. Create an Artifact Registry:

    gcloudartifactsrepositoriescreateREPOSITORY\--repository-formatdocker\--locationREGION

    Replace:

    • REPOSITORY: a unique name for the repository. For each repository location in a project, repository names must be unique.
    • REGION: the Google Cloud region to be used for theArtifact Registry repository.
  2. Run the following command to build your container and publish onArtifact Registry.

    Node.js

    gcloudbuildssubmit--tagREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz

    WherePROJECT_ID is your Google Cloud project ID, andgraphviz is thename you want to give your service.

    Upon success, you will see a SUCCESS message containing the ID, creationtime, and image name. The image is stored in Artifact Registry and can bere-used if desired.

    Python

    gcloudbuildssubmit--tagREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz

    WherePROJECT_ID is your Google Cloud project ID, andgraphviz is thename you want to give your service.

    Upon success, you will see a SUCCESS message containing the ID, creationtime, and image name. The image is stored in Artifact Registry and can bereused if desired.

    Go

    gcloudbuildssubmit--tagREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz

    WherePROJECT_ID is your Google Cloud project ID, andgraphviz is thename you want to give your service.

    Upon success, you will see a SUCCESS message containing the ID, creationtime, and image name. The image is stored in Artifact Registry and can bereused if desired.

    Java

    This sample usesJib to buildDocker images using common Java tools. Jib optimizes container builds withoutthe need for a Dockerfile or havingDockerinstalled. Learn more aboutbuilding Java containers with Jib.

    1. Using the Dockerfile, configure and build a base image with the systempackages installed to override Jib's default base image:

      #UsetheOfficialeclipse-temurinimageforaleanproductionstageofourmulti-stagebuild.#https://hub.docker.com/_/eclipse-temurin/FROMeclipse-temurin:17.0.16_8-jreRUNapt-getupdate-y &&apt-getinstall-y\graphviz\  &&apt-getclean
      gcloudbuildssubmit--tagREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz-base

      WherePROJECT_ID is your Google Cloud project ID.

    2. Use thegcloud credential helperto authorize Docker to push to your Artifact Registry.

      gcloudauthconfigure-docker

    3. Build your final container with Jib and publish on Artifact Registry:

      <plugin><groupId>com.google.cloud.tools</groupId><artifactId>jib-maven-plugin</artifactId><version>3.4.0</version><configuration><from><image>gcr.io/PROJECT_ID/graphviz-base</image></from><to><image>gcr.io/PROJECT_ID/graphviz</image></to></configuration></plugin>
      mvncompilejib:build\-Dimage=REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz\-Djib.from.image=REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz-base

      WherePROJECT_ID is your Google Cloud project ID.

  3. Deploy using the following:

    gcloud

    1. Create a new service account. Your code, including any system packagesit uses, can use only thoseGoogle Cloud services that have been granted to this service account.
      gcloudiamservice-accountscreateSA_NAME
      WhereSA_NAME is a name you give this service account. If there isan error or a vulnerability in your code, your code will not be able to access anyof your other Google Cloud project resources.
    2. Deploy the code, specifying the service account.
      gcloudrundeploygraphviz-web--service-accountSA_NAME@PROJECT_ID.iam.gserviceaccount.com--imageREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz
      WherePROJECT_ID is your Google Cloud project ID,SA_NAME isthe name of the service account you created, andgraphviz is thename of the container from above andgraphviz-web is the name of theservice.RespondY to the "allow unauthenticated"prompt. SeeManaging access for moredetails on IAM-based authentication.
    3. Wait for the deployment to finish. This can take about half a minute.On success, the command line displays the service URL.

    Terraform

    To learn how to apply or remove a Terraform configuration, seeBasic Terraform commands.

    The following Terraform code creates a Cloud Run service.

    resource"google_service_account""graphviz"{account_id="graphviz"display_name="GraphViz Tutorial Service Account"}resource"google_cloud_run_v2_service""default"{name="graphviz-example"location="us-central1"deletion_protection=false # set to "true" in productiontemplate{containers{      # Replace with the URL of your graphviz image      #   gcr.io/<YOUR_GCP_PROJECT_ID>/graphvizimage="us-docker.pkg.dev/cloudrun/container/hello"}service_account=google_service_account.graphviz.email}}

    ReplaceIMAGE_URL with a reference to the container image, forexample,us-docker.pkg.dev/cloudrun/container/hello:latest. If you use Artifact Registry,therepositoryREPO_NAME mustalready be created. The URL follows the format ofLOCATION-docker.pkg.dev/PROJECT_ID/REPO_NAME/PATH:TAG.

    The following Terraform code makes your Cloud Run service public.

    # Make Cloud Run service publicly accessibleresource"google_cloud_run_service_iam_member""allow_unauthenticated"{service=google_cloud_run_v2_service.default.namelocation=google_cloud_run_v2_service.default.locationrole="roles/run.invoker"member="allUsers"}
  4. If you want to deploy a code update to the service, repeat the previoussteps. Each deployment to a service creates a new revision and automaticallystarts serving traffic when ready.

Try it out

Try out your service by sending HTTPPOST requests with DOT syntaxdescriptions in the request payload.

  1. Send an HTTP request to your service.

    Copy the URL into your browser URL bar and update[SERVICE_DOMAIN]:

    https://SERVICE_DOMAIN/diagram.png?dot=digraphRun{rankdir=LRCode->Build->Deploy->Run}

    You can embed the diagram in a web page:

    <imgsrc="https://SERVICE_DOMAIN/diagram.png?dot=digraph Run { rankdir=LR Code -> Build -> Deploy -> Run }"/>
  2. Open the resultingdiagram.png file in any application that supportsPNGfiles, such as Chrome.

    It should look like this:

    Diagram showing the stage flow  of Code to Build to Deploy to 'Run'.
    Source: DOT Description

You can explore a small collection ofready-made diagram descriptions.

  1. Copy the contents of the selected.dot file
  2. Send an HTTP request to your service.

    Copy the URL into your browser URL bar

    https://SERVICE_DOMAIN/diagram.png?dot=SELECTEDDOTFILECONTENTS
    Success: You deployed a custom Cloud Run service that transformsa graph description input parameter into a diagram in thePNG image format.

Clean up

To avoid additional charges to your Google Cloud account, delete all the resourcesyou deployed with this tutorial.

Delete the project

If you created a new project for this tutorial, delete the project.If you used an existing project and need to keep it without the changes you addedin this tutorial,delete resources that you created for the tutorial.

The easiest way to eliminate billing is to delete the project that you created for the tutorial.

To delete the project:

    Caution: Deleting a project has the following effects:
    • Everything in the project is deleted. If you used an existing project for the tasks in this document, when you delete it, you also delete any other work you've done in the project.
    • Custom project IDs are lost. When you created this project, you might have created a custom project ID that you want to use in the future. To preserve the URLs that use the project ID, such as anappspot.com URL, delete selected resources inside the project instead of deleting the whole project.

    If you plan to explore multiple architectures, tutorials, or quickstarts, reusing projects can help you avoid exceeding project quota limits.

  1. In the Google Cloud console, go to theManage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then clickDelete.
  3. In the dialog, type the project ID, and then clickShut down to delete the project.

Delete tutorial resources

  1. Delete the Cloud Run service you deployed in this tutorial.Cloud Run services don't incur costs until they receive requests.

    To delete your Cloud Run service, run the following command:

    gcloudrunservicesdeleteSERVICE-NAME

    ReplaceSERVICE-NAME with the name of your service.

    You can also delete Cloud Run services from theGoogle Cloud console.

  2. Remove thegcloud default region configuration you added during tutorialsetup:

    gcloudconfigunsetrun/region
  3. Remove the project configuration:

     gcloud config unset project
  4. Delete other Google Cloud resources created in this tutorial:

    • Delete the container image namedREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz from Artifact Registry.

    • Delete the service accountSA_NAME.

      gcloudiamservice-accountsdeleteSA_NAME@PROJECT_ID.iam.gserviceaccount.com

What's next

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026-02-18 UTC.