AWS Setup
AWS is not the default choice for sGTM — Google’s documentation and tooling center on GCP. But if your organization runs its infrastructure on AWS, forcing a GCP dependency for a single workload is an unnecessary complication. sGTM is a Docker container. It runs on ECS Fargate with reasonable setup effort, and you get the same first-party tracking capabilities with cost and operational patterns that fit your existing AWS footprint.
Architecture overview
Section titled “Architecture overview”The recommended AWS architecture for sGTM:
Route 53 / Your DNS provider ↓ CNAME collect.yoursite.com ↓Application Load Balancer (HTTPS, port 443) ACM Certificate ↓ECS Fargate Service Task: gtm-cloud-image:stable Environment: CONTAINER_CONFIG Port: 8080Alternatives exist — EC2, Elastic Beanstalk, App Runner — but ECS Fargate provides the best balance of autoscaling, managed infrastructure, and predictable cost for this workload.
Step 1: Prepare the container configuration
Section titled “Step 1: Prepare the container configuration”Get your Container Config string from your GTM server container (the same base64-encoded string used in GCP setup):
- In GTM, open your server container
- Admin → Container Settings → find the Container Config value
Store it in AWS Secrets Manager:
aws secretsmanager create-secret \ --name sgtm/container-config \ --secret-string "YOUR_CONTAINER_CONFIG_STRING" \ --region eu-west-1Step 2: Create an ECS cluster
Section titled “Step 2: Create an ECS cluster”-
AWS Console → ECS → Clusters → Create Cluster
-
Choose AWS Fargate (serverless compute — no EC2 instances to manage)
-
Name:
sgtm-cluster -
Leave other settings at defaults, then create.
Step 3: Create a Task Definition
Section titled “Step 3: Create a Task Definition”-
ECS → Task Definitions → Create new task definition → JSON tab
-
Paste the following, replacing the region, account ID, and secret ARN:
{"family": "sgtm-task","networkMode": "awsvpc","requiresCompatibilities": ["FARGATE"],"cpu": "512","memory": "1024","executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole","taskRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskRole","containerDefinitions": [{"name": "sgtm","image": "gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable","portMappings": [{"containerPort": 8080,"protocol": "tcp"}],"secrets": [{"name": "CONTAINER_CONFIG","valueFrom": "arn:aws:secretsmanager:eu-west-1:ACCOUNT_ID:secret:sgtm/container-config"}],"logConfiguration": {"logDriver": "awslogs","options": {"awslogs-group": "/ecs/sgtm","awslogs-region": "eu-west-1","awslogs-stream-prefix": "ecs"}},"healthCheck": {"command": ["CMD-SHELL", "curl -f http://localhost:8080/healthz || exit 1"],"interval": 30,"timeout": 5,"retries": 3}}]} -
Create the log group in CloudWatch first:
Terminal window aws logs create-log-group --log-group-name /ecs/sgtm --region eu-west-1
Step 4: Configure IAM roles
Section titled “Step 4: Configure IAM roles”The ECS task needs two IAM roles:
Execution role (ecsTaskExecutionRole) — used by ECS to pull the container image and read secrets:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecr:GetAuthorizationToken", "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", "logs:PutLogEvents", "secretsmanager:GetSecretValue" ], "Resource": "*" } ]}Step 5: Create an Application Load Balancer
Section titled “Step 5: Create an Application Load Balancer”-
EC2 → Load Balancers → Create Load Balancer → Application Load Balancer
-
Settings:
- Name:
sgtm-alb - Scheme: Internet-facing
- Listeners: HTTPS on port 443
- Availability zones: at least 2
- Name:
-
SSL Certificate: Request a certificate in ACM for
collect.yoursite.com. ACM certificate provisioning requires domain validation — either DNS CNAME records or email validation. -
Create a target group:
- Target type: IP (required for Fargate)
- Protocol: HTTP (the ALB terminates TLS, Fargate receives HTTP)
- Port: 8080
- Health check path:
/healthz
-
Attach the target group to your HTTPS listener.
Step 6: Create the ECS service
Section titled “Step 6: Create the ECS service”-
ECS → your cluster → Services → Create
-
Configuration:
- Launch type: Fargate
- Task definition: select
sgtm-task - Service name:
sgtm-service - Desired tasks: 2 (minimum for availability — no cold start risk with multiple running tasks)
-
Networking:
- VPC: your VPC
- Subnets: private subnets (the ALB handles public traffic)
- Security group: allow inbound 8080 from the ALB’s security group only
-
Load balancing: select your ALB and target group
-
Auto Scaling:
- Minimum tasks: 2
- Maximum tasks: 20
- Scale out policy: CPU utilization > 70%
- Scale in policy: CPU utilization < 30% for 5 minutes
Step 7: Configure your custom domain
Section titled “Step 7: Configure your custom domain”Once the ALB is running:
-
In Route 53 (or your DNS provider), create a CNAME record:
collect.yoursite.com CNAME sgtm-alb-xxxxxxxxx.eu-west-1.elb.amazonaws.com -
Verify:
Terminal window curl https://collect.yoursite.com/healthz# Expected: ok
Step 8: Connect client-side GTM
Section titled “Step 8: Connect client-side GTM”Same as GCP: update your client-side GA4 tag’s Tagging Server URL to https://collect.yoursite.com.
Cost comparison: AWS vs. GCP
Section titled “Cost comparison: AWS vs. GCP”At typical sGTM traffic levels, AWS is modestly more expensive than GCP Cloud Run due to the fixed ALB cost:
| Component | Monthly cost |
|---|---|
| ALB (base fee) | ~$16/month |
| ALB LCUs (2M requests) | ~$5/month |
| Fargate (2 tasks, 0.25 vCPU, 512 MB) | ~$25/month |
| Total at 2M requests | ~$46/month |
Compare to GCP Cloud Run at 2M requests: ~$35–60/month with min 1 instance. The costs are comparable at moderate scale, with GCP becoming slightly cheaper at very high request volumes due to the pay-per-request model.
CloudWatch monitoring
Section titled “CloudWatch monitoring”CloudWatch automatically receives logs from the Fargate tasks (configured in the task definition). Set up an alarm:
# Create an alarm for task count dropping below minimumaws cloudwatch put-metric-alarm \ --alarm-name sgtm-task-count-low \ --alarm-description "sGTM running task count below 2" \ --metric-name RunningTaskCount \ --namespace ECS/ContainerInsights \ --statistic Average \ --period 60 \ --threshold 2 \ --comparison-operator LessThanThreshold \ --dimensions Name=ServiceName,Value=sgtm-service Name=ClusterName,Value=sgtm-cluster \ --evaluation-periods 2 \ --alarm-actions arn:aws:sns:eu-west-1:ACCOUNT_ID:sgtm-alertsCommon mistakes
Section titled “Common mistakes”Single AZ deployment. Running all Fargate tasks in one availability zone means a zonal outage takes down your tracking entirely. Always deploy across at least 2 AZs.
Target group protocol mismatch. The ALB terminates TLS and forwards HTTP to Fargate on port 8080. If you set the target group to HTTPS, the ALB tries to establish TLS to the container, which fails. Protocol must be HTTP.
Insufficient container memory. The default 512 MB works for basic deployments. With Firestore enrichment or multiple concurrent vendor API calls, increase to 1024 MB to prevent OOM errors.
Not testing health check path before launch. Verify /healthz returns ok from the ALB target before directing production traffic. A misconfigured health check causes the ALB to mark all tasks unhealthy and return 503 to all requests.