From c4-skills
Model deployment environments using C4 deployment diagrams and Structurizr DSL. Use when adding deployment views, modeling infrastructure, documenting production/staging environments, or mapping containers to infrastructure.
How this skill is triggered — by the user, by Claude, or both
Slash command
/c4-skills:c4-deploymentThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Model deployment environments using C4 deployment diagrams and Structurizr DSL.
Model deployment environments using C4 deployment diagrams and Structurizr DSL.
workspace.dsl) with the software system and its containers already definedDeployment diagrams answer: "Where does it run?"
They show the mapping from C4 containers to physical or virtual infrastructure, for a specific environment. Create separate deployment views for production, staging, development, etc.
| Element | What It Represents | Examples |
|---|---|---|
| Deployment Node | Something containers are deployed onto | Physical server, VM, Docker host, Kubernetes cluster, AWS region, browser |
| Infrastructure Node | Supporting infrastructure | Load balancer, DNS, firewall, CDN, API gateway |
| Software System Instance | A deployed instance of a software system | Only for external systems in deployment context |
| Container Instance | A deployed instance of a container | Your API running on a specific server |
Deployment nodes host containers — they run your code or store your data:
Infrastructure nodes support the system but don't host containers:
model {
# Define your system and containers first
system = softwareSystem "System" {
api = container "API" "REST API" "Go"
db = container "Database" "Data store" "PostgreSQL"
web = container "Web App" "SPA" "React"
}
# Then define deployment environments
production = deploymentEnvironment "Production" {
# deployment nodes, instances, infrastructure
}
staging = deploymentEnvironment "Staging" {
# simpler version of production
}
}
deploymentNode <name> [description] [technology] [tags] [instances] {
# nested nodes, instances, infrastructure
}
Nodes can be nested to show hierarchical infrastructure:
deploymentEnvironment "Production" {
aws = deploymentNode "AWS" "" "Amazon Web Services" {
region = deploymentNode "eu-west-1" "" "AWS Region" {
vpc = deploymentNode "VPC" "" "AWS VPC" {
subnet = deploymentNode "Private Subnet" "" "AWS Subnet" {
ec2 = deploymentNode "EC2 Instance" "" "t3.medium" "" 2 {
containerInstance system.api
}
}
}
}
}
}
deploymentNode "Web Server" "" "Nginx" "" 4 # exactly 4
deploymentNode "Worker" "" "Go" "" "1..N" # 1 to N (elastic)
deploymentNode "Standby" "" "PostgreSQL" "" "0..1" # 0 or 1 (failover)
deploymentNode "Shard" "" "MongoDB" "" "3..6" # 3 to 6
deploymentNode "App Server" "" "Ubuntu" {
containerInstance system.api
containerInstance system.worker
}
Used for external systems in deployment context:
deploymentNode "External" {
softwareSystemInstance externalPaymentSystem
}
deploymentNode "AWS" {
route53 = infrastructureNode "Route 53" "DNS" "AWS Route 53"
alb = infrastructureNode "ALB" "Load balancer" "AWS ALB"
cdn = infrastructureNode "CloudFront" "CDN" "AWS CloudFront"
# Infrastructure nodes can have relationships
route53 -> cdn "resolves to"
cdn -> alb "forwards to"
}
Control which instances are linked when you have multiple replicas:
deploymentEnvironment "Production" {
primaryGroup = deploymentGroup "Primary"
secondaryGroup = deploymentGroup "Secondary"
deploymentNode "Primary Server" {
containerInstance system.api primaryGroup
containerInstance system.db primaryGroup
}
deploymentNode "Secondary Server" {
containerInstance system.api secondaryGroup
containerInstance system.db secondaryGroup
}
}
Without deployment groups, all instances of container A would be linked to all instances of container B. Deployment groups restrict relationships to instances within the same group.
containerInstance system.api {
healthCheck "API Health" "https://api.example.com/health" 30 5000
# name, URL, interval (seconds), timeout (milliseconds)
}
views {
deployment system "Production" "ProductionDeployment" {
include *
autoLayout lr
title "Production Deployment"
description "How the system is deployed in production"
}
deployment system "Staging" "StagingDeployment" {
include *
autoLayout lr
title "Staging Deployment"
}
# Wildcard: show all systems in an environment
deployment * "Production" "FullProductionDeployment" {
include *
autoLayout lr
}
}
styles {
element "Deployment Node" {
shape RoundedBox
background #ffffff
color #000000
stroke #888888
}
element "Infrastructure Node" {
shape RoundedBox
background #ffffff
color #000000
stroke #888888
}
}
deploymentEnvironment "Production" {
server = deploymentNode "Server" "Single production server" "Ubuntu 22.04" {
nginx = infrastructureNode "Nginx" "Reverse proxy" "Nginx"
appNode = deploymentNode "Application" "" "Systemd Service" {
containerInstance system.api
}
dbNode = deploymentNode "Database" "" "PostgreSQL 16" {
containerInstance system.db
}
nginx -> appNode "proxies to" "HTTP/localhost:4000"
}
}
deploymentEnvironment "Production" {
cloud = deploymentNode "Cloud Provider" "" "GCP" {
cluster = deploymentNode "GKE Cluster" "" "Kubernetes 1.29" {
apiPod = deploymentNode "api Pod" "" "Kubernetes Pod" "" "1..N" {
containerInstance system.api
}
workerPod = deploymentNode "worker Pod" "" "Kubernetes Pod" "" "1..N" {
containerInstance system.worker
}
}
cloudSql = deploymentNode "Cloud SQL" "" "GCP Cloud SQL" {
containerInstance system.db
}
lb = infrastructureNode "Cloud Load Balancer" "Ingress" "GCP LB"
lb -> apiPod "routes to" "HTTPS"
}
}
views {
theme amazon-web-services-2020.04.30
}
model {
deploymentEnvironment "Production" {
aws = deploymentNode "AWS" "" "" "Amazon Web Services - Cloud" {
region = deploymentNode "eu-west-1" "" "" "Amazon Web Services - Region" {
alb = infrastructureNode "ALB" "" "" "Amazon Web Services - Elastic Load Balancing"
deploymentNode "ECS" "" "" "Amazon Web Services - Elastic Container Service" {
containerInstance system.api
}
deploymentNode "RDS" "" "" "Amazon Web Services - RDS" {
containerInstance system.db
}
}
}
}
}
Tag elements with the exact AWS/Azure/GCP theme tag names to get provider icons.
deploymentEnvironment "Development" {
devMachine = deploymentNode "Developer Machine" "" "macOS/Linux" {
podman = deploymentNode "Podman" "" "Podman" {
appContainer = deploymentNode "app" "" "Container" {
containerInstance system.api
}
dbContainer = deploymentNode "db" "" "Container" {
containerInstance system.db
}
}
browser = deploymentNode "Browser" "" "Chrome/Firefox" {
containerInstance system.web
}
browser -> appContainer "connects to" "HTTP/localhost:4000"
}
}
A standard Phoenix/OTP release deployed as a systemd service with clustering:
model {
system = softwareSystem "System" {
phoenix = container "Phoenix Application" "Web application and API" "Elixir/Phoenix"
liveview = container "LiveView UI" "Real-time user interface" "Phoenix LiveView"
oban = container "Oban Workers" "Background job processing" "Oban"
db = container "Database" "Data store" "PostgreSQL"
}
deploymentEnvironment "Production" {
server = deploymentNode "App Server" "" "Ubuntu 22.04" "" 2 {
beam = deploymentNode "BEAM VM" "Erlang runtime" "Erlang/OTP 27" {
release = deploymentNode "Release" "" "mix release" {
containerInstance system.phoenix
containerInstance system.liveview
containerInstance system.oban
}
}
}
dbServer = deploymentNode "Database Server" "" "Ubuntu 22.04" {
deploymentNode "PostgreSQL" "" "PostgreSQL 16" {
containerInstance system.db
}
}
lb = infrastructureNode "Load Balancer" "Reverse proxy, SSL termination, WebSocket upgrade" "Nginx"
lb -> server "proxies to" "HTTP/4000, WebSocket"
server -> dbServer "queries" "TCP/5432"
server -> server "cluster communication" "Erlang Distribution/EPMD"
}
}
Key modeling decisions for Elixir/OTP:
deploymentEnvironment "Production" {
deploymentNode "Kubernetes" "" "K8s 1.29" {
pod1 = deploymentNode "app-0" "" "Kubernetes Pod" {
beam1 = deploymentNode "BEAM VM" "" "Erlang/OTP 27" {
containerInstance system.phoenix
}
}
pod2 = deploymentNode "app-1" "" "Kubernetes Pod" {
beam2 = deploymentNode "BEAM VM" "" "Erlang/OTP 27" {
containerInstance system.phoenix
}
}
pod3 = deploymentNode "app-2" "" "Kubernetes Pod" {
beam3 = deploymentNode "BEAM VM" "" "Erlang/OTP 27" {
containerInstance system.phoenix
}
}
# libcluster forms an Erlang mesh
pod1 -> pod2 "Erlang distribution" "BEAM Clustering/libcluster"
pod2 -> pod3 "Erlang distribution" "BEAM Clustering/libcluster"
pod1 -> pod3 "Erlang distribution" "BEAM Clustering/libcluster"
}
deploymentNode "Cloud SQL" "" "PostgreSQL 16" {
containerInstance system.db
}
}
deploymentEnvironment "Production" {
fly = deploymentNode "Fly.io" "" "Fly.io" {
region1 = deploymentNode "cdg (Paris)" "" "Fly Region" {
machine1 = deploymentNode "Fly Machine" "" "Firecracker VM" "" "1..N" {
deploymentNode "BEAM VM" "" "Erlang/OTP 27" {
containerInstance system.phoenix
}
}
}
region2 = deploymentNode "ams (Amsterdam)" "" "Fly Region" {
machine2 = deploymentNode "Fly Machine" "" "Firecracker VM" "" "1..N" {
deploymentNode "BEAM VM" "" "Erlang/OTP 27" {
containerInstance system.phoenix
}
}
}
flyProxy = infrastructureNode "Fly Proxy" "Anycast routing, TLS termination" "Fly Proxy"
dns = infrastructureNode "Fly DNS" "Internal DNS for clustering" "Fly DNS"
flyProxy -> machine1 "routes to nearest" "HTTP/HTTPS"
flyProxy -> machine2 "routes to nearest" "HTTP/HTTPS"
region1 -> region2 "Erlang distribution" "WireGuard/6PN"
pgFly = deploymentNode "Fly Postgres" "" "Fly Managed Postgres" {
containerInstance system.db
}
}
}
When an Elixir umbrella deploys as a single release, model each umbrella app as a C4 component within one container, not as separate containers:
model {
system = softwareSystem "System" {
# The release is ONE container — umbrella apps are components
release = container "Application Release" "Umbrella release" "Elixir/OTP" {
webApp = component "Web" "Phoenix endpoint and LiveView" "Phoenix"
core = component "Core" "Business logic and domain" "Elixir"
ingestion = component "Ingestion" "Data pipeline" "Broadway"
notifications = component "Notifications" "Email and push" "Swoosh"
webApp -> core "calls" "Elixir function calls"
core -> ingestion "triggers" "GenServer.call"
core -> notifications "sends via" "Elixir function calls"
}
db = container "Database" "Data store" "PostgreSQL"
queue = container "Message Queue" "Event streaming" "RabbitMQ"
release -> db "queries" "Ecto/TCP"
release.ingestion -> queue "consumes from" "AMQP/Broadway"
}
deploymentEnvironment "Production" {
server = deploymentNode "App Server" "" "Ubuntu 22.04" "" 2 {
deploymentNode "BEAM VM" "" "Erlang/OTP 27" {
containerInstance system.release
}
}
deploymentNode "Database Server" "" "Ubuntu 22.04" {
containerInstance system.db
}
deploymentNode "Queue Server" "" "Ubuntu 22.04" {
containerInstance system.queue
}
}
}
If umbrella apps are deployed as separate releases (separate BEAM instances), model each as a separate C4 container instead.
deploymentEnvironment "Production" {
primaryRegion = deploymentNode "Primary (eu-west-1)" "" "AWS Region" {
deploymentNode "Primary DB" "" "PostgreSQL" {
containerInstance system.db
}
deploymentNode "App Servers" "" "EC2" "" "2..4" {
containerInstance system.api
}
}
secondaryRegion = deploymentNode "Secondary (eu-west-2)" "" "AWS Region" {
deploymentNode "Standby DB" "" "PostgreSQL (Read Replica)" "" "0..1" {
containerInstance system.db
}
}
primaryRegion -> secondaryRegion "replicates to" "PostgreSQL Streaming Replication"
}
When modeling deployment, verify:
deploymentEnvironment per env)containerInstance in productionautoLayout lr is used (left-to-right often works better for deployment)After adding deployment views, validate the workspace. Mount the project root (so any !adrs / !docs references resolve) and point at the DSL file:
podman run --rm -v "$(pwd):/work:z" structurizr/structurizr validate -w /work/architecture/workspace.dsl
Then lint the model (violations by severity; exit code = the violation count):
podman run --rm -v "$(pwd):/work:z" structurizr/structurizr inspect -w /work/architecture/workspace.dsl -s error,warning
See c4-review for a full model review.
c4-model — C4 abstractions and diagram typesstructurizr-dsl — Full DSL syntax referencec4-review — Review models for correctnessnpx claudepluginhub jrjsmrtn/jrjsmrtn-skills --plugin c4-skillsGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
Dispatches multiple subagents concurrently for independent tasks without shared state. Use when facing 2+ unrelated failures or subsystems that can be investigated in parallel.