← Back to blog Cloud Architecture

Server-Side GTM on Google Cloud: An End-to-End Setup Guide

Sebastian Pop October 28, 2025 7 min read
Share
Server-Side GTM on Google Cloud: An End-to-End Setup Guide

Most guides to server-side Google Tag Manager stop at “click the auto-provision button”. That button is fine for a proof of concept and wrong for production: it spins up a GCP project you do not control, in a region you did not choose, with defaults that will quietly cost you money and lose you data. ...

Most guides to server-side Google Tag Manager stop at “click the auto-provision button”. That button is fine for a proof of concept and wrong for production: it spins up a GCP project you do not control, in a region you did not choose, with defaults that will quietly cost you money and lose you data. This guide does it the other way, in your own project, with the commands, the sizing decisions and the traps.

We assume you know what server-side tagging is for. If you need the why first, our post on server-side tag management covers the case. This one is the how.

The architecture, in one paragraph

Your browser stops talking to Google Analytics, Meta and everyone else directly. Instead it sends one request to a subdomain you own, which resolves to a container running on Cloud Run. That container runs a GTM server container: it receives the request, turns it into an event, and fans it out to whichever destinations you configure, server to server. You gain first-party cookie durability, control over what leaves your infrastructure, and a lighter page. You take on an always-on service to operate and pay for.

Two Cloud Run services, not one. A tagging server handles live traffic, and a separate preview server powers the debug mode in the GTM interface. Keeping them apart is what stops your debugging sessions from touching production traffic.

Step 1: create the server container in Tag Manager

In Tag Manager, create a new container and choose Server as the target platform. When it offers to provision automatically, decline and select the manual option instead. Tag Manager will show you a container configuration string: a long base64 blob that tells a server container which GTM container it belongs to. Copy it. That string is the only piece of GTM state your infrastructure needs.

Step 2: deploy the preview server first

The order matters, because the tagging server needs the preview server’s URL as an environment variable. Both run the same public image: gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable.

gcloud config set project YOUR_PROJECT_ID
gcloud services enable run.googleapis.com

gcloud run deploy gtm-preview-server 
  --image gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable 
  --region europe-west1 
  --platform managed 
  --allow-unauthenticated 
  --set-env-vars RUN_AS_PREVIEW_SERVER=true 
  --min-instances 0 
  --max-instances 1 
  --timeout 60

Note the sizing: the preview server serves you and your colleagues, not the public, so --min-instances 0 lets it scale to zero and cost nothing when nobody is debugging. Pick a region close to your users; on a European site europe-west1 or europe-southwest1 (Madrid) keeps latency and data residency sensible.

Note the URL Cloud Run returns. You need it next.

Step 3: deploy the tagging server

gcloud run deploy gtm-tagging-server 
  --image gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable 
  --region europe-west1 
  --platform managed 
  --allow-unauthenticated 
  --set-env-vars CONTAINER_CONFIG='YOUR_CONTAINER_CONFIG_STRING' 
  --set-env-vars PREVIEW_SERVER_URL='https://gtm-preview-server-xxxx.run.app' 
  --min-instances 2 
  --max-instances 10 
  --timeout 60 
  --no-cpu-throttling

Four of those flags deserve explanation, because they are where people get burned.

  • --min-instances 2. Google recommends a minimum of two instances to reduce the risk of data loss during an outage. One instance means a cold start or a crash drops events, and dropped tracking events are gone forever. Two is the floor for anything you would call production.
  • --no-cpu-throttling. Without it, Cloud Run throttles the CPU between requests, which breaks tags doing work after the response is returned. This is the flag whose absence causes the classic “some events arrive, some do not” mystery.
  • --max-instances 10. Autoscaling between 2 and 10 servers handles roughly 35 to 350 requests per second, which covers most sites comfortably. Set the ceiling deliberately; unbounded autoscaling plus a traffic spike or a bot storm is how you get a surprise invoice.
  • --allow-unauthenticated. Required. This endpoint has to be reachable by every browser on the internet.

Verify before going further. Append /healthy to the service URL: a working tagging server returns ok.

Step 4: map your own subdomain

This step is not cosmetic, it is the entire point. Left on the default run.app URL, your tagging server is a third-party domain to the browser, and the cookies it sets get treated accordingly, which throws away the durability you deployed all this for. Map a subdomain of the site itself, something like gtm.yourdomain.com or sst.yourdomain.com.

You have two routes. Cloud Run domain mapping is the simplest: verify the domain, add the mapping, and add the DNS records Google gives you. Putting a load balancer in front is more work and more cost, but gives you Cloud Armor, better control over caching and headers, and a static IP. For a single-region European setup, start with domain mapping and move to a load balancer when you have a reason.

Whichever you choose, the subdomain must be on the same registrable domain as the site. A tagging server at gtm.somethingelse.com is still third party, and you have gained nothing.

Step 5: point the client side at it

In your web container, set the Google tag’s server_container_url to your new subdomain. Your GA4 requests now go to your own domain, get processed by the server container, and are forwarded to Google from the server.

In the server container, add the GA4 client so it can parse incoming requests, then the tags for each destination. Start with GA4 only. Get one clean event flowing end to end before adding Meta CAPI, Ads or anything else, because debugging three integrations at once on a new stack is a bad afternoon.

What it actually costs

Each server instance is roughly 45 USD per month, based on 1 vCPU and 0.5 GB memory on the CPU-always-allocated pricing model. With the recommended two-instance minimum, budget at least 90 USD per month before traffic, plus egress and any load balancer.

That number is the honest reason managed providers exist. Running it yourself in GCP gives you full control, data residency you can point to in a DPA, and no third party in the request path. Managed hosting gives you a fixed monthly price and someone else’s on-call rota. Neither is wrong. What is wrong is deploying it yourself and then not operating it: no monitoring, no alerting, no one who notices when the container has been failing for a week.

The traps, in the order people hit them

  1. Consent is not solved by moving servers. Server-side tagging changes where tags fire, not whether you are allowed to fire them. Consent Mode signals still have to reach the server container and still have to be respected there. Moving a tag server-side to dodge a consent banner is a compliance problem wearing an engineering costume.
  2. Skipping --no-cpu-throttling. Everything looks fine in preview, then a fraction of events vanish under real traffic.
  3. Running one instance to save 45 dollars. You save the money and lose events during every deploy and every cold start.
  4. Forgetting the preview server exists. If debug mode stops working after a redeploy, check that PREVIEW_SERVER_URL still points at a live service.
  5. Never rotating the container config. That string is a credential. Treat it like one: keep it out of your git history, and store it in Secret Manager rather than a shell script on someone’s laptop.
  6. No monitoring. Put an uptime check on /healthy and an alert on Cloud Run 5xx rate. A tagging server that silently dies takes your entire measurement with it, and nobody notices until the monthly report looks odd.

Where to go next

Once GA4 is stable, the genuinely valuable additions are the ones that are hard or impossible client side: Meta Conversions API with proper event deduplication, Google Ads conversions with enhanced conversions, and enriching events server side from your own systems before forwarding. That last one is where server-side tagging stops being a privacy tactic and starts being a data capability.

If you would like this built properly, or reviewed because it was built in a hurry, talk to us. It is a large share of what we do.

Get in touch

Questions? A project in mind? Talk to us.

  • Analytics, tracking or data questions: real humans answer, fast
  • Enterprise: audits, sGTM, GA4 360, BigQuery and MMM engagements
  • Tell us your stack and goals, we reply within one business day