Back
Back

Zero-Code Instrumentation in Kubernetes Without the Instrumentation CRD

Zero-Code Instrumentation in Kubernetes Without the Instrumentation CRD

The OpenTelemetry Operator changed how teams approach telemetry collection in Kubernetes. The core appeal of zero-code instrumentation is that you can bring up telemetry inside application containers to collect traces, metrics, and logs without touching your source code or rebuilding your container images.

However, if you follow the default OpenTelemetry Operator documentation, you quickly run into a heavy operational prerequisite: the Instrumentation CRD. To get your telemetry flowing, the standard path forces you to write, apply, and manage a custom YAML manifest that defines the configuration for the OpenTelemetry SDK.

When you scale this across dozens of namespaces and multiple production clusters, managing these extra custom resources becomes tedious. They split your workload configuration across separate objects and add maintenance friction during cluster upgrades.

Fortunately, you can bypass the Instrumentation custom resource entirely. By using native Kubernetes annotations, you can implement zero-code instrumentation while keeping your cluster architecture clean and routing data directly to your OpenTelemetry Collectors.

Custom resource sprawl causes headaches

In a standard setup, the OpenTelemetry Operator relies on a mutating admission webhook. When you annotate a deployment, the Operator looks up a specific Instrumentation custom resource in the cluster to see which environment variables to inject and which init container images to use.

This model works, but it moves your operational complexity from the application code to your infrastructure layer.

  • Configuration drift: Your standard application settings live in your deployment manifest, but your telemetry configuration is tied to an external resource. This makes tracking changes in GitOps workflows more difficult.
  • Pipeline RBAC complexity: Your deployment pipelines or developers must be granted explicit RBAC permissions to create and patch resources under the apiGroups: [“instrumentation.opentelemetry.io”]. Without the CRD, your pipelines only need permissions for standard, native workloads like deployments and pods.
  • Upgrades and API changes: As the OpenTelemetry Operator project moves forward, the schema of the Instrumentation CRD changes. Upgrading the Operator requires tracking version compatibility and updating your manifests to prevent deployment failures.

If you just need to bootstrap the SDK and send your data, you do not need the extra weight of a dedicated CRD. The OpenTelemetry auto-instrumentation feature from Coralogix helps you deal with these issues.

How to trigger SDK injection using annotations

Before doing anything else, you need to install Kubernetes observability using the OpenTelemetry Integration Helm chart. That will create a set of OpenTelemetry Collector instances that extract and prepare the telemetry data from your cluster and workloads, then send it to Coralogix.

As part of the installation of the integration, you can enable the OpenTelemetry auto-instrumentation feature:

opentelemetry-autoinstrumentation:

  enabled: true

This will start an instance of the OpenTelemetry Operator without installing the CRDs, and create a default configuration that works out of the box with the OpenTelemetry Collector Coralogix Helm chart integration.

With the global settings applied via Helm, the Operator handles pod modifications based entirely on metadata. It intercepts pod creation requests and modifies the pod template on the fly before the pod is scheduled to a node.

The mechanism the Operator uses depends heavily on the language runtime you are targeting. It does not treat a Java application the same way it treats a Node.js script. When you apply an annotation, the Operator adds an init container containing the appropriate OpenTelemetry binaries or source libraries to your pod. This container runs first, copies the files into a shared volume accessible by your main application container, and exits.

To trigger this, developers just need to target their specific application runtime within their pod template metadata. For a Java microservice, you can instruct the Operator to inject the required packages by applying a language-specific annotation directly to your deployment template:

apiVersion: apps/v1

kind: Deployment

metadata:

  name: checkout

spec:

  replicas: 1

  selector:

    matchLabels:

      app: checkout

  template:

    metadata:

      labels:

        app: checkout

      annotations:

instrumentation.opentelemetry.io/inject-java: "true"

    spec:

      containers:

        - name: checkout

          image: "<your_app_image>"

The Operator supports targeted injection for multiple runtimes out of the box, including inject-java, inject-python, inject-dotnet, and inject-nodejs.

If you use instrumentation.opentelemetry.io/inject-sdk: “true”, the Operator changes its behavior entirely. It skips the init container phase and does not attempt to attach any language-specific code libraries, agents, or profilers to the container process. Instead, it injects only the base OpenTelemetry SDK environment variables.

This option is designed for services that already include the OpenTelemetry SDK packages natively inside their application code. For example, if your developers have manually added and configured the OpenTelemetry SDK via npm or pip inside the application source, they do not need the Operator to copy external library files. They just need the container to automatically inherit the correct OTLP endpoints, security headers, and resource attributes from the cluster infrastructure. Using inject-sdk: “true” fulfills this by laying down the environment variable foundation without risking conflicts with your application’s internal dependencies.

Troubleshooting your zero-code instrumentation setup

After you deploy your applications, you should start seeing data populate your Coralogix dashboards within a few minutes. If your screens are still blank, don’t panic. Debugging a zero-code pipeline usually comes down to checking a few specific handshakes between the webhook and your runtime.

No data showing up at all?

  • Check the pod annotations: Run kubectl get pod <pod-name> -o yaml and verify that the instrumentation.opentelemetry.io/inject-* annotation is present on the pod template metadata, not just the top-level Deployment metadata. If the annotation isn’t on the pod itself, the mutating webhook will ignore it.
  • Network and firewall restrictions: If your cluster enforces strict egress network policies or runs behind a proxy, ensure the nodes or the OpenTelemetry Collector can establish an outbound HTTPS connection to your Coralogix ingestion endpoint on port 443.
  • Secret or token mismatches: Double-check that your Coralogix Send-Your-Data API key is valid and that your values.yaml points to the correct regional endpoint (like ingress.eu2.coralogix.com for the EU2 region).

The pod deployed, but nothing was injected

  • Unsupported languages or runtimes: The Operator supports major enterprise runtimes out of the box—specifically Java, Python, Node.js, and .NET. If you are running an unsupported runtime without manual SDK configuration, the Operator won’t know how to attach to the process.
  • Operator language flags: Depending on the version of the Operator running in your cluster, certain language features might be disabled by default to save resource overhead. Review the Operator’s configuration to ensure the specific language manager you need is explicitly active.

Application crashes or weird span data

  • The double-instrumentation trap: If your developers have already manually imported OpenTelemetry libraries into their code, using a language-specific injection like inject-nodejs: “true” can cause runtime conflicts or duplicate spans. For those workloads, switch to inject-sdk: “true” to safely pass the cluster’s OTLP environment variables without forcing a second code injection.
  • Init container failures: If a pod gets stuck in Init:CrashLoopBackOff, check the init container logs. This usually points to an architecture mismatch, such as trying to inject a glibc-dependent agent into an ultra-lean distroless or Alpine container image.
  • The dual-webhook clash: If your cluster already has a standard, standalone OpenTelemetry Operator installed, enabling opentelemetry-autoinstrumentation.enabled: true inside the Coralogix chart will cause conflicts. Two separate mutating admission webhooks will fight over the same pod creation events. This can lead to duplicate instrumentation init containers, conflicting environment variables, or API validation failures that block your pods from scheduling entirely. Remove any existing standalone OpenTelemetry Operator deployments before turning on the integrated Coralogix auto-instrumentation engine.

Keeping your deployments clean

Your observability setup should not create more management debt than the applications you are trying to monitor. Relying heavily on custom resources or sprawling environment variables to drive simple agent injections adds unneeded complexity to your Kubernetes clusters.

By activating global auto-instrumentation in your Helm values, the platform team configures the collection pipeline once at the cluster layer. Developers don’t have to learn a new custom resource syntax, manage separate lifecycles for their telemetry configuration, or run a standalone Operator deployment. They keep using standard, readable Kubernetes manifests that work with existing GitOps workflows, ensuring visibility remains a frictionless default across the entire engineering organization.

On this page