Install CloudGrange — Azure Container Apps
Azure Container Apps runs CloudGrange as managed containers next to managed Azure services: Azure Database for PostgreSQL, Key Vault, Azure Files and Log Analytics. You never create a virtual machine, never install Kubernetes, and never run Helm.
This is the one path that is not the Helm chart. Container Apps has its own deployment schema and cannot run Kubernetes manifests, so the platform is described by a Bicep template instead. Everything above that line is the same product: the same API, the same portal, the same Keycloak realm, the same built-in relay, the same cg CLI, and the same Platform → Updates page.
CloudGrange is in preview, not GA. The installer always deploys the latest release unless you pass
--version. Container Apps ships from2609.0.0-preview.27onwards:Install-CloudGrange-Aca.zipis published with that release and every release after it, so the download step below resolves. See current product and release status.
Known issues in 2609.0.0-preview.27
2609.0.0-preview.27 is the first release to carry the Container Apps installer, and three defects were found on it after it was cut. All three are fixed and land in the next release. Until then:
| Issue | What you see | What to do |
|---|---|---|
| The deployment fails in some regions | After about 15 minutes, with the database and Key Vault already created, the deployment fails with ContainerAppInvalidName. It happens where the region's short code is four characters — westus3, eastus2, westus2 and similar. |
Install into a region with a three-character code, such as --location eastus or --location centralus. Re-running after the failure does not help: the Keycloak realm is imported once and keeps the wrong portal address, so delete the resource group and start again in a supported region. |
| "Sign in with your organization" returns 502 | The SSO button leads to a blank page reading 502 Bad Gateway. Local sign-in, the cg CLI device-code login and the rest of the portal are unaffected. |
Use the local administrator account you created in the setup wizard. |
| Platform updates are refused | Platform → Updates → Apply ends with "the pre-update backup … had not finished after 4 minutes. Nothing has been changed." The backup itself is fine and completes shortly afterwards. | Nothing is damaged — the update stops before changing anything. Wait for the next release, which raises the wait. |
Modules do not run on Container Apps. This is not a defect of a particular release: a module is a container workload, and CloudGrange has no module runtime for Container Apps yet — see Modules and the Container Apps path.
In 2609.0.0-preview.27 this was silent: installing a module left it at Installing for ever, its nav entry still appeared, and its page returned a 503 that suggested trying again. From 2609.0.0-preview.28 the product says so. The Module Catalog shows the reason and Install is disabled; if you call the API directly, install, enable and the module proxy answer 501 with the problem code module-runtime-unsupported. Nothing is recorded, so there is nothing to clean up. Use AKS or another Kubernetes path if you need modules.
When to choose this path
| Choose Container Apps if | You want Azure to own the host, the runtime and the database, with no VM and no Kubernetes to patch |
| Choose AKS instead if | You want the Helm chart, your own cluster, and the same Kubernetes deployment you would run on-premises |
| Choose Azure VM instead if | You want a single VM you control, with CloudGrange managing the Foundation on it |
There are no Foundation updates on Container Apps. Azure owns the host operating system and the runtime, so CloudGrange has no Foundation to offer you and the portal shows no Foundation card — only the Platform card. On the appliance, Windows-script, Linux-script and Azure VM paths, CloudGrange owns the Foundation and an administrator applies those updates from that second card. Here, Azure patches the platform underneath you.
Prerequisites
The full list is on Azure prerequisites. In short:
- An Azure subscription, and on it Owner, or Contributor plus User Access Administrator. The template assigns roles, which Contributor alone cannot do.
- Container Apps and PostgreSQL flexible server capacity in your region.
az,jq,opensslandcurlon the machine you run the installer from, andaz loginalready done.- Outbound access to
ghcr.io(CloudGrange images) andquay.io(Keycloak) from Azure. Container Apps pulls these itself. - A region your subscription can actually use. Azure restricts Azure Database for PostgreSQL flexible-server provisioning per subscription and region, and Container Apps environments can hit regional capacity limits independently of that. The installer checks the database restriction before it creates anything and tells you to pick another region; a Container Apps capacity error only appears during the deployment. If either happens, re-run with
--location <another region>.
You do not create any secret. The installer generates the database password, the encryption master key, the Keycloak bootstrap credentials, the API's realm client secret and the relay enrolment token, and stores them in the deployment's own Key Vault. Nothing is printed and nothing is written to a file. On a re-run the installer reads each value back out of the vault rather than generating a new one, so redeploying never rotates a live credential out from under the running database or realm.
Step 1 — get the installer
curl -fsSLO https://github.com/CloudGrange/cloudgrange-deployment-installer/releases/latest/download/Install-CloudGrange-Aca.zip
curl -fsSLO https://github.com/CloudGrange/cloudgrange-deployment-installer/releases/latest/download/Install-CloudGrange-Aca.zip.sha256
sha256sum -c Install-CloudGrange-Aca.zip.sha256
unzip -q Install-CloudGrange-Aca.zip
The archive contains scripts/Install-CloudGrange-Aca.sh and the iac/ templates it deploys.
Step 2 — see what will be created
./scripts/Install-CloudGrange-Aca.sh --owner-email you@example.com --what-if
This runs az deployment sub what-if and changes nothing. A clean what-if is not a promise of a clean deploy — several Azure provider checks only run on create — but it will catch a missing permission, a bad region or a quota problem before anything exists.
Step 3 — install
./scripts/Install-CloudGrange-Aca.sh --owner-email you@example.com
Useful options:
| Option | Meaning |
|---|---|
--resource-group <name> |
The group to create or reuse. Default follows the CAF pattern, rg-cloudgrange-<env>-<region>-<instance> |
--location <region> |
Default eastus |
--environment dev|test|stage|prod |
Default prod. Feeds the resource names and the tag set |
--version <tag> |
A specific release. Default: whatever the update channel currently calls latest |
--update-channel <url> |
The release channel. Must be https — the installer refuses a plain-http channel rather than installing something the updater would later refuse |
--no-governance |
Skip the Azure Policy assignments and the Defender for Cloud plan changes. Both are subscription-level changes that deleting the resource group does not undo, so use this for anything you intend to throw away |
--tag k=v |
An extra tag on every resource. Repeatable |
The deployment takes roughly 10 to 15 minutes, most of it the PostgreSQL flexible server. When it finishes the installer prints the portal URL.
Step 4 — first-run setup
Open the portal URL. You land in the setup wizard, exactly as on every other path, and you create the first administrator there. No account and no password was created for you.
After setup completes, the built-in relay enrols itself within about a minute and appears under Resources.
What gets deployed
| Resource | Why it is there |
|---|---|
| Container App — API | The CloudGrange API |
| Container App — portal | The web UI, and the only public origin. It proxies /api, /hubs, /realms/cloudgrange and the Keycloak theme path to the other apps |
| Container App — Keycloak | The identity provider, with the same cloudgrange realm the Helm chart imports. Internal ingress only — reachable through the portal, never directly |
| Container App — relay | The built-in site relay, which is what makes cluster registration and job dispatch work |
| Azure Database for PostgreSQL flexible server | The platform database, shared with Keycloak, as in the chart |
| Azure Key Vault | Every bootstrap secret, plus the update status document. The Container Apps read them as Key Vault secret references, never as plaintext environment variables |
| Azure Files (two shares) | The API's data-protection key ring and the relay's enrolment identity. Both must survive a revision roll — see Updates |
| User-assigned managed identity | What the apps use to read Key Vault, and what the in-app updater uses to change revisions |
| Log Analytics, Application Insights, Azure Monitor | Logs and metrics |
Authentication lives on one origin, the portal's, on this path exactly as on every other one. That is why Keycloak is not published separately: SSO redirects, the cg auth login device-code page and its theme assets all come back through the portal host, so nothing about the CLI or the browser flow differs from an on-premises install.
Updates and rollback
Updates are in-app, from Platform → Updates, the same page as everywhere else. Only the Platform card appears; there is no Foundation card, because Azure owns that layer.
The database must be General Purpose or Memory Optimized. Azure does not support on-demand backup on the Burstable compute tier, and CloudGrange refuses an update it cannot back up first — so a Burstable server can never be updated in place. The installer provisions Standard_D2ds_v4 (General Purpose) for exactly this reason. Azure also allows at most seven on-demand backups per server; once you reach that, delete an older one under the server's Backup and restore blade before the next update.
What an update does. The API checks the update channel, verifies the release manifest against its published SHA-256, takes an on-demand backup of the PostgreSQL flexible server, then re-tags each CloudGrange container to the new release. Azure Container Apps creates a new revision per app and keeps the previous revision serving until the new one is healthy. If a new revision never becomes healthy, the update is reported failed and the old revision is still the one answering requests.
What rollback does — and does not do. Rolling back re-tags the Container Apps to the previous release. Azure then serves the previous revision again, so the application layer goes back cleanly.
It does not restore the database. Be precise about this before you rely on it:
- The on-demand backup taken before the update is a restore input, not an automatic undo. Restoring it is an operator action.
- Point-in-time restore on Azure Database for PostgreSQL creates a new server. It does not rewind the existing one in place. Recovering data therefore means restoring to a new server and repointing the platform at it — a deliberate, planned operation, not a button.
- Schema changes applied by the newer release are not reversed by a rollback. This is the same caveat as on the Kubernetes paths; the difference is that there the updater restores a
pg_dumpit took itself, and here it cannot, because the database is a managed Azure service and not a container the updater controls.
In practice: an update that fails to come up healthy is safe, because Azure never stopped serving the old revision and the database was never migrated by a version that did not start. An update that comes up, migrates the schema and is then judged bad is the case where you need the backup and a planned restore.
Offline updates are not supported here. Uploading a Platform bundle is a feature of the air-gapped Kubernetes paths; Container Apps pulls images from a registry, so an uploaded bundle has nowhere to go. The API refuses it with that reason.
Removing it
Everything except the subscription-level governance settings lives in the one resource group:
az group delete --name <resource group> --yes
If Key Vault soft-delete retention is on — it is, by default — the vault name stays reserved until it is purged or the retention period passes:
az keyvault list-deleted --query "[?name=='<vault name>']"
az keyvault purge --name <vault name>
If you installed without --no-governance, the Azure Policy assignments and the Defender for Cloud plan changes were made outside the resource group and are not removed by deleting it. Remove those separately.