While Google App Engine, the default implementation pattern of server-side Google Tag Manager, is straightforward to setup with the automatic provisioning steps, it’s certainly not the only way to deploy the server.

In fact, the manual setup guide gives you the details on how to deploy a Google Tag Manager Server in any environment that runs Docker.

Docker is a way to wrap an entire application (such as the GTM server) in a container, which can then be deployed easily with tools offered by most server platforms. Read more here.

In this guide, I’ll build on what Mark (brilliantly) wrote about by elaborating on a Cloud Run installation. To make matters easier, there’s a simple install script you can use to automate most of the process.


The Simmer Newsletter

Subscribe to the Simmer newsletter to get the latest news and content from Simo Ahava into your email inbox!

Tip #130: How to deploy a GTM Server using Cloud Run

Cloud Run is still fairly recent tech, as it entered general availability in late 2019. If Cloud Run had the feature set it has today, it’s likely that it would have been chosen as the native integration in Google Tag Manager instead of App Engine.

Here’s an App Engine vs. Cloud Run table I’ve compiled:

App Engine Cloud Run
Runs on Compute Engine instances. Runs on serverless containers.
No scale-to-zero (App Engine Flexible). Can be configured to only run when servicing requests.
Automatically compresses files. No automatic compression.
Adds some additional metadata to HTTP requests. Doesn’t modify the HTTP requests.
(Probably) more efficient for a large number of requests (>100 million / month). (Probably) more efficient for a smaller number of requests (<100 million / month).
Single-region, difficult to load balance. Can be setup with a multi-region network using a load balancer.
Established technology. Up-and-coming technology.

As you can see, the decision on which tech to choose is hardly an easy one. If you’re running a very large number of requests, it seems like App Engine might still have the edge, especially when you’re using auto-scaling and starting off with an appropriate minimum number of instances.

But if you’re processing a more modest request load and if you don’t specifically need compression out-of-the-box, you might enjoy what Cloud Run brings to the mix.

Anecdotal evidence

Anecdotes are rarely useful, but I’ll share them anyway. After switching from App Engine (3 instances, catering to ~5 million requests per month) to Cloud Run, this is what I found.

The daily cost of running three app engine instances (above) is just under 4 euros per day. This is the recommended setup of server-side Google Tag Manager.

Yes, I could have trivially scaled down to two or even just one instance to save in expenses, but traffic spikes can come without warning, and the cold boot time of an App Engine instance can be unbearably long.

App Engine can also run into resource exhaustion issues depending on region, where even the minimum number of instances cannot be met.

With on-demand Cloud Run the cost varies from day-to-day, but on an average week I am paying under 1 euro per day, so even less than what I would pay for a single instance App Engine setup (which would be risky in case of traffic spikes and the long cold boot time of App Engine instances).

As for latency, here’s what Cloud Run shows:

The corresponding values for App Engine were (taken from an arbitrary 5-day sample):

  • 50th percentile: 0.44s
  • 95th percentile: 0.87s
  • 99th percentile: 1.13s

I’m not sure why Cloud Run has better latency, considering it doesn’t compress files automatically, so gtm.js (Google Tag Manager) and gtag.js (Global Site Tag), for example, are sent fully uncompressed over the network.

I am of course happy with these results, but I’ll need more data before I can draw further conclusions.

And, again, the site in question has very modest traffic (just around five million requests per month). It would be interesting to see comparison data from different cohorts: dozens of millions of monthly requests, hundreds of millions of monthly requests, billions of monthly requests…

How to deploy Cloud Run

If you want to give Cloud Run a go, it’s very easy to do so. You need a Google Cloud Project where billing has been enabled.

In the project, fire up Cloud Shell by clicking the corresponding icon in the navigation bar.

Once the shell instance has started, run the following command:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/sahava/sgtm-cloud-run-shell/main/cr-script.sh)"

This executes a shell script I’ve written that walks you through the deployment steps.

  • Service Name – this is what the Cloud Run service will be called, with suffixes -debug for the debug service and -prod for the tagging service automatically added by the script. Choose whatever you like or use the recommended gtm-server by pressing enter.
  • Fetch existing service configuration – if you’ve already deployed a Cloud Run service, you can use this to fetch the existing configuration details from the region you’ll select next. This is helpful as it prefills all the subsequent settings in the shell script. You can skip this step by pressing enter.
  • Container Config - copy-paste the container config string from the Google Tag Manager Server container user interface by clicking the container ID in the top bar.
  • Policy Script URL - if you want to use a policy script to control what a server container can do, you can add the URL to the script here.
  • Memory per instance - choose how much memory is reserved per Cloud Run instance. Use the recommended 512Mi to mirror what an App Engine deployment would use.
  • CPU allocation per instance - choose how many CPUs are allocated per instance.
  • Minimum number of servers - choose how many instances to scale up to by default.
  • Maximum number of instances - choose the limit of how many instances the deployment can scale up to.

When you’re done, this is what you should see. If you’re happy with the setup, just type y and press enter to proceed.

If you haven’t enabled the Cloud Run API yet, at this point the script will do so for you.

Each Server container runs on two services: a debug service and a tagging service (called production service in the script). The debug service is solely used for Preview mode, and runs on very limited capacity. The tagging service is the one you configured in the steps above.

Deploy the debug service

The first thing you’ll need to choose is the region for the debug service.

You should choose a region that’s geographically closest to the people who will be doing the debugging. As I’m from Finland, I’ll choose the europe-north1 region for the debug service.

Once you’ve chosen the region, it’s going to take a moment for the service to deploy.

After deployment is complete, you should see a message like the one above. Press any key to proceed to deploy the tagging service.

Deploy the tagging service

You’ll need to choose a region for the tagging service, too. This time, select a region that’s closest to where the bulk of your visitors visit from (see more about multi-region setups below).

NOTE! If you want to map a custom domain to the tagging service, as you should, do note that custom domain mappings are available in only a limited set of regions.

By reducing the geographical distance between the machines that send the requests and your server that processes the requests, you’ll also minimize the cost of network egress that your service will need to process.

This does not need to be the same region as what you chose for the debug service!

As the bulk of my visitors come from the US, I’ve chosen us-central1 as the region of my tagging service.

Once done, you should see a screen like the one above. Click the link that ends with /healthy to see if your new service works. You should see a new page that simply has the message ok written on it.

Congratulations! You’ve setup a simple Cloud Run deployment that has one debug service and one production service.

You can now map a custom domain to the service. Note that you map the custom domain only to the tagging service. You will never need to directly access the debug service URL, as the tagging service automatically redirects requests sent in Preview mode to the debug service.

A few words on multi-region setups

The cool thing about Cloud Run is that you can create a number of tagging servers, each setup in a different region, and then add a load balancer in front of them that distributes the traffic automatically to the closest server region to where the request is coming from.

Note! You’ll only need one single debug service, though. All the tagging services can be configured to point to this one debug service. This is not currently supported by the shell script, but it’s a feature I’m going to add to it in the near future.

Setting up the load balancer is beyond the scope of this article, but I will definitely write a guide on how to do this in the future.

For the time being, you can check out the official documentation for a step-by-step.


Jumping on Cloud Run isn’t something I’d unconditionally recommend to everyone using server-side Google Tag Manager.

It still has unpolished edges, there are issues with general availability of some features, and measures to reduce latency (such as compression of files) require a lot of manual work.

Having said that, the general idea of Cloud Run makes so much more sense than App Engine does. App Engine relies on virtual machines reserved for use in Google Cloud.

These machines are in use whether or not they serve requests. This means that at any given time you are most likely running a lot of overhead in your setup and, in essence, paying for nothing.

With Cloud Run, you can setup your deployment to only run when it actually serves requests. The per-request cost is slightly more than an “always on” deployment, but if you have wild fluctuation in your traffic your overall cost might still be smaller.

Additionally, Cloud Run is the new, shiny thing in the Google Cloud Platform. It’s plausible that Google will put a lot of effort into developing it to replace some of App Engine’s use cases.

For now, being able to smoothly run multi-region deployments and to having more control over how requests are serviced by your cloud infrastructure might be enough to persuade enterprise customers to look into Cloud Run as the next development step of their server-side Google Tag Manager deployments.

Have you tried Cloud Run yet with server-side Google Tag Manager? What’s your experience been like?