Module dependency graph¶
Terraform infers ordering from data flow, but these modules mostly exchange no outputs. They race for the same cluster. stage2/main.tf therefore declares ordering explicitly with depends_on.
One edge is implicit rather than declared: argocd reads module.monitoring.monitoring_namespace, which Terraform enforces exactly like a depends_on. It is drawn dotted below.
Dashed nodes are gated by an enable flag and may be absent from the plan entirely.
flowchart TD
preflight["preflight<br/>chart compatibility gate"]
kubernetes["kubernetes"]
istiogw["istio_gateway"]
certmanager["cert_manager_letsencrypt"]
longhorn["longhorn_storage"]
minio["minio_object_storage"]
monitoring["monitoring"]
kubecost["kubecost<br/>kubecost_enable"]:::optional
auth["auth"]
argocd["argocd"]
vpn["vpn"]
reloader["reloader"]
gitlab["gitlab_platform<br/>amd64 only"]:::optional
logging["logging<br/>logging_module_enable"]:::optional
cloudflare["cloudflare_tunnel<br/>cloudflare_tunnel_enable"]:::optional
updater["argocd_image_updater<br/>argocd_image_updater_enable"]:::optional
datadog["datadog<br/>datadog_enable"]:::optional
sealed["sealed_secrets<br/>sealed_secrets_enable"]:::optional
litellm["litellm<br/>litellm_enable"]:::optional
omniroute["omniroute<br/>omniroute_enable"]:::optional
preflight --> kubernetes
kubernetes --> istiogw
kubernetes --> vpn
kubernetes --> reloader
kubernetes --> sealed
certmanager --> istiogw
certmanager --> longhorn
certmanager --> logging
certmanager --> monitoring
certmanager --> kubecost
certmanager --> datadog
certmanager --> auth
certmanager --> litellm
certmanager --> omniroute
certmanager --> argocd
istiogw --> auth
istiogw --> longhorn
istiogw --> minio
istiogw --> monitoring
istiogw --> kubecost
istiogw --> gitlab
istiogw --> argocd
istiogw --> logging
istiogw --> litellm
istiogw --> omniroute
longhorn --> minio
longhorn --> litellm
longhorn --> omniroute
minio --> gitlab
minio --> kubecost
logging --> monitoring
monitoring --> auth
gitlab --> argocd
monitoring -.-> argocd
argocd --> updater
classDef optional stroke-dasharray:5 3
Why each edge exists¶
| Edge | Reason |
|---|---|
preflight → kubernetes |
Prevents the cluster dependency chain from starting unless independently pinned components pass their compatibility checks. |
kubernetes → * |
Installs the Prometheus CRDs and CoreDNS config that later modules' manifests reference. A CRD must exist before a CR that uses it. |
kubernetes → istio_gateway, cert_manager_letsencrypt → istio_gateway |
The gateway module installs the Gateway API CRDs and its own Gateway, and creates the letsencrypt-gateway ClusterIssuer, which needs cert-manager and its Cloudflare token Secret already present. |
istio_gateway → * |
Every module that exposes a host declares a ListenerSet, an HTTPRoute and, where the host is gated, an AuthorizationPolicy. The Gateway API CRDs must exist before any of those apply. |
cert_manager_letsencrypt → * |
Anything terminating TLS needs the ClusterIssuer to exist before it requests a Certificate. |
longhorn_storage → minio_object_storage |
MinIO's PVCs bind against Longhorn's StorageClass. Without it they stay Pending. |
minio_object_storage → gitlab_platform |
GitLab stores artifacts, LFS, uploads and backups in MinIO buckets. |
minio_object_storage → kubecost |
Kubecost federates its cost data into a MinIO bucket. |
logging → monitoring |
Grafana provisions an Elasticsearch datasource pointing at the ECK stack. |
monitoring → auth |
OAuth2 Proxy renders a ServiceMonitor into the monitoring namespace, so that namespace must exist first. |
gitlab_platform → argocd |
ArgoCD's initial Application set points at repositories hosted on the in-cluster GitLab. |
monitoring → argocd (implicit) |
ArgoCD reads module.monitoring.monitoring_namespace for its ServiceMonitor. Data flow, not depends_on. |
argocd → argocd_image_updater |
The updater writes back to Applications ArgoCD owns. |
The auth edge is the awkward one
auth depends on monitoring, and monitoring depends on logging, so enabling OAuth2 pulls the whole observability stack into the critical path of the apply. That is why the first terraform apply on a fresh cluster takes so long: cert issuance and Longhorn volume creation both sit upstream of it.
Gated modules and the DAG¶
count = 0 removes a module from the plan but not its depends_on edges. The dependency simply resolves against an empty resource set. The practical consequences:
- On ARM64,
gitlab_platformhascount = 0, soargocdloses its GitLab ordering edge. It still waits oncert_manager_letsencryptand onmonitoring, which in turn waits onlogging. - Disabling
logging_module_enabledrops the Elasticsearch datasource from Grafana and the Kibana hostname from the gateway, but leavesmonitoringotherwise intact.