DocumentationArchitecture Practices

Deployment Diagrams: The C4 Way to Stop Confusing Everyone

OL
Oscar van der Leij
10 min read
Deployment Diagrams: The C4 Way to Stop Confusing Everyone

A senior architect at a healthcare tech company was presenting their new microservices platform to the executive team. Fifteen minutes in, the CTO interrupted: "Wait, so where does this actually run? Are we talking containers? VMs? Which cloud regions?" The architect pulled up what they thought was their deployment diagram: A tangled mess of boxes, lines, and AWS icons that looked like it had been designed by a caffeinated spider. The CTO squinted at the screen, then at the architect, then back at the screen. "I’ll just... ask the DevOps team later."

Sound familiar? We’ve all been there. You know your system inside and out, but when it comes time to explain where everything actually lives and how it’s deployed, your diagrams turn into abstract art that would make Picasso proud and your stakeholders confused.

The Deployment Diagram Dilemma

Deployment diagrams are supposed to answer the simplest questions. Where does this service run? What infrastructure do we need? How do the pieces connect in the real world? But, they often end up being the most confusing artifacts in our documentation arsenal.

The problem isn’t that we don’t understand our deployments. It’s that we try to show everything at once: infrastructure, networking, containers, orchestration, monitoring, security. All crammed into one diagram that requires a PhD to decipher. As Simon Brown, creator of the C4 model, puts it: "If you can’t explain your architecture in five minutes, you probably don’t understand it well enough yourself."

This matters because deployment diagrams are where the rubber meets the road. They’re what your DevOps team uses to provision infrastructure, what your security team uses to assess attack surfaces, and what your finance team uses to estimate cloud costs. Get them wrong, you’re not just confusing people, you’re setting everyone up for expensive mistakes.

Think of It as a Travel Itinerary

Before we dive into the C4 approach, let’s establish what a deployment diagram actually needs to accomplish. Think of it like planning a vacation itinerary for a friend. You don’t hand them an entire world atlas with every road, building, and landmark marked. Instead, you show them: "You’ll fly into Denver, stay at the Marriott downtown, take this specific shuttle to the ski resort, and your equipment will be stored in locker 42."

That’s what a good deployment diagram does. It shows the journey your software takes from code to running system: which deployment nodes (servers, containers, regions) host which containers (applications, services, databases), and how they’re connected in the physical world. Not every cable, not every firewall rule, not every load balancer setting. Just the essential topology that helps people understand where things live and how they communicate.

What Actually Goes in the Box

Let’s break down what makes a C4 deployment diagram different from the infrastructure spaghetti you might be used to.

Deployment Nodes: The Physical Homes

A deployment node represents something that can execute or host software. This could be:

  • Physical Infrastructure: Bare metal servers, network appliances
  • Virtualized Infrastructure: VMs, virtual networks
  • Containerized Infrastructure: Kubernetes clusters, Docker hosts
  • Cloud Services: AWS regions, Azure resource groups, managed services
  • Client Devices: Web browsers, mobile devices, desktop applications

The key is that deployment nodes can be nested. You might have an AWS Region containing a VPC, which contains an EKS cluster, which contains pods. This nesting shows the hierarchical relationship without cluttering the diagram.

deploymentEnvironment "Production" {
    deploymentNode "AWS US-East-1" {
        tags "Amazon Web Services - Region"

        deploymentNode "Production VPC" {
            tags "Amazon Web Services - VPC"

            deploymentNode "EKS Cluster" {
                tags "Amazon Web Services - EKS"

                deploymentNode "API Pod" {
                    containerInstance apiContainer
                    instances 3
                }

                deploymentNode "Worker Pod" {
                    containerInstance workerContainer
                    instances 5
                }
            }
        }
    }
}

AWS US-East-1
[Deployment Node]
AWS US-East-1...
Worker
[Container]


Processes background jobs
Worker...
API
[Container]


Handles API requests
API...
Production VPC
[Deployment Node]
Production VPC...
EKS Cluster
[Deployment Node]
EKS Cluster...
Worker Pod
[Deployment Node]
Worker Pod...
API Pod
[Deployment Node]
API Pod...
x5
x5
x3
x3

Container Instances: What’s Actually Running

Here’s where C4 really proves its value. Instead of drawing your services from scratch on every diagram, you reference the containers you already defined in your Container diagram. A container instance shows where a specific container (from your earlier C4 diagrams) is deployed.

This creates a beautiful thread through your documentation. Your Container diagram shows what your system is made of. Your Deployment diagram shows where those things live.

Element Type Purpose Example
Deployment Node Physical or virtual infrastructure AWS Region, Kubernetes Cluster, Web Browser
Container Instance A deployed instance of a container 3 instances of "API Application" running in pods
Infrastructure Node Supporting infrastructure Load balancers, message queues, databases
Relationship How deployed elements communicate HTTPS over internet, internal VPC traffic

Relationships: The Real-World Connections

The relationships in a deployment diagram show the actual protocols and infrastructure paths. Not "calls" or "uses", but "HTTPS over port 443" or "PostgreSQL wire protocol over internal VPC network."

This is where you can add deployment-specific details that matter:

apiPod -> databaseNode "Reads from and writes to" "PostgreSQL wire protocol (port 5432)" {
    tags "Internal Network"
}

loadBalancer -> apiPod "Forwards requests to" "HTTPS (port 443)" {
    tags "TLS Terminated"
}

userBrowser -> loadBalancer "Makes API calls to" "HTTPS (port 443)" {
    tags "Public Internet"
}

AWS US-East-1
[Deployment Node]
AWS US-East-1...
Worker
[Container]


Processes background jobs
Worker...
API
[Container]


Handles API requests
API...
Production VPC
[Deployment Node]
Production VPC...
EKS Cluster
[Deployment Node]
EKS Cluster...
Worker Pod
[Deployment Node]
Worker Pod...
API Pod
[Deployment Node]
API Pod...
x5
x5
x3
x3
ALB
[Infrastucture Node]


ALB...
Application Load Balancer
[Deployment Node]
Application Load Balancer...
Forwards requests to
[HTTPS (port 443)]
Forwards requests to...
PostgreSQL Database
[Infrastucture Node]


PostgreSQL Database...
RDS PostgreSQL
[Deployment Node]
RDS PostgreSQL...
Reads from and writes to
[PostgreSQL wire protocol (port 5432)]
Reads from and writes to...
Browser
[Infrastucture Node]


Browser...
User Browser
[Deployment Node]
User Browser...
Makes API calls to
[HTTPS (port 443)]
Makes API calls to...

Time to Build Your First C4 Deployment Diagram

Let’s walk through creating a deployment diagram for a realistic scenario: a multi-tier web application deployed on Azure.

Step 1: Start with Your Deployment Environments

Most systems have multiple environments. Don’t try to show them all in one diagram. Create separate deployment diagrams for each. Start with production, as it’s usually the most complex and important.

workspace {
    model {
        // First, define your containers (from your Container diagram)
        softwareSystem = softwareSystem "Patient Portal" {
            webApp = container "Web Application" "React SPA" "TypeScript/React"
            apiApp = container "API Application" "REST API" "ASP.NET Core"
            database = container "Database" "Patient data" "Azure SQL"
        }

        // Then define deployment for Production
        deploymentEnvironment "Production" {
            deploymentNode "Azure East US 2" {
                tags "Microsoft Azure - Region"

                deploymentNode "Production Resource Group" {
                    // We’ll add more here
                }
            }
        }
    }
}

Step 2: Model Your Infrastructure Hierarchy

Think about the nesting. What contains what? In Azure, you might have: Region > Resource Group > App Service Plan > App Service. In AWS: Region > VPC > Subnet > EC2 Instance.

userNode = deploymentNode "User Browser" {
    infrastructureNode "Browser"
}

deploymentNode "Azure East US 2" {
    deploymentNode "Production Resource Group" {
        cdnNode = deploymentNode "CDN" {
            tags "Microsoft Azure - CDN"
            webAppInstance = containerInstance webApp
        }

        deploymentNode "App Service Plan (P2V2)" {
            tags "Microsoft Azure - App Service Plans"


            deploymentNode "API App Service" {
                properties {
                    "SKU" "P2V2"
                    "Instances" "2-10 (autoscale)"
                    "Runtime" ".NET 8"
                    "Always On" "true"
                }
                tags "Microsoft Azure - App Services"
                apiAppInstance = containerInstance apiApp
                instances 2
            }
        }

        deploymentNode "SQL Server" {
            tags "Microsoft Azure - SQL Server"

            deploymentNode "SQL Database (S2)" {
                tags "Microsoft Azure - SQL Database"
                databaseInstance = containerInstance database
            }
        }
    }
}

Step 3: Add the Relationships with Deployment Details

Now connect the dots with real-world protocols and infrastructure paths:

softwareSystem = softwareSystem "Patient Portal" {
    webApp = container "Web Application" "React SPA" "TypeScript/React"
    apiApp = container "API Application" "REST API" "ASP.NET Core"
    database = container "Database" "Patient data" "Azure SQL"

    webApp -> apiApp "Makes API calls to" "HTTPS (443)"
    apiApp -> database "Reads/writes" "TDS Protocol (1433)"
}

userNode -> cdnNode "Views" "HTTPS (443)" {
    tags "Public Internet"
}

Step 4: Add Deployment-Specific Details

This is where you capture information that only matters at deployment time:

  • Number of instances
  • Machine sizes/SKUs
  • Scaling configuration
  • Geographic regions
  • Network zones
deploymentNode "API App Service" {
    properties {
        "SKU" "P2V2"
        "Instances" "2-10 (autoscale)"
        "Runtime" ".NET 8"
        "Always On" "true"
    }
    tags "Microsoft Azure - App Services"
    apiAppInstance = containerInstance apiApp
    instances 2
}

Azure East US 2
[Deployment Node]
Azure East US 2...
Database
[Container]


Patient data
Database...
API Application
[Container]


REST API
API Application...
Production Resource Group
[Deployment Node]
Production Resource Group...
App Service Plan (P2V2)
[Deployment Node]
App Service Plan (P2V2)...
API App Service
[Deployment Node]
API App Service...
x2
x2
CDN
[Deployment Node]
CDN...
Makes API calls to
[HTTPS (port 443)]
Makes API calls to...
SQL Database (S2)
[Deployment Node]
SQL Database (S2)...
Reads/writes
[TDS Protocol (1433)]
Reads/writes...
Browser
[Infrastucture Node]


Browser...
User Browser
[Deployment Node]
User Browser...
Views
[HTTPS (port 443)]
Views...
Web Application
[Container]


React SPA
Web Application...
SQL Server
[Deployment Node]
SQL Server...

Step 5: Validate with Your Teams

Here’s the real test: show your deployment diagram to three people:

  1. A developer: Can they identify where their code runs?
  2. A DevOps engineer: Can they provision the infrastructure from this?
  3. A non-technical stakeholder: Can they understand the basic topology?

If all three can make sense of it in under five minutes, you’ve succeeded.

But What About...?

Once you’ve mastered the basics, you’ll inevitably face more complex deployment scenarios. Here’s how to handle the most common edge cases while keeping your diagrams clean and understandable.

Multiple Regions and Disaster Recovery

Create separate deployment diagrams for each region, or use a high-level diagram showing just the regional distribution:

deploymentEnvironment "Production - Multi-Region" {
    deploymentNode "Primary Region (East US 2)" {
        primaryDB = deploymentNode "SQL Server (Primary)" {
            infrastructureNode "Primary Database"
        }
    }

    deploymentNode "Secondary Region (West US 2)" {
        secondaryDB = deploymentNode "SQL Server (Secondary)" {
            infrastructureNode "Secondary Database"
        }
    }

    primaryDB -> secondaryDB "Geo-replicates to" "Azure SQL Geo-Replication"
}

Hybrid Cloud or On-Premises

Use deployment nodes to show the boundary between cloud and on-prem:

deploymentNode "On-Premises Data Center" {
    legacySystemNode = deploymentNode "Legacy System Server" {
        containerInstance webApp
    }
}

deploymentNode "AWS US-East-1" {
    apiNode = deploymentNode "Application VPC" {
        containerInstance apiApp
    }
}

apiNode -> legacySystemNode "Integrates with" "HTTPS over VPN (443)"

Serverless and Managed Services

Serverless functions are containers too. Show them as container instances within the serverless platform:

deploymentNode "AWS Lambda" {
    tags "Amazon Web Services - Lambda"

    deploymentNode "Order Processing Function" {
        properties {
            "Memory" "1024 MB"
            "Timeout" "30 seconds"
            "Concurrency" "100"
        }
        orderFunctionInstance = containerInstance orderProcessor
    }
}

The Rubber Duck Method

The best way to validate your deployment diagram is the "explain it to a rubber duck" test. Or better yet, to a new team member. Walk through these questions:

  1. Can you trace a user request? Start from a user's device and follow the path through every deployment node to the database and back.
  2. Can you identify single points of failure? Look for deployment nodes with only one instance or no redundancy.
  3. Can you estimate the cost? If you can’t roughly calculate cloud costs from your diagram, you’re missing important details.
  4. Can you explain the deployment process? How does code get from your CI/CD pipeline into these deployment nodes?

Your Deployment Diagram Survival Kit

A good C4 deployment diagram should:

  • Show where containers run, not just what they are. Link back to your Container diagram and show the physical/virtual infrastructure hosting each container
  • Use nested deployment nodes to represent infrastructure hierarchy without cluttering the diagram with every detail
  • Specify real protocols and paths in relationships. "HTTPS over public internet" is infinitely more useful than "uses"
  • Create separate diagrams for each environment. Don’t try to show dev, staging, and production in one overwhelming diagram

The C4 deployment diagram is about answering the fundamental question: "Where does my software actually run, and how does it get there?" When your CTO interrupts your next presentation with "But where does this run?", you’ll have a clear, unambiguous answer ready.

Take your most complex system and try to create a C4 deployment diagram for it. Can you explain it to someone in five minutes? If not, you might be trying to show too much. What would you remove to make it clearer? And more importantly, would your DevOps team be able to provision the infrastructure from your diagram alone?

Share this article

Enjoyed this article?

Subscribe to get more insights delivered to your inbox monthly

Subscribe to Newsletter