Most Kubernetes clusters hide their own call graph: a ClusterIP Service masks which Pod answered a request, a rescheduled Pod may come back with a new IP, and static architecture diagrams quickly become outdated. Because Pod identities change constantly and workloads communicate through stable virtual addresses, static records struggle to keep up with who depends on whom. Kubernetes microservice dependency mapping complements those records with a live graph built from observed traffic, where workloads and Services are the nodes and calls are the edges. When available, request rate, error, and latency data help SREs narrow a latency spike to the dependencies involved.
This guide explains how to map Kubernetes microservice dependencies using service-mesh telemetry, distributed tracing, and eBPF-based approaches. You’ll learn how to discover service connections, identify unhealthy dependencies, trace failures across workloads, and account for dependencies that network traffic alone cannot reveal.
What Kubernetes Microservice Dependency Mapping Is
A Kubernetes microservice dependency map is a directed graph built from observed communication. Each node represents a workload or Service, and each edge points from caller to callee. Edges reflect traffic observed during a selected time window and, where request telemetry is available, can show request rate, errors, and latency. Kubernetes metadata helps resolve source and destination addresses to Pods, workloads, namespaces, and Services, so the map remains useful when Pod IPs change.
Observed traffic, distributed traces, and configuration data reveal different relationships. Traffic confirms communication during the selected time window, while traces use propagated context to link related calls and downstream operations. Kubernetes manifests, Helm values, and application configuration reveal declared dependencies that may not generate recent traffic, but those references do not prove runtime communication. Combining these sources provides a more complete picture than relying on network flows or static architecture diagrams alone.
Why Dependency Mapping Matters for Platform Engineers and SREs
As applications change and Pods are replaced, static diagrams can fall behind the dependencies running in a Kubernetes cluster. Stale or incomplete dependency data can send investigations in the wrong direction. An up-to-date dependency map helps platform engineers and SREs investigate questions such as:
- Which downstream service is contributing to errors or latency?
- Which upstream services depend on an affected workload?
- Which service connections exist that the team hasn’t documented?
- Which connections appeared or disappeared after a deployment?
For more on visualizing these relationships, see our guide to microservices visualization.
Black-box vs white-box Mapping approaches
Black-box mapping discovers dependencies by observing application and network activity without modifying application code. Depending on the collection method, it can capture network connections, HTTP requests, traces, errors, and latency. White-box mapping uses application instrumentation to record requests, internal operations, and custom attributes.
The approaches differ primarily in how they collect telemetry and which operations they observe:
| Dimension | Black-box collection | White-box instrumentation |
|---|---|---|
| Collection method | Proxies, eBPF probes, and network observation | Application SDKs and instrumentation agents |
| Code changes | None for supported automatic collection | Automatic instrumentation can avoid code changes; custom instrumentation requires them |
| Dependency discovery | Observed communication between supported workloads | Calls and operations captured by instrumentation |
| Request-level visibility | Supported protocol details, traces, errors, and latency | Application spans, request context, and custom attributes |
| Causal relationships | Depends on the available context and correlation capabilities | Can link related operations through propagated trace context |
| Coverage | Depends on collector placement, traffic visibility, and protocol support | Depends on instrumentation coverage and supported operations |
Static analysis of Kubernetes manifests and Git repositories provides another way to identify dependencies. Service definitions, selectors, environment variables, connection strings, Helm values, and ConfigMaps can reveal relationships missing from the current traffic window. These include failover targets and scheduled jobs that haven’t run yet.
However, configuration shows intended relationships, not actual communication. A declared dependency with no observed traffic is worth investigating, but it isn’t a confirmed connection. Network observation identifies active communication, distributed traces establish causality between related operations, and configuration analysis reveals dependencies that traffic monitoring may miss.
Choosing by Constraint
Choose an approach based on the telemetry you need and how your applications are deployed. eBPF-based collection can discover supported service communication and capture request-level telemetry without application changes. Application instrumentation adds custom spans and business-specific context where needed. Both approaches can be used together to provide a more complete view of service dependencies.
How to Map Microservice Dependencies in Kubernetes
To map microservice dependencies in Kubernetes, choose a telemetry source: service-mesh metrics, OpenTelemetry traces, or an eBPF collector. Install and configure the required components, then let them observe traffic between workloads. Open a dependency-map viewer, such as groundcover’s Map page for eBPF telemetry, to identify callers, downstream services, and failed requests. Use Kubernetes metadata to verify each connection and inspect the underlying traces for latency, errors, or missing dependencies.
1. Deploy the eBPF Collector
Start with a Kubernetes cluster that meets groundcover’s requirements, including Kubernetes 1.21 or later and a compatible Linux 5.3+ kernel with BTF/CO-RE support.
After creating a groundcover workspace, provisioning its BYOC backend, and configuring CLI authentication, prepare a values.yaml file using the ingestion endpoint from your workspace:
Install the groundcover CLI in a POSIX shell if it isn’t already installed, then deploy the sensor:
Replace YOUR_BYOC_ENDPOINT with the actual endpoint before deploying. The CLI handles the sensor installation and ingestion-key setup after the required workspace authentication.
Confirm that the collector workloads are running:
Check the scheduled sensor instances against your cluster’s worker nodes. Unsupported or excluded nodes can create gaps in dependency coverage.
2. Deploy Two Communicating Workloads
Create a namespace containing a sample catalog backend and a storefront client:
The catalog Deployment runs an HTTP server, and the catalog Service provides a stable destination despite changes to the backend Pod IP.
Next, create a client Deployment that continuously sends requests to the Service:
The storefront Pod calls catalog every three seconds. This generates traffic that the collector can observe without requiring an application SDK.
3. Confirm That the Workloads Communicate
Inspect the running workloads:
Send an additional request from the client:
A successful request returns the default NGINX HTML response. This confirms that storefront can reach catalog through its Kubernetes Service.
4. Open and Interpret the Dependency Map
In groundcover, open Map and select the relevant cluster and observation period. This view uses eBPF-generated trace spans to display detected request dependencies.
Look for a relationship between the storefront and catalog workloads. When supported spans contain both client and server identities, the map can represent the connection as:
The nodes identify the communicating workloads, while the connecting edge identifies the observed request dependency. An edge is green when all included span statuses are OK and red when at least one indicates an error.
For a view that combines eBPF telemetry with ingested OpenTelemetry spans, use Traces → Map. This distinction matters when an application already emits custom or instrumented spans.
5. Resolve the Dependency to Kubernetes Resources
Compare the observed relationship with Kubernetes metadata:
The EndpointSlice identifies the backend addresses associated with the catalog Service. Compare those addresses with the catalog Pod IPs.
The dependency can now be described at three levels:
- Pod level: The current
storefrontPod communicates with acatalogbackend Pod. - Workload and Service level: The
storefrontDeployment calls thecatalogService, backed by thecatalogDeployment. - Namespace level: Both workloads run in
dependency-demo.
Because Pods can be rescheduled and IPs reused, dependency maps need time-appropriate workload metadata rather than relying on addresses alone.
6. Check Dependency Health and Investigate Missing Edges
Inspect the detected connection and its underlying traces for request failures, errors, and latency. Use available metrics to compare request rates and latency across dependencies.
If the storefront → catalog relationship doesn’t appear, check whether:
- Both workloads are running and requests are reaching the backend.
- The sensor covers their nodes and supports the observed protocol.
- The map’s cluster, filters, and time window include the generated traffic.
- Relevant spans contain both client and server identities.
A successful network connection doesn’t guarantee that an application request succeeded, and not every observed network flow produces a map edge. Where network observation cannot establish a causal relationship, OpenTelemetry spans can supply additional request context.
After completing the exercise, delete the sample workloads:
This removes the demo resources while leaving groundcover installed for dependency mapping across the rest of the cluster.
Service Mesh Dependency Mapping With Istio and Envoy
An Istio service mesh generates telemetry that tools such as Kiali use to map service dependencies. In sidecar mode, Envoy proxies collect metrics for traffic passing through them, including request counts, errors, and latency, without requiring application code changes.
The core edge metric is istio_requests_total, a counter incremented for every request handled by an Istio proxy across HTTP, HTTP/2, and gRPC. Its labels define the edge: source_workload, destination_service, response_code, and reporter, which is source for a client proxy and destination for a server proxy. TCP traffic gets istio_tcp_sent_bytes_total and related byte and connection counters.
Envoy fills the peer labels through a metadata exchange between proxies. When one side of a call has no proxy, telemetry is missing peer attributes such as workload name, namespace, and labels. That gap is why meshes produce unknown nodes: the edge exists, but one endpoint has no identity.
How Istio Metrics Become Dependency Edges
Kiali builds dependency graphs from Istio metrics collected by Prometheus. HTTP and gRPC request edges come primarily from istio_requests_total, while TCP edges use istio_tcp_sent_bytes_total. Source and destination labels identify the communicating workloads, and Kiali combines this telemetry with Kubernetes resources and Istio configuration to display relationships between Services and workloads.
A rate needs at least two samples, so a query window that is short relative to the Prometheus scrape interval can hide edges that carry traffic.
If an expected edge is missing, confirm that requests occurred during the selected time window, the relevant workloads have mesh telemetry enabled, and Prometheus is scraping the proxy metrics. Check the graph’s namespace and traffic filters as well. Connections to PassthroughCluster or BlackHoleCluster indicate traffic handled by Istio’s passthrough or blackhole routing rather than a defined Service or ServiceEntry. To verify the underlying telemetry, open Prometheus:
Then query the edge you expect directly:
Traces work differently from metrics. When distributed tracing is configured in the mesh, each Envoy proxy generates spans for the requests it handles and exports them to the tracing backend, subject to the sampling rate operators set. Those spans form an end-to-end trace only if applications propagate trace context across service boundaries, because the proxy cannot link an inbound request to the outbound calls the application makes in response.
To make that link, each service forwards the W3C trace-context headers (or the B3 headers when using Zipkin) plus Envoy’s request ID. The dependency graph aggregates edges across all traffic, while a trace shows the path one request took across them.
Ambient Mode Changes What the Map Captures
Istio’s ambient mode removes the per-pod sidecar. The node-level ztunnel handles L3 and L4 only, so a service that uses only the ztunnel overlay reports only the four TCP metrics, which gives connection-level edges and byte counts. HTTP request metrics, response codes, and request latency, reported with reporter="waypoint", appear only when traffic passes through a waypoint proxy.
First, the graph itself: ztunnel reports all of its traffic as TCP, so ztunnel-only edges appear as TCP edges. When a waypoint is in the path, ztunnel and the waypoint both report the same communication, so a graph built from every reporter can show double edges between the same nodes. Filter on the reporter label to isolate one source, since ztunnel metrics use reporter="source" and waypoint metrics use reporter="waypoint".
Next, the scrape targets: Prometheus metric merging is not available in ambient mode, so Prometheus must scrape ztunnel and waypoint pods as separate targets. Missing scrape targets show up as missing or TCP-only edges.
groundcover works alongside Istio-managed traffic, using its eBPF sensor to capture service communication without requiring application instrumentation. It can also ingest Istio-generated distributed traces and collect service-mesh metrics. The standalone Map view visualizes dependencies from eBPF spans, while Traces → Map brings together eBPF and ingested OpenTelemetry spans for investigating service communication across telemetry sources.
Distributed Tracing for Call-Chain Visibility
A dependency map shows which services communicate, while a distributed trace shows what happened during an individual request. For example, a map may show that checkout calls payments and inventory. A trace can reveal that payments waited on a database call while inventory completed, helping engineers identify where latency or errors originated.
Distributed tracing connects related operations through spans and trace context propagated between services. OpenTelemetry SDKs and automatic instrumentation can record application operations, including HTTP requests and database queries. An OpenTelemetry Collector receives and forwards the generated spans to a tracing backend for storage and analysis.
groundcover captures supported request traces automatically through its eBPF sensor and ingests OpenTelemetry spans from instrumented applications. It correlates this telemetry with Kubernetes workload information, helping engineers investigate request paths alongside service dependencies. For instrumentation and sampling details, see our guide to Jaeger and OpenTelemetry distributed tracing.
Mapping Asynchronous and Shared Dependencies
Dependency maps can reveal communication through message brokers and shared databases. Consider a checkout service that publishes an order to Kafka or RabbitMQ, a worker that consumes the message, and the worker’s subsequent database write. Network observation identifies connections between checkout and the broker, the worker and the broker, and the worker and database. These connections reveal the participating systems but differ from the logical path of an individual message.
Distributed tracing adds that request-level relationship. When trace context is propagated in message headers, producer and consumer spans can use parent-child relationships or span links to associate message publication with downstream processing. This helps engineers identify which checkout operation triggered a particular worker task.
Other dependencies require additional context:
- Scheduled operations: A CronJob that hasn’t executed during the observation window may have dependencies that aren’t represented by recent traffic.
- Shared resources: Two services connecting to the same database appear as separate dependencies on that database. The map reveals the shared backend, while traces and application context help identify operations affected by schema changes or resource contention.
Kubernetes manifests, Helm values, and application configuration can reveal declared dependencies such as scheduled job targets and shared database connections, even when they have no recent traffic.
eBPF-Based Dependency Mapping Without Sidecars
Node-level eBPF collectors observe supported network and application traffic from the Linux kernel without requiring a proxy in every pod. They run as Kubernetes DaemonSets and can identify communication between workloads that have not been instrumented with application SDKs.
The collector combines observed source and destination addresses with Kubernetes metadata to identify the workloads and Services involved. Protocol support, TLS inspection, collector permissions, and node coverage determine which dependencies appear. Network observation can establish that two workloads communicate, while OpenTelemetry spans provide additional request causality and application-specific attributes.
OpenTelemetry eBPF Instrumentation
OpenTelemetry eBPF Instrumentation (OBI) can run as a Kubernetes DaemonSet to observe supported application requests without modifying application code. It generates trace spans, RED metrics, and service-graph metrics that identify communication between clients and servers. These relationships can be mapped using the available workload identities and request telemetry.
groundcover’s eBPF Sensor
groundcover deploys its eBPF sensor as a Kubernetes DaemonSet, with one sensor pod on each monitored node. The sensor uses kernel probes to capture supported application and network activity, including request traces, without SDKs, language-specific agents, application restarts, or code changes.
The sensor provides visibility into HTTP requests, database queries, and messaging operations. groundcover combines automatically collected telemetry with logs, metrics, and Kubernetes events to support service dependency investigations. Its dependency maps use eBPF trace spans to identify communication between workloads.
Requirements are Kubernetes 1.21 or later and Linux kernel 5.3 or later with BTF and CO-RE support. Supported platforms include EKS, AKS, GKE, OKE, OpenShift, Rancher, and self-managed Kubernetes clusters; AWS Fargate and Docker Desktop are not supported.
groundcover combines eBPF request telemetry with Kubernetes metadata to identify the workloads, Services, and namespaces participating in observed communication. Its supported technologies include HTTP, gRPC, Kafka, AMQP, PostgreSQL, Redis, and other database protocols. This allows engineers to discover application, messaging, and database dependencies without instrumenting each service.
The standalone Map view builds dependency graphs from eBPF spans, while Traces → Map also includes ingested OpenTelemetry spans. You can therefore investigate automatically discovered dependencies alongside custom application traces. For an open-source example of eBPF-based Kubernetes service mapping developed by groundcover, see Caretta.
Mapping Dependencies Across Clusters and External Services
Cross-cluster dependency mapping requires telemetry from each cluster and consistent cluster identifiers. Collect workload communication in each cluster and correlate it with OpenTelemetry spans in a shared backend. For a request to appear as one distributed trace across cluster boundaries, services must propagate trace context throughout the request path.
Identify workloads using their cluster, namespace, and workload names. Consistent identifiers distinguish services with the same name in different clusters, preventing unrelated dependencies from being combined during graph aggregation.
Identify external dependencies such as managed databases, external Kafka services, third-party APIs, and SaaS platforms from these sources:
- DNS query telemetry, where available
- Egress traffic destinations
- Deployment configuration, such as connection strings and hostnames
- Cloud inventories
Represent external dependencies using stable hostnames or known service endpoints, enriched with DNS records, configuration, and cloud inventory data where available. When the external system also exports traces, correlate those spans to follow requests beyond the Kubernetes cluster.
Using Metrics to Identify Unhealthy Service Dependencies
A dependency map becomes more useful when its connections include request rate, errors, and latency. Google’s SRE framework identifies four golden signals: latency, traffic, errors, and saturation. Request rate, errors, and duration (RED) describe communication between services, while utilization, saturation, and errors (USE) help identify resource pressure on the workloads involved.
RED and USE answer different questions. An HTTP 500 response indicates a failed request, while a resource error may indicate a disk or network problem. Latency should also be tracked separately for successful and failed requests, since fast failures can distort overall latency measurements.
| Golden signal | RED equivalent | USE equivalent | Where it lives on the map |
|---|---|---|---|
| Traffic | Rate | (none) | Edge weight |
| Errors | Errors (failed requests) | (none) | Edge color |
| Latency | Duration | (none) | Edge latency percentile |
| Saturation | (none) | Utilization, saturation | Node resource state |
Expressing the Signals in PromQL
Both queries below group by source_workload and destination_service, so every result series is one caller-to-callee edge: the first gives the request rate and the second gives the 5xx error ratio.
These queries locate an unhealthy edge by comparison. If one caller’s error ratio to a destination rises while other callers of the same destination stay healthy, the problem is on that edge or in that caller. If every caller degrades, look at the destination.
Per-edge latency needs both endpoints as labels. Istio’s observability best practices give a per-edge p95 pattern:
Apply rate() only to counters, and when combining irate() with sum(), take the rate first so counter resets after a restart are detected.
Storage Constraints for Per-Edge Metrics
Each distinct source-destination label combination adds metric series. Latency histograms increase cardinality further because every observed label combination produces multiple bucket series. Pod-level labels can multiply the number of series even when a dependency graph only needs workload-level relationships.
groundcover stores metrics in VictoriaMetrics inside the customer’s environment as part of its BYOC (Bring Your Own Cloud, where your data plane runs in your own cloud account) architecture, and it supports PromQL-compatible queries. Whether a given query works depends on which metric names and labels are available.
For dependency views, aggregate metrics by workload or Service rather than individual Pods. Istio’s recommended recording rule removes instance and Pod labels from the resulting aggregated series:
This produces workload-level series suitable for dependency analysis. The original Pod-level metrics remain available unless collection or retention settings are changed separately.
Using Dependency Maps for Root-Cause Analysis and Latency Troubleshooting
A dependency map earns its place in the first ten minutes of an incident, when the question is where to look. The workflow below moves from symptom to cause, and each step narrows the search space:
- Start at the symptom edge: Find the edge whose error rate or p95 crossed its threshold, usually at the ingress or the first user-facing service.
- Walk downstream: Follow outbound edges from that service and compare each edge’s latency and errors with its baseline. The hop where latency jumps or errors originate is the candidate.
- Bound the blast radius: Walk upstream from the candidate to list every caller that inherits the failure. Those callers explain the secondary alerts.
- Drop to one trace: Open a trace that crosses the candidate edge and find the span that consumed the time, such as a database call, a retry loop, or a timeout.
- Correlate with change: Check deploys, config changes, and Kubernetes events on the candidate workload in the same window.
After locating the suspicious dependency, compare its traces and metrics with workload changes from the same time window. This helps engineers distinguish where an error propagated from the changes that may have triggered it. Check the following context:
- Recent rollouts of the workloads at either end of the edge.
- Configuration changes, such as ConfigMaps, Secrets, feature flags, or mesh and policy changes.
- Kubernetes warning events on the affected pods.
- Shifts in health on the edge’s upstream and downstream neighbors.
groundcover’s Agent Mode supports root-cause investigations through natural-language questions. It correlates metrics, traces, logs, Kubernetes events, and workload metadata to help engineers identify affected services, investigate failing dependencies, and examine possible causes. With the appropriate integrations configured, it can also bring in incident discussions and change context from external tools.
Agent Mode supports multiple model configurations, including Amazon Bedrock. For engineers investigating incidents from an IDE or another AI assistant, the groundcover MCP Server exposes logs, metrics, traces, Kubernetes events, and infrastructure metadata through query tools. These capabilities allow engineers to investigate dependencies using the same underlying observability data.
Performance Overhead and Trade-Offs: Sidecars, Agents, and eBPF
The deployment footprint of an observability component depends partly on where it runs. Sidecar proxies scale with the number of injected Pods, while node-level collectors typically scale with the number of monitored nodes.
In a three-node cluster with 100 sidecar-injected Pods, Istio deploys 100 sidecars, while a node-level eBPF collector typically deploys three DaemonSet Pods. This compares deployment footprint, not equivalent resource consumption or functionality. Sidecars proxy traffic, while eBPF collectors observe supported application and network activity, and both can run together. Actual CPU and memory overhead depend on workload, traffic, and configuration.
Mesh Proxies
At 1,000 HTTP requests per second with 1 KB payloads, Istio 1.24 proxies consumed the following CPU and memory in the Istio benchmark; these figures come from one benchmark workload and will vary with your own traffic, telemetry configuration, and cluster conditions, so validate them against your own workloads:
| Component | vCPU | Memory |
|---|---|---|
| Sidecar proxy (2 worker threads) | 0.20 | 60 MB |
| Waypoint proxy (2 worker threads) | 0.25 | 60 MB |
| ztunnel | 0.06 | 12 MB |
The Istio performance and scalability benchmarks also identify configuration state as a memory cost: proxy memory grows with the configuration the proxy holds, so large meshes with many listeners, clusters, and routes use more.
Node-Level eBPF Collectors
Node-level collectors consume CPU and memory on every monitored node, and their overhead depends on traffic volume, collection settings, and workload characteristics.
In a benchmark using a Go HTTP application handling 3,000 requests per second, groundcover’s eBPF sensor added 9% application CPU overhead compared with the baseline, with no measured increase in application memory. These results demonstrate the sensor’s low application overhead under the tested conditions. When evaluating your own cluster, measure both application resource consumption and the sensor’s CPU and memory usage.
Operational Burden
Deployment and maintenance requirements also shape operational overhead, beyond CPU and memory:
- Sidecar meshes: Managing proxy deployment and injection, proxy upgrades, and mesh telemetry, including a Prometheus sized for per-edge histograms.
- Ambient meshes: Fewer per-pod proxies, but L7 visibility needs waypoints, and ztunnel and waypoint pods need separate metric scraping.
- SDK-based APM: Application integration and maintenance for each language, plus coordination with application teams. OpenTelemetry instrumentation does not necessarily require re-instrumentation when changing backends.
- eBPF collectors: Compatible kernels and appropriate permissions for the DaemonSet. TLS-library compatibility affects encrypted L7 inspection, not network-level dependency discovery as a whole.
How Kubernetes Resources Affect Dependency Map Accuracy
Kubernetes rewrites, restricts, and schedules traffic in ways that change what any observer records. Each resource below affects either which edges exist or which address appears on an edge.
Services and DNAT
A connection sent to a Service is redirected to one of the Service’s endpoints. The same connection can therefore be observed with the Service’s virtual IP or with the selected backend pod IP, depending on where traffic is captured.
That difference explains why one tool shows frontend → cart-service while another shows frontend → cart-7d9f-x2kq. By combining observed endpoints with Kubernetes metadata, a mapping tool can associate the backend Pod with its workload and Service. Whether the graph shows a Service-level or Pod-level edge depends on how the tool groups resources.
Headless Services have no ClusterIP, and kube-proxy does not load-balance their traffic. Kubernetes DNS returns endpoint addresses directly, so an observer may see Pod IPs rather than a Service virtual IP. A mapping tool can still associate those endpoints with the headless Service or their owning workload using Kubernetes metadata.
EndpointSlices
EndpointSlices associate network endpoints with a Service through the kubernetes.io/service-name label. A Service can have multiple EndpointSlices, and an endpoint may appear in more than one slice during updates, so mapping tools must combine and deduplicate those records. Endpoint addresses aren’t always Pod IPs: Services without selectors can reference backends managed outside Kubernetes.
Endpoint membership and readiness change during rollouts, and terminating Pods can remain listed in EndpointSlices. To attribute historical traffic correctly, a mapping tool needs the endpoint and workload identities that were valid when the request occurred. Resolving an old IP against only current Kubernetes metadata risks assigning a connection to the wrong backend.
NetworkPolicy
NetworkPolicy declares which connections are permitted for the Pods it selects, which is different from the dependencies observed in traffic. Policies are additive and apply only to the Pods they select, and ingress and egress isolation are evaluated separately. Traffic is allowed by default when no relevant isolation policy applies, so a set of policies does not list every allowed connection.
Enforcement and address handling both depend on the cluster’s network plugin. Policy needs a plugin that enforces it, and creating a NetworkPolicy without a controller that implements it has no effect. Whether NAT happens before or after policy processing is undefined and varies by plugin, so the source IP a policy evaluates can be a node or load balancer IP.
Comparing declared policy with observed traffic gives two sets to investigate:
- First, permitted connections with no observed traffic. These can be unused, or simply inactive during the selected window.
- Second, observed connections that appear to violate policy. Check the policy selectors, the ingress and egress rules, NAT behavior, and plugin enforcement to find the cause.
Read together, the two views point to missing or unexpected dependencies that neither shows alone.
DaemonSets
Node-level collectors run as DaemonSets because a DaemonSet ensures that all (or some) nodes run a copy of a pod and adds pods as nodes join. DaemonSet placement determines which nodes can contribute telemetry, but actual dependency visibility also depends on collector health, permissions, supported protocols, and whether the traffic is visible to the collector. Node selectors, taints without matching tolerations, unsupported kernels, and excluded node pools can leave nodes without a working collector. Such gaps may prevent dependencies from appearing or leave them only partially observed.
Identifying Deployment Dependencies in GitOps Configuration
Runtime communication dependencies and deployment prerequisites are different things. A service that calls another service does not automatically need the two deployed in a particular order, especially when the application handles temporary unavailability with retries.
GitOps configuration reveals dependencies that network traffic does not show. Examples include CRDs that must exist before the custom resources that use them, and controllers that must run before workloads can become operational. The runtime dependency graph is additional evidence for reviewing deployment configuration, not a source from which deployment order can be inferred automatically.
Flux CD dependsOn
In Flux, .spec.dependsOn delays reconciliation of a Kustomization until the referenced Kustomizations are Ready. Setting .spec.wait: true or .spec.healthChecks on the dependency makes Ready reflect the health of the resources it deployed, not merely a successful apply. For example, a controller’s CRDs can be installed and the controller made ready before deploying custom resources that depend on them.
The following YAML is an illustrative fragment, not a complete manifest (sourceRef, path and interval are omitted):
Argo CD Sync Waves
Argo CD orders resources within a sync with the argocd.argoproj.io/sync-wave annotation. Waves are integers, default to 0, and can be negative to run before everything else. Argo CD applies waves in ascending order and waits for each wave to become healthy before starting the next:
Sync waves express deployment prerequisites within an Application: place CRDs or a controller in a lower wave such as -1, and the custom resources and workloads that depend on them in wave 0. An unhealthy resource in an earlier wave prevents later waves from proceeding. Sync-wave annotations order resources only within one Application sync, so they do not coordinate deployment order across separate Argo CD Applications.
Compare the observed dependency graph with Flux and Argo CD manifests to identify potential deployment prerequisites, but don’t infer deployment order from traffic alone. Before adding dependsOn or a sync wave, confirm that the dependency is necessary for installation or readiness rather than only for runtime communication.
FAQs
Yes. Deploy an eBPF-based collector such as groundcover’s sensor to automatically discover supported service communication and capture request-level telemetry without application code changes or SDKs. Open groundcover’s Map view to inspect detected dependencies and request errors over the selected time window.
Istio captures dependency data through Envoy proxy telemetry. In sidecar mode, proxies generate request counts, response codes, and latency metrics, including istio_requests_total. Prometheus collects these metrics, and Kiali uses them to construct dependency graphs. In ambient mode, ztunnel provides TCP-level metrics, while waypoint proxies provide supported L7 request telemetry.
Start with eBPF-based collection if you want automatic dependency discovery and request-level visibility without modifying application code. Choose OpenTelemetry application instrumentation when you need custom spans, business-specific attributes, or explicit context propagation across application operations. For broader coverage, use both: groundcover automatically discovers supported dependencies through eBPF and can ingest OpenTelemetry traces to add application context.
Yes. Collectors that support Kafka, AMQP, HTTP, and database protocols can identify communication with message brokers, databases, and external APIs. For example, groundcover’s eBPF sensor supports Kafka, PostgreSQL, Redis, and other application protocols. Distributed traces can also connect message publication to downstream processing when trace context is propagated. DNS records and application configuration help identify external dependencies beyond observed traffic.
A dependency may be missing because no requests occurred during the selected time window, the collector isn’t running on the relevant nodes, or filters exclude the workloads. Check workload communication, collector health, and the selected cluster and observation period. In groundcover, use Map for eBPF-generated dependencies or Traces → Map to include ingested OpenTelemetry spans.
Start with the dependency showing increased latency or errors, then follow its downstream connections to identify where the problem originates. Trace upstream connections to determine which callers are affected. Finally, inspect request traces, Kubernetes events, and recent deployments to investigate the cause. groundcover’s Agent Mode can correlate these signals during an incident investigation.
Sidecar proxies scale with the number of injected Pods, while node-level eBPF collectors typically scale with monitored nodes. A cluster with 100 meshed Pods across three nodes would have 100 sidecars compared with three node-level collectors, although this doesn’t imply equivalent resource consumption. In a benchmark at 3,000 requests per second, groundcover’s eBPF sensor added 9% application CPU overhead with no measured increase in application memory. Actual overhead depends on traffic and configuration.
