Skip to content
ceaksan
gtm

sGTM Hosting Decision Matrix: Cloud Run, Stape, and Self-Host Compared

Where should server-side GTM be hosted? Cloud Run, Stape, and self-hosted Docker compared across cost, maintenance, and agency multi-client scenarios. Including Cloudflare Workers' same-origin proxy role.

14 min read Updated:
TL;DR

sGTM is a Docker image with Node.js runtime requirements; it needs a platform that can run Docker containers (such as Cloud Run, Stape, AWS ECS, or self-hosted Docker). Cloudflare Workers cannot run sGTM; it is used as a same-origin proxy in front of sGTM. At agency scale, the default recommendation is Cloud Run; Stape when there is no maintenance capacity, and self-host when the country where data is processed is defined in the contract.

In the previous guide I covered sGTM (server-side Google Tag Manager) setup on Cloud Run, Stape, and Docker self-host. This post is the decision side of the same topic: which hosting for which scenario, a production-ready Caddy pattern for self-host, agency multi-client setup, and the real role of Cloudflare Workers.

Hosting choice is not made by tool name but by weighing three axes together: maintenance capacity, data sovereignty, traffic predictability. Below I first compare the three options along these axes, then derive a scenario-based decision matrix at the end.

The sGTM Runtime Reality

The foundation of the decision: sGTM is a Docker image. The official image is gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable.1 According to the release notes, since version 4.0.0 (November 10, 2025) the base image has been gcr.io/distroless/nodejs24-debian13, meaning Node.js 24 on distroless Debian 13. The earlier 3.x versions used Node.js 22 on Debian 12.2

Runtime requirement: a container platform that can run a Docker image. The default resources in the Cloud Run guide are 1 vCPU and 0.5 GB of memory per instance.3 The manual setup guide states the upper limit explicitly: “The provisioned servers should have at most 1 vCPU. Additional vCPUs are not utilized and affect autoscaling negatively.”1

The result: the sGTM hosting list is limited to platforms that can run Docker + Node.js. This list does not include Cloudflare Workers, Vercel Edge Functions, or Netlify Edge Functions. These are all V8 isolate platforms; they cannot run Docker images or long-lived Node.js servers. Cloudflare’s separate Containers product can run Docker images, but it is not a documented setup path for sGTM.4 CF Workers can be placed in front of sGTM; I cover that in a separate section below.

Three Options, Three Profiles

OptionManaged?Region choiceMaintenancePredictable cost
Google Cloud RunPartially managedBroad (GCP regions)LowNo (usage-based)
StapeFully managedLimited to Stape’s locationsVery lowYes (fixed tier)
Self-host DockerNoFully flexibleHighYes (within capacity)
Google App EnginePartially managedBroad (GCP regions)MediumNo

App Engine has receded in favor of Cloud Run in current documentation, so I do not cover it separately here; the Cloud Run recommendation subsumes it. The actual decision is between Cloud Run, Stape, and self-host.

Cloud Run

Cloud Run is Google’s recommended path for sGTM. Automatic provisioning in the GTM interface deploys directly to Cloud Run, and the Cloud Run setup guide covers both automatic and manual setup.3

Strengths:

  • Native integration with GCP services: Secret Manager (container config and auth token management), IAM (client project isolation), Cloud Logging and Cloud Trace (no need for a separate debug collection layer).
  • Built-in auto-scaling: instance count adjusts to traffic. According to the example in the guide, 2-10 servers handle 35-350 requests per second, depending on tag complexity.3
  • Region variety: EU regions such as Frankfurt, Warsaw, or Milan.
  • The richest documentation and practitioner content: sources such as Mark Edmondson and the Square Developer Blog walk through setup on Cloud Run.56

Weaknesses:

  • Cost is usage-based. Google’s guide recommends at least 2 production instances to reduce the risk of data loss; that means a fixed cost floor even when traffic is low, and the preview server comes on top of it.3 As traffic grows, instance count and the bill grow with it; the advantage of fixed-tier models becomes visible during campaign days.
  • Google Cloud billing setup in a client account typically requires documentation and an approval process. If the client does not have a GCP project, creating one adds an onboarding step.
  • Cold start: if min instance is set to 0, the first request has latency. Google recommends at least 2 instances for production; min 0 is a cost choice that departs from that recommendation.
  • The preview server is deployed separately. The manual setup guide requires exactly 1 preview server and says not to configure autoscaling beyond 1 instance.1 On Cloud Run, that means a separate service with max instances set to 1.

Fits: Agencies deeply embedded in the Google ecosystem, technical teams familiar with GCP, variable traffic and scale needs, client accounts already using GA4 BigQuery export.

Does not fit: Agencies without time for maintenance, client-side GCP setup operations that drag on, scenarios where fixed monthly cost is required.

Stape

Stape is a managed service focused on sGTM hosting. The container runs on their infrastructure; the user handles container config and tag mapping.7

Strengths:

  • Near-zero ops. No need to deal with Docker, IAM, or billing layers.
  • Region choice: multiple locations, including the EU.
  • Ready-made extra features called “power-ups” (such as Cookie Keeper and Custom Loader); functions that take time to build manually are enabled from the panel.8
  • The preview container is managed separately via a UI.
  • The custom loader is used for resilience against ad blockers.

Weaknesses:

  • Pricing is tier-based; exceeding the tier limit forces a tier upgrade. The request quota counts not only tracking hits but also script loads (gtm.js, gtag.js), preview sessions, and bot requests.8
  • Power-ups are tied to plan tiers; a feature you need (Multi Domains, for example) can require a higher plan even when traffic does not.8
  • Subscriptions are per site: each site needs its own container and its own plan, and one subscription cannot be split across several sites.8 On the agency side, cost grows with the number of clients.
  • Container image cannot be modified; flexibility for custom logic is more limited compared to Cloud Run or self-host.
  • Migration cost is higher once the stack is built on top of Stape.

Fits: Agencies managing multiple clients that can pass the per-client plan on to the client, teams wanting to put the time that would go to maintenance into creative and strategy work, accounts where an EU region is sufficient.

Does not fit: Clients with critical data sovereignty requirements (finance, public sector, health), advanced setups requiring custom container logic, accounts whose traffic frequently exceeds tier limits, so the fixed-price advantage disappears.

Self-Host Docker

Running the Docker image on your own server. Paolo Bietolini published a production-ready pattern: Docker Compose with a Caddy reverse proxy.9 In practice, two layers:

Production pattern: Docker Compose with two containers (preview + tagging), both bound to 127.0.0.1 ports, with Caddy providing HTTPS termination and subdomain routing (sgtm.site.com → tagging, sgtm-preview.site.com → preview). Critical Caddy notes: according to Bietolini, Preview mode can delay responses by up to 20 seconds in some cases, so he sets the Caddy timeout to 30 seconds; cache must be disabled (Preview does not work with cached responses); X-Real-IP and X-Forwarded-For headers must be passed to the container.

Infrastructure: A VPS in an EU location with a provider like Hetzner Cloud, Caddy (automatic Let’s Encrypt), Docker. In multi-app environments, a PaaS layer like Coolify or Dokploy can be added; but since sGTM is a single service, running docker compose and Caddy directly is generally sufficient and offers a smaller attack surface.

Setup essence (simplified from the Paolo Bietolini pattern):

# docker-compose.yml
services:
  gtm_prod:
    image: gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable
    environment:
      CONTAINER_CONFIG: ${CONTAINER_CONFIG}
      PREVIEW_SERVER_URL: ${PREVIEW_SERVER_URL}
    ports:
      - "127.0.0.1:8081:8080"
    restart: always

  gtm_preview:
    image: gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable
    environment:
      CONTAINER_CONFIG: ${CONTAINER_CONFIG}
      RUN_AS_PREVIEW_SERVER: "true"
    ports:
      - "127.0.0.1:8082:8080"
    restart: always

The container config string is found in the sGTM UI under Admin > Container Settings > Manual Provisioning. Caddy configuration, SSL, and log rotation details are documented in Paolo Bietolini’s guide.9

Local debug: If you want to run sGTM on your own machine, Simo Ahava’s docker-compose stack does the job.10 Because the official image is published only as linux/amd64, Apple Silicon Macs need emulation; I cover the steps, including a Colima setup, in the sGTM setup guide.

Strengths:

  • You know where the server and the logs are kept; data sovereignty extends only as far as the country the server is in. The provider’s name does not guarantee the location: on Hetzner, choosing a Germany or Finland location puts the server in the EU, but Hetzner also has US and Singapore locations.11
  • Predictable cost: within server capacity and the traffic quota included in the package, traffic generates no extra bill; overage is billed separately.12 Operating effort sits outside this figure.
  • No customization ceiling. The Docker image can be forked, custom middleware added.
  • Fewer processors in the chain during audits, since no third party like Stape is involved. The cross-border transfer question remains, however, as long as the server is outside Türkiye and sGTM forwards data to platforms like Google and Meta.

Weaknesses:

  • Patch management, security updates, monitoring, backup, and SSL renewal fall into your responsibility (Caddy renews SSL automatically; the other layers are manual).
  • Auto-scaling is manual. If VPS capacity falls short during a sudden traffic increase, the service goes down.
  • The Compose layout in the example runs a single production and a single preview container. Preview is not a backup for production; when the single server stops, tagging stops too.
  • Preview and prod separation, reverse proxy, and firewall setup are manual.
  • sGTM image updates must be tracked manually; a test layer is required during Node.js major version transitions (the transitions in the release notes are 16, 18, 22, and 24, all even-numbered LTS releases).2

Fits: Agencies managing a small client portfolio, with a DevOps team in place, data sovereignty in the client contract, and fixed cost as a requirement.

Does not fit: Agencies without DevOps capacity, high client variety (different traffic profiles require different sizing), scenarios requiring fast onboarding.

Docker in Your Own Cloud Account: AWS and Azure

The same Docker image also runs on managed container platforms such as AWS ECS/Fargate or Azure Container Apps. This is not the same maintenance model as a VPS: the operating system and the Docker host move to the provider, while sGTM configuration, image updates, domain, capacity, and monitoring stay with the team. Setup is also no longer as manual as it was: ECS Express Mode sets up the Fargate service, the load balancer, and autoscaling in one step.13 On the Azure side, ready-made scripts exist for Container Apps.14 A working script, however, does not mean client isolation, monitoring, and handover processes are solved. These paths make sense when the client already has an AWS or Azure account and a team that operates that environment.

Additional Layer: Same-Origin Proxy with Cloudflare Workers

In the sGTM ecosystem, Cloudflare Workers is a transparent proxy placed in front of sGTM. Google tag gateway for advertisers rests on the same first-party serving idea, but it does not require sGTM: it routes requests at the CDN (Content Delivery Network) layer and there is no server to manage.15

What it does:

The visitor browser calls https://site.com/metrics. The CF Worker takes this request and forwards it to the sGTM endpoint running on Cloud Run (or Stape, or self-host). The response comes back and the Worker returns it to the browser. To the user, all analytics traffic appears to stay on the site.com domain.

Prerequisite: the site’s DNS (Domain Name System) must be on Cloudflare and proxied; a Worker route only works on a domain that Cloudflare sits in front of.

What it gains:

  • First-party cookie: The browser sets the site.com cookie; cross-site restrictions do not apply.
  • Safari ITP (Intelligent Tracking Prevention): Safari also caps server-set cookies at 7 days when the response that sets them comes from a third-party IP, or when the subdomain resolves via CNAME to a different domain.1617 With a same-origin proxy, the request goes to the site’s own domain and the response comes back from the site’s Cloudflare IPs; neither check is triggered. The details of the IP matching rule are in the setup guide.
  • Latency: The Worker receives the request on the Cloudflare network at a point close to the user, but because caching is disabled, the response still comes from the sGTM origin. The effect is not a latency gain; an extra hop is added.
  • Ad blocker resilience (partial): Because your own domain path is used instead of known GA and Meta endpoint paths, simple list-based blockers may not recognize it. More comprehensive lists can also catch container IDs and request parameters, so this is not a full bypass.

Critical: CF default caching must be disabled. sGTM Preview Mode does not work with cached responses, so debugging stops working.18

Open source example: simondahla/sgtm-same-origin-proxy-cloudflare-worker provides ready Worker code.19 owntag published a detailed write-up on the same pattern.20 Stape provides a Cloudflare Worker setup walkthrough from its own management panel.21

Recommendation: If the site is behind Cloudflare, whatever hosting you choose, a CF Worker proxy layer in production is worth considering. The real gains are first-party cookies and ITP resistance.

Agency Multi-Client Architecture

For agencies managing multiple clients, three architectural paths:

Path A: Full isolation. A separate Cloud Run project or a separate Stape container per client (on Stape, subscriptions are per site anyway). Cleanest, highest ops load.

Path B: Shared infrastructure, separate container. Under a single agency Cloud Run project, a separate service per client (sgtm-clienta, sgtm-clientb). In a self-host scenario, separate Docker Compose services on a single VPS provide the same pattern. This is the middle ground between isolation and shared infrastructure.

Three items determine the cost of an additional client: setup effort, shared resources, and the client’s own consumption. Templates and deployment code lower the setup effort for the second client, but on their own they do not lower the cloud bill. The bill is only split when a running resource is shared, and that sharing is conditional on the platform: on AWS the load balancer can be shared, but each client’s compute is billed separately;22 on Azure App Service plan capacity is shared, but the bill grows as the plan grows, and Google’s single preview instance rule1 may require a separate plan or a higher tier. Traffic and log consumption add to the bill on every platform.

Technical note: In ECS Express Mode, up to 25 services in the same VPC, with subnets matching the load balancer’s availability zones, can share a single Application Load Balancer;23 if each client’s tagging and preview services sit in that group, 12 complete client setups fit on one ALB. On Azure App Service, you are billed per VM in the plan, and apps in the same plan scale together by default;24 per-app scaling, which limits preview to one instance, is only available on Standard and higher tiers and does not by itself reduce the plan’s bill.25

Path C: Single server container, multiple domains. A tagging server runs a single server container, but a single server container can receive data from multiple domains.26 Client sites route to the same service, and traffic and configuration are shared. Lowest cost, no isolation: a load increase on one site affects the shared service, a single publish affects everyone, and anyone with GTM access sees all tags. It makes sense for multiple domains of the same brand; if separate team access, independent publishing, or client handover is needed, the separate container and service model fits.

Client departure scenario:

  • Cloud Run, separate project (Path A): The project can be handed over to the client’s own organization and billing account.
  • Cloud Run, shared project (Path B): The project cannot be handed over; doing so would grant access to other clients’ resources too. The client’s services are rebuilt in their own project, and the container config and DNS are moved. Granting IAM access is not moving resources.
  • Stape: Container ownership can be transferred to the client’s Stape account (ownership transfer).8
  • Self-host: Docker compose and .env are moved to the client’s server.

A handover checklist should be in the agency contract annex: DNS handover, removing the agency’s IAM access, revoking or transferring the API (Application Programming Interface) keys of third-party integrations in sGTM, GA4 property ownership.

Decision Matrix

ScenarioRecommendedWhy
Small-to-mid agency, no DevOps teamStape EU regionNear-zero ops, fast onboarding, fixed tier pricing
Client deep in Google ecosystem, using BigQueryCloud RunNative integration with GCP services, flexible scale
Enterprise client, data sovereignty in contractSelf-host, server in the contracted countryData processing location and processor chain under control
Multi-client agency, standardization is priorityShared Cloud Run projectShared infrastructure, separate service, template deploy
Multi-client agency, AWS or Azure requiredECS Express Mode or App Service planShared load balancer or plan splits the fixed cost across clients
Ad blocker issue is front and centerAny + CF Worker proxyList-based blockers may not recognize your own domain path (partial)

If the site is behind the Cloudflare proxy, adding a CF Worker same-origin proxy on top of any option should be considered; it is not a standalone hosting choice but a layer.

Cost by Scale

ScaleOption that may be reasonableWhat drives the cost
Single site, low traffic, no technical teamStapeFixed tier; on Cloud Run, the recommended 2 instances plus preview create a fixed cost floor even at low traffic
Single site, variable or high trafficCloud Run or a higher Stape tierWhether instance hours or tier limits fit the traffic profile better
A few clients, one agencyShared Cloud Run project or a single VPSTemplates lower setup effort; on a VPS capacity is shared, and one client’s load affects the others
Many clients, AWS or Azure requiredECS Express Mode or App Service planLoad balancer or plan is split across clients; on Fargate compute is per client, on App Service plan capacity is shared (preview kept separate)
Data processing country defined in contractIn-country self-hostContract terms before cost; operating effort is highest

Closing Note

The sGTM hosting decision cannot be reduced to the Cloud Run vs Stape binary. The decision starts on three axes (maintenance capacity, data sovereignty, traffic predictability) and branches based on agency and client profile. Tool names change; the axes remain.

For setup details, see the sGTM Setup Guide. The remaining layers of the agency marketing stack will be covered in the upcoming Marketing Stack 2026 pillar post.

Stuck on Something Here?

If you hit a wall applying this to your own setup, or your situation raises a question this post does not cover, get in touch.

Get in Touch

Footnotes

  1. Manual setup guide. Google Tag Manager ↩ ↩2 ↩3 ↩4
  2. Server-side tagging release notes. Google Tag Manager ↩ ↩2
  3. Cloud Run Setup Guide. Google Tag Manager ↩ ↩2 ↩3 ↩4
  4. Lifecycle of a Container. Cloudflare Containers Docs ↩
  5. Deploying Server-Side Google Tag Manager on Cloud Run. Square Developer Blog ↩
  6. Google Tag Manager Server Side on Cloud Run - Pros and Cons. Mark Edmondson ↩
  7. Google Tag Manager Server-Side Tagging with Stape. Analytics Mania ↩
  8. Pricing and plans. Stape ↩ ↩2 ↩3 ↩4 ↩5
  9. Implementing Server-Side GTM with Docker: On-Premise Solution Guide. Paolo Bietolini ↩ ↩2
  10. Run Server-side Google Tag Manager On Localhost. Simo Ahava ↩
  11. Locations. Hetzner Docs ↩
  12. Billing FAQ. Hetzner Docs ↩
  13. Amazon ECS Express Mode. AWS Documentation ↩
  14. sGTM Deployment on Azure Container Apps. Selnekovic ↩
  15. Google Tag Gateway vs Server-Side GTM: Which One Do You Need? Paolo Bietolini ↩
  16. Private Browsing 2.0. WebKit ↩
  17. Tracking Prevention in WebKit. WebKit ↩
  18. Same-origin Deployment With Server-side Google Tag Manager. Simmer ↩
  19. sgtm-same-origin-proxy-cloudflare-worker. simondahla (GitHub) ↩
  20. How to Make Your Server-Side GTM Accessible for Free via Cloudflare on Your Main Domain and Bypass ITP. owntag ↩
  21. How to Use Same Origin Through Cloudflare. Stape ↩
  22. AWS Fargate Pricing. Amazon Web Services ↩
  23. Resources created by Amazon ECS Express Mode services. AWS Documentation ↩
  24. Azure App Service plans. Microsoft Learn ↩
  25. High-density hosting with per-app scaling. Microsoft Learn ↩
  26. How Do I Organize My Multi-domain Setup For Server-side Tagging In Google Tag Manager? Simmer ↩
Key Takeaways
  • 01 sGTM is a Docker + Node.js container and does not run on V8 isolate platforms like Cloudflare Workers
  • 02 Cloud Run is the best-documented path, Stape is the near-zero-ops alternative, self-host is for controlling the country the server runs in
  • 03 CF Workers runs as a same-origin proxy in front of sGTM; when the site is behind Cloudflare, it provides first-party cookies and ITP resistance
  • 04 In agency multi-client setups, templates split the setup effort and a shared load balancer or plan splits the fixed cost; per-client consumption is billed separately or absorbed by shared capacity, depending on the platform
  • 05 The hosting decision is made on three axes before list price: maintenance capacity, data sovereignty, traffic predictability
Frequently Asked Questions (FAQ)
+ Can I run sGTM on Cloudflare Workers?

No. sGTM is a Docker image (gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable) and requires a Node.js runtime. Cloudflare Workers is a V8 isolate platform and cannot run Docker containers or long-lived Node.js servers. CF Workers can only be used as a same-origin proxy in front of sGTM.

+ Is Cloud Run or Stape cheaper?

It depends on scale. At low traffic, Stape's fixed tier is usually more predictable; on Cloud Run, the 2 production instances Google recommends plus the preview server create a fixed cost floor even when traffic is low. As traffic grows, the comparison comes down to how instance hours and tier limits fit the traffic profile. On the agency side, setup and maintenance effort also belong in the calculation.

+ Should the preview container be hosted separately?

Yes. Google's manual setup guide requires exactly 1 preview server and says not to configure autoscaling beyond 1 instance. Tagging servers route preview requests to that server through PREVIEW_SERVER_URL. The guide does not explain the reason for the single-instance rule.

+ When using Stape, does data leave Türkiye?

Stape offers EU region selection, but it does not guarantee that data stays in Türkiye. Self-host only changes this if the server is hosted in Türkiye; EU providers like Hetzner raise the same cross-border transfer question. Also, sGTM's job is to forward data to platforms abroad such as Google and Meta. Hosting location does not remove that transfer; it only changes the processor in between.

+ How many clients can a single sGTM instance serve?

A tagging server runs a single server container; CONTAINER_CONFIG is the configuration of one container. A single server container can receive data from multiple domains, so multiple client sites can technically send to the same container. In agency practice, a separate server container and a separate tagging server per client is preferred for isolation; under a shared Cloud Run project, each client is deployed as a separate service.

+ Are CAPI and Enhanced Conversions dependent on hosting choice?

The core functions are not: CAPI and Enhanced Conversions tags are configured in the sGTM container and work the same on any hosting. The difference is in the extra layers. Stape's power-ups (such as Cookie Keeper and the custom loader) are Stape-specific; on Cloud Run or self-host, the same function has to be set up separately.

Feedback

Share your thoughts on the selected paragraphs. Email is required so I can reply.

Type