Using system packages tutorial Stay organized with collections Save and categorize content based on your preferences.
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.
Before you begin
- 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.
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
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.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.createpermission.Learn how to grant roles.
Verify that billing is enabled for your Google Cloud project.
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
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.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.createpermission.Learn how to grant roles.
Verify that billing is enabled for your Google Cloud project.
- Enable the Cloud Run Admin API
- Install and initialize the gcloud CLI.
- 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:
- Cloud Build Editor (
roles/cloudbuild.builds.editor) - Cloud Run Admin (
roles/run.admin) - Create Service Accounts (
roles/iam.serviceAccountCreator) - Service Account User (
roles/iam.serviceAccountUser) - Service Usage Consumer (
roles/serviceusage.serviceUsageConsumer) - Storage Admin (
roles/storage.admin)
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:
Set your default project:
gcloudconfigsetprojectPROJECT_IDReplacePROJECT_ID with the name of the project you created forthis tutorial.
Configure gcloud for your chosen region:
gcloudconfigsetrun/regionREGIONReplaceREGION 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)Low CO2
europe-north2(Stockholm)Low CO2
europe-southwest1(Madrid)Low CO2
europe-west1(Belgium)Low CO2
europe-west4(Netherlands)Low CO2
europe-west8(Milan)europe-west9(Paris)Low CO2
me-west1(Tel Aviv)northamerica-south1(Mexico)us-central1(Iowa)Low CO2
us-east1(South Carolina)us-east4(Northern Virginia)us-east5(Columbus)us-south1(Dallas)Low CO2
us-west1(Oregon)Low 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)Low CO2
europe-west3(Frankfurt, Germany)europe-west6(Zurich, Switzerland)Low CO2
me-central1(Doha)me-central2(Dammam)northamerica-northeast1(Montreal)Low CO2
northamerica-northeast2(Toronto)Low CO2
southamerica-east1(Sao Paulo, Brazil)Low CO2
southamerica-west1(Santiago, Chile)Low 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:
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.
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:

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.
Open the
Dockerfilein an editor.Look for a
DockerfileRUNstatement. This statement allows running arbitrary shell commands to modifythe environment. If theDockerfilehas multiple stages, identified byfinding multipleFROMstatements, 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-getcleanAlpine Alpine requires a second package for font support.RUNapk--no-cacheaddgraphvizTo determine the operating system of your container image, check the name inthe
FROMstatement or a README associated with your base image. For example,if you extend fromnode, you can find documentation and the parentDockerfileonDocker Hub.Test your customization by building the image, using
docker buildlocally 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",500Go
// 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")returnimageGo
// 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:
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.
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, and
graphvizis 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, and
graphvizis 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, and
graphvizis 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.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-getcleangcloudbuildssubmit--tagREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz-base
WherePROJECT_ID is your Google Cloud project ID.
Use thegcloud credential helperto authorize Docker to push to your Artifact Registry.
gcloudauthconfigure-docker
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.
Deploy using the following:
gcloud
- 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.
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.gcloudiamservice-accountscreateSA_NAME
- Deploy the code, specifying the service account.
WherePROJECT_ID is your Google Cloud project ID,SA_NAME isthe name of the service account you created, andgcloudrundeploygraphviz-web--service-accountSA_NAME@PROJECT_ID.iam.gserviceaccount.com--imageREGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphviz
graphvizis thename of the container from above andgraphviz-webis the name of theservice.RespondYto the "allow unauthenticated"prompt. SeeManaging access for moredetails on IAM-based authentication. - 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"}- 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.
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.
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 }"/>
Open the resulting
diagram.pngfile in any application that supportsPNGfiles, such as Chrome.It should look like this:

Source: DOT Description
You can explore a small collection ofready-made diagram descriptions.
- Copy the contents of the selected
.dotfile Send an HTTP request to your service.
Copy the URL into your browser URL bar
Success: You deployed a custom Cloud Run service that transformsa graph description input parameter into a diagram in thehttps://SERVICE_DOMAIN/diagram.png?dot=SELECTEDDOTFILECONTENTSPNGimage 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:
Delete tutorial resources
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.
Remove the
gclouddefault region configuration you added during tutorialsetup:gcloudconfigunsetrun/regionRemove the project configuration:
gcloud config unset projectDelete other Google Cloud resources created in this tutorial:
Delete the container image named
REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/graphvizfrom Artifact Registry.Delete the service accountSA_NAME.
gcloudiamservice-accountsdeleteSA_NAME@PROJECT_ID.iam.gserviceaccount.com
What's next
- Experiment with your graphviz app:
- Add support for other graphviz utilities which apply different algorithms todiagram generation.
- Save diagrams toCloud Storage. Do you want to save the imageor the DOT syntax?
- Implement content abuse protection withCloud Natural Language API.
- See another example of a system package in theImage Processing with Cloud Run tutorial.
- Explore reference architectures, diagrams, and best practices about Google Cloud.Take a look at ourCloud Architecture Center.
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.