I just wanna share my experiment to install sGTM on Cloudron.
And I think it works well, and I got this code from Claude AI.
You can use this CloudronManifest.json and execute it via Cloudron CLI:
And my question is how to make the update process automatically from Cloudron dashboard?
Thank you.
{
"id": "com.example.sgtm",
"title": "Server-side GTM",
"author": "Admin",
"description": "Google server-side Tag Manager (sGTM) tagging server, packaged for Cloudron.",
"tagline": "Self-hosted sGTM tagging server",
"version": "1.0.4",
"dockerImage": "gcr.io/cloud-tagging-10302018/gtm-cloud-image@sha256:688d35c6c54473be42d5128befa4d98a722d76d6fdf9cb84d63b52a3f223a1e6",
"healthCheckPath": "/healthz",
"httpPort": 8080,
"manifestVersion": 2,
"website": "https://services.example.com",
"contactEmail": "admin@example.com",
"tags": [
"analytics",
"gtm",
"tracking"
],
"memoryLimit": 1073741824,
"addons": {
"localstorage": {}
}
}
And this is the README.md:
Server-Side GTM (sGTM) on Cloudron — Setup Guide & Best Practices
This guide explains how to run the official Google image gcr.io/cloud-tagging-10302018/gtm-cloud-image as a custom Cloudron app, directly without building a Dockerfile. The image is pinned via the dockerImage field in CloudronManifest.json using an immutable digest (@sha256:...). There is no Dockerfile, and no build process is required.
You will run TWO apps from the same package:
| Role | Domain (REMEMBER!) | Required Environment Variables |
|---|---|---|
| Serving | services.example.com |
CONTAINER_CONFIG + PREVIEW_SERVER_URL (no RUN_AS_*) |
| Preview | service.example.com |
CONTAINER_CONFIG + RUN_AS_PREVIEW_SERVER=true |
️ Important: Naming Convention (Easy to mix up!)
- "services" (PLURAL, with an 's') = PRODUCTION / Serving -> serves your
gtm.js - "service" (SINGULAR) = PREVIEW / Debug
- Note: The role is strictly determined by the Environment Variables (ENV VAR), not the domain name. If they ever get swapped, check the variables!
️ Important: Docker Images (A hard-learned lesson!)
- DO NOT use the
:stabletag indockerImage. Cloudron/Docker caches:stableto the old digest. This means an update might report as "successful," but the old version will still be running (e.g., sticking to 4.3.0 instead of 4.4.0). Using--no-cacheDOES NOT help here—that's a build-cache flag, not a pull-cache flag, and this setup doesn't use builds anyway. - USE A DIGEST (
@sha256:...). It is immutable and content-addressed, making it impossible to cache incorrectly. Explicit version tags (e.g.,:4.4.0) are also safe; only:stable(and:latest) are dangerous because they are mutable. - Since there is no Dockerfile and no build, setting
dockerfileName: nullin the app config is the correct approach.
The Correct Serving Path: /gtm.js (NOT /load)
The server only serves the default /gtm.js path. Custom paths like /load WILL NOT work (returning a 400/Not Found error). In your GTM "Web Container" client, it doesn't matter if you fill the Tag serving path with /load or leave it empty—it won't affect anything, as the server will always use the default /gtm.js.
The first-party URL used by your site (via GTM4WP):
https://services.example.com/gtm.js?id=GTM-XXXXXXX
(The ?id= parameter is MANDATORY; without it, you will get an error, which is completely normal).
1) Check & Fix Server Roles (If your first-party /gtm.js isn't working)
Check the environment variables for both apps:
cloudron env list --app services.example.com
cloudron env list --app service.example.com
PRODUCTION (services) MUST HAVE:
CONTAINER_CONFIG= (your configuration string)PREVIEW_SERVER_URL=https://service.example.com- NO
RUN_AS_PREVIEW_SERVERvariable
If your services app was accidentally set as preview, fix it:
cloudron env unset --app services.example.com RUN_AS_PREVIEW_SERVER
cloudron env set --app services.example.com PREVIEW_SERVER_URL='https://service.example.com'
cloudron restart --app services.example.com
PREVIEW (service) MUST HAVE:
CONTAINER_CONFIG= (your configuration string)RUN_AS_PREVIEW_SERVER=true
Test it: Visit https://services.example.com/gtm.js?id=GTM-XXXXXXX -> It should output JavaScript code.
NOTE: CONTAINER_CONFIG and sensitive tokens are NOT stored in this repository. Always set them via cloudron env set, keeping them out of Git.
2) Updating the Image to the Latest Version
Because the image is pinned via dockerImage (not a Dockerfile build), updating simply means changing the image reference in CloudronManifest.json and deploying. You DO NOT need --no-cache, and there is no Dockerfile to edit.
Steps:
-
Find the latest version & digest:
- Go to Google Cloud > Artifact Registry > package
gtm-cloud-image(projectcloud-tagging-10302018, repogcr.io), and open the Overview tab. - Look at the Tags: find the latest version (e.g.,
4.5.0). Copy the "Digest" value (sha256:...) from the version that also holds thestabletag. - Alternative: Check Google's server-side release notes for version numbers.
- Go to Google Cloud > Artifact Registry > package
-
Edit
CloudronManifest.json:- Increase the
version(e.g.,1.0.3->1.0.4) — MUST be increased with every update. - Change
dockerImageto the new digest:
"gcr.io/cloud-tagging-10302018/gtm-cloud-image@sha256:<NEW_DIGEST>"
(If you prefer readability, you can use the version tag:...gtm-cloud-image:4.5.0)
- Increase the
-
Commit & push to your Git repository.
-
Deploy BOTH apps from the repo folder (from any machine with the Cloudron CLI installed):
git clone https://git.example.com/username/sgtm-cloudron.git # first time only cd sgtm-cloudron git pull # if already cloned cloudron update --app services.example.com # PRODUCTION cloudron update --app service.example.com # PREVIEWCloudron will read the
dockerImagefrom the local manifest and pull that image directly. A different digest equals a different image, guaranteeing a fresh pull.
Quick Alternative without editing the manifest (Ad-hoc):
This is valid but leaves your repo out-of-sync with what's actually running. If you use this, please update your manifest afterward!
cloudron update --app services.example.com --image gcr.io/cloud-tagging-10302018/gtm-cloud-image@sha256:<DIGEST>
cloudron update --app service.example.com --image gcr.io/cloud-tagging-10302018/gtm-cloud-image@sha256:<DIGEST>
Verification:
https://services.example.com/healthz-> Should returnOKhttps://services.example.com/gtm.js?id=GTM-XXXXXXX-> Should still return JS code- GTM > Admin > Container Settings -> Version number should increase.
- Note: This number is REPORTED via usage beacons triggered by traffic and can be DELAYED up to 24 hours. The preview server rarely gets traffic, so it reports the slowest, and GTM tends to display the oldest version between the two servers. To confirm quickly without waiting: run Preview mode + send a few new requests from the web container, then check the Console (the outdated version warning should disappear).
Update downtime is just a few seconds and WILL NOT affect your site.
3) Update Cadence (Security)
The image consists of Node.js + libraries. Google releases periodic security patches. Because this is self-hosted, keeping it updated is your responsibility.
- Update quarterly.
- Update when a version warning appears in the GTM Console.
- Update when a major release drops.
Just repeat the steps in Section 2.
Note: The enableAutomaticUpdate feature in Cloudron DOES NOT pull new Google versions on its own—it's only for package versions we publish. Google version updates are always manual via Section 2.
4) Files in this Repository
CloudronManifest.json: App metadata + pinned image viadockerImage(digest). This is the SOURCE OF TRUTH for the running version.README.md: This file.






