Detailed companion to the README's Deployment environments section. Three environments run the system; this page covers the two continuously-live ones. For local Docker Compose, the README's Running Locally section is already the complete reference.
A single Ubuntu 24.04 VM in Azure's Poland Central region runs the exact same infra/docker-compose.yml stack as local dev, minus the override file — so it terminates real TLS via Let's Encrypt instead of serving plain HTTP.
Live URL: https://team-devoops.polandcentral.cloudapp.azure.com
| Resource | Purpose |
|---|---|
azurerm_resource_group |
rg-team-devoops |
azurerm_virtual_network + azurerm_subnet |
10.0.0.0/16 / 10.0.1.0/24 |
azurerm_network_security_group |
Allows inbound 22 (SSH), 80 (HTTP), 443 (HTTPS) only |
azurerm_public_ip |
Static, with a free Azure-assigned FQDN (domain_name_label) |
azurerm_linux_virtual_machine |
Standard_D2as_v4, Ubuntu 24.04 LTS, SSH-key auth only (disable_password_authentication = true) |
Remote state lives in Azure Blob Storage (stdevoops26tfstate/tfstate), shared and locked between local runs and CI so two applies can't race. Auth is OIDC (ARM_USE_OIDC=true) — no long-lived Azure client secret is stored anywhere.
Run it via the infra workflow (.github/workflows/infra.yml, manual workflow_dispatch with plan / apply / destroy), or locally:
az login
az account set --subscription <AZURE_SUBSCRIPTION_ID>
export ARM_SUBSCRIPTION_ID=<AZURE_SUBSCRIPTION_ID>
export ARM_USE_AZUREAD=true
cd infra/terraform
echo "admin_ssh_public_key = \"$(cat ~/.ssh/team-devoops-azure.pub)\"" > terraform.tfvars
terraform init
terraform planRuns after Terraform, targeting the VM's public IP:
- Install Docker (apt repo +
docker-ce,docker-compose-plugin). - Clone/update the repo at the deployed commit (
gitmodule,force: trueso the working tree always matchesmain). - Write
infra/.env(Postgres and Keycloak passwords, OIDC client secrets),services/py-genai-helper/.env, andservices/spring-letter/.envfrom content passed in as extra-vars (sourced from GitHub Secrets — never committed). Nothing indocker-compose.ymlis hardcoded; every credential is a${VAR}resolved frominfra/.envat container start. docker compose -f infra/docker-compose.yml up -d --build --remove-orphans.
Triggered automatically by the cd workflow on every push to main — see docs/cicd.md for the exact secrets involved.
- Infra change (new resource, VM size, etc.) → push → manually trigger
infraworkflow withapply. - App code change → merge to
main→cdworkflow redeploys automatically (both this VM and Kubernetes, in parallel). - Teardown → trigger
infraworkflow withdestroy.
The same services are also deployed via a Helm umbrella chart to the course's RKE2 cluster, with autoscaling and rolling-update self-healing configured per service.
Live URL: https://ge83mom-devops26.stud.k8s.aet.cit.tum.de · Namespace: ge83mom-devops26 · Chart: infra/helm/team-devoops
This path has enough moving parts (one-time Secret/ConfigMap bootstrapping, the Keycloak theme's projected-volume workaround, resource-quota tuning to fit Ollama into a fixed namespace quota, autoscaling scope) that it has its own dedicated, actively-maintained reference — infra/helm/README.md — rather than being duplicated here. Highlights:
- Images are built and pushed to
ghcr.io/aet-devops26/team-devoops/<service>by thecdworkflow'sdocker-pushjob, thendeploy-k8srunshelm upgrade --install --rollback-on-failure. - PostgreSQL, Prometheus, Grafana, Loki, and Ollama each run in-cluster with their own PVC on the cluster's default StorageClass (
csi-rbd-sc, ReadWriteOnce). - On every pull request,
ci.yml'shelm-validatejob lints the chart, renders it, and schema-validates the output withkubeconform— a broken chart can't merge.
The spec's cloud-environment requirement is satisfied by the Azure VM (Docker Compose) rather than a managed Kubernetes service like AKS; the Kubernetes requirement is satisfied by the course's Rancher-managed RKE2 cluster instead. Both requirements are met, just not by the same deployment target — provisioning a second Kubernetes cluster on Azure was judged not to add anything beyond what the VM and the RKE2 cluster already each demonstrate independently.