Skip to content
Kevo Rojas

Private architecture on Azure Container Apps with Terraform: the full series, end to end

KR
Kevo Rojas
Jul 9, 2026 · 10 min read

I build a private architecture on Azure Container Apps with Terraform from scratch: VNet, no public ingress, OIDC pipelines. A 13-chapter series.

In short: This series builds, from scratch and with Terraform, a private architecture on Azure Container Apps: a platform where services talk to each other only over the internal network (VNet), with nothing exposed to the internet except a single controlled door. It's 13 chapters, and each chapter is a layer (networking, private registry and secrets, passwordless identity, separate pipelines, observability), each with its own article and video. This is chapter 0: the full map before writing the first line of infrastructure.

Hi, let me introduce the project

Hi there. I want to show you the project I'm building, and that you'll follow along with me in this series. The idea is to build, step by step, a secure architecture on Azure where services talk to each other only over the internal network (the VNet) using Azure Container Apps. Nothing is exposed to the internet except what we choose to expose, explicitly, behind a single controlled door. In one sentence: nothing gets in or out without permission. Everything is private by default (the network, the image registry, the secrets, the storage) and public is the exception, not the rule.

If you're interested in seeing how a private, secure architecture on Azure is actually put together (not loose pieces, but the whole platform end to end), this is the place. It's not a "hello world" tutorial: it's the kind of platform you run inside a company, with an isolated network, passwordless identities, pipelines split by permissions, progressive delivery and monitoring. The app running inside is minimal on purpose; the star is the infrastructure that protects it.

The project is 13 chapters, and each chapter is a layer. In each one you'll see the real Terraform code, how the CI/CD is solved, and how the pieces connect to one another. You can read them in order (they build on top of each other) or on their own, jumping straight to the topic you care about. This chapter 0 is the full walkthrough: the map of everything that's coming.

One note before we start: you don't need to be an expert. It helps to be comfortable with the terminal, to have a basic grasp of networking, and to have touched Terraform at some point. If you already know Azure and Terraform at a high level and you want the real how (the modules, the decisions and the gotchas that don't show up in the official docs), or if you're evaluating Azure Container Apps for private workloads and wondering how far it goes before jumping to Kubernetes, you'll get a lot out of this series.

The app, first on local

Before touching Azure, the app runs entirely on your machine with a single docker compose up. It's four services working together: web (the interface), api (the backend), processor (validates and stores) and catalog (the index). You upload a document from the web and it shows up in the catalog, without reloading the page. The full code lives in the app's public repository: github.com/kevorojas/container-apps-demo. Clone it, read it and bring it up with one command; there you'll see how everything is wired together.

What runs on local is the bare app: there's no VNet, no managed identity, no private endpoints, no Application Gateway. And that's on purpose. These same four services are the ones we'll host privately and securely on Azure throughout the series; the only thing that changes is the hosting, the app stays the same. It reinforces the underlying idea: the app is trivial, the star is the platform that protects it.

The architecture, at a glance

These are the layers built across the series, in order. Each one is an episode:

  • Private network (VNet). A virtual network with delegated subnets, security rules (NSG) and Private DNS zones. It's the ground everything else lives on.
  • Private registry and secrets. The container registry (ACR) and the Key Vault connect over private endpoints, with no public access. Images and secrets never travel over the internet.
  • Internal Container Apps Environment. The heart of the architecture: an environment integrated into the VNet and with no public ingress. The services exist, but from the internet you can't see them.
  • Passwordless identity. The apps run with managed identity: they pull from the registry and read secrets from the Key Vault without a single password or connection string.
  • Internal communication. The app's services talk to each other only over the internal network (intra-VNet), never leaving for the internet at any point, including the hop from the frontend (web) to the backend (api).
  • A single door. An Application Gateway with WAF out front exposes only the entry service. It's the only public thing in the whole architecture.
  • Progressive delivery and autoscaling. Container Apps brings revisions with traffic splitting (native canary) and event-driven autoscaling with KEDA, including scale-to-zero.
  • Two pipelines, separate permissions. An infrastructure pipeline with broad permissions and an application one with least privilege, each with its own federated identity (OIDC), no long-lived secrets.
  • Observability. Azure Monitor, Log Analytics and managed Grafana to see what's happening inside.

The payload: secure document intake

Inside runs a minimal FastAPI app with four services that collaborate without anything leaving for the internet. Here's how they talk to each other:

  • web is the frontend (BFF): a minimal two-screen web UI (upload a document and list the catalog), server-rendered with HTMX. It's the only service with ingress, behind the Application Gateway. It doesn't touch storage or the catalog directly: it calls the api over the internal network (intra-VNet).
  • api is the backend, internal and with no ingress (only the web can reach it). It receives the document from the web and doesn't touch storage directly: it hands it off to the processor over the internal network.
  • processor validates the document (hash, size, type) and writes it to private storage using managed identity (no connection strings). When it's done, it notifies the catalog (another internal call) that the document has been saved.
  • catalog keeps the index and the metadata. When someone asks to "list documents", the api queries the catalog (again, over the internal network) and returns the response to the web.

The full flow is webapiprocessorstorage, with processorcatalog for the index, and apicatalog for listing. Nothing touches storage directly or leaves for the internet uncontrolled, and there's a single public door: the interface comes in where everything comes in, behind the Application Gateway; the api and the rest stay internal. The webapi hop is, on top of that, another example of the series' core theme: private service-to-service communication inside the VNet. The app is trivial on purpose: it exists to put the infrastructure to the test (internal communication, identity and private storage in action), not to be a product.

Why Azure Container Apps and not AKS?

Azure Container Apps is a serverless container platform managed by Azure: it runs containers with automatic scaling and private networking, without administering a Kubernetes cluster. I start the series here, and not with AKS, for a fundamental reason: when you don't have that many services, standing up a Kubernetes cluster isn't justified.

A Kubernetes cluster (AKS or whichever) carries a big operational load: you have to maintain the cluster, do version upgrades, manage control-plane networking, node pools, add-ons, RBAC. All of that makes sense when you run dozens of services with a platform team behind them. But to host two or three applications, that overhead doesn't pay off: you spend more time operating the cluster than on what you set out to build. Container Apps gives you what you need without that baggage:

  1. Serverless and scale-to-zero. You don't administer nodes or pay for idle capacity; the environment scales on its own, even down to zero when there's no traffic.
  2. Native VNet and private ingress. Having apps that talk only at the VNet level, with no public ingress, is a capability out of the box. There's nothing to hand-build.
  3. Progressive delivery and autoscaling included. Revisions with traffic splitting and event-driven autoscaling (KEDA built in) come standard. In Kubernetes that's solved with Argo Rollouts and KEDA installed and maintained separately.

This is not "Container Apps beats AKS". It's picking the right tool for the size of the problem. When the cluster is justified (many services, GitOps, fine-grained control) the answer changes, and that's exactly the thread of the next season: a second project chasing the same goal with private AKS and GitOps, to answer the question that ties them together (when Container Apps and when AKS?). For now, all the focus is on this architecture.

The index: the 13 episodes coming up

Each episode is an article with its video. You can read them in order (they build on top of each other) or jump straight to the topic you care about. This is the full map of what's coming; each link goes live when I publish that episode.

Foundations

  1. The foundations: Terraform on Azure with remote state and locking. Repo structure, remote state backend, locking and authentication to Azure. (Coming soon)
  2. Infrastructure pipeline: deploying Terraform with GitHub Actions and OIDC. Deploying the infra without secrets, with a broad-permission federated identity. (Coming soon)

The private network and its foundations

  1. Private networking on Azure: VNet, NSG and Private DNS zones with Terraform. The network everything else lives on. (Coming soon)
  2. Private ACR and Key Vault: Private Endpoints with Terraform. Image registry and secrets with no public access. (Coming soon)

The heart: Container Apps

  1. Internal Container Apps Environment: VNet-integrated and with no public ingress. The central piece, nothing exposed to the internet. (Coming soon)
  2. Passwordless identity: managed identity on Container Apps (ACR + Key Vault). Apps that run without a single password. (Coming soon)
  3. Internal communication between Container Apps: service-to-service in the VNet. Services talking only over the internal network. (Coming soon)

Expose, scale and deploy well

  1. Controlled exposure: Application Gateway and WAF in front of Container Apps. The only open door in the whole architecture. (Coming soon)
  2. Event-driven autoscaling on Container Apps: KEDA and scale-to-zero. Scaling only when needed, and to zero when not. (Coming soon)
  3. The app pipeline with least privilege: OIDC, Trivy and deploy to Container Apps. A pipeline that can't touch the infrastructure. (Coming soon)
  4. Native progressive delivery: revisions and traffic splitting on Container Apps. Canary with no extra tooling. (Coming soon)

Operate and wrap up

  1. Observability on Container Apps: Azure Monitor, Log Analytics and Grafana. Seeing everything that happens inside. (Coming soon)
  2. What did it all cost? Real costs, terraform destroy and what I'd improve. The honest bill and the final shutdown. (Coming soon)

What do you need to follow the series?

  • An Azure account. The free trial (USD 200 credit for 30 days) is more than enough to reproduce this architecture with the ephemeral-labs strategy I use.
  • Terraform and the Azure CLI installed.
  • A basic grasp of networking (what a subnet is, DNS) and of containers (an image, a registry). You don't need to master Kubernetes.
  • The itch to build. The app's code lives in a public repo that serves as a reference as you go: github.com/kevorojas/container-apps-demo. The infrastructure will have its own repository, which I'll link once I publish the first chapters.

Next episode

Ep 1 — The foundations: Terraform on Azure with remote state and locking (Coming soon). Before creating a single private resource, you have to lay the foundations: how I structure the repository, where I store Terraform's state remotely and with locking, and how I authenticate Terraform against Azure without leaving secrets lying around. It's the base the whole architecture rests on.


I publish one chapter of the series a week. If you want me to let you know when the next one is out, leave me your email. No spam, just the heads-up when there's something new.

Get new tutorials by email

I email you when I publish a tutorial or a video. No spam; unsubscribe anytime.