Last updated: August 11, 2025

Building a Custom OpenTelemetry Collector

The official OpenTelemetry Collector Contrib image is like a Swiss Army Knife, with hundreds of components for every imaginable observability scenario. But that 'everything and the kitchen sink' approach comes with significant trade-offs.

The default build is often bloated with components you'll never use, which increases memory footprint, slows startup times, and expands the security attack surface since every unused component is extra code that could potentially contain a vulnerability.

This is why building a custom Collector is a best practice. Instead of a Swiss Army knife, you'll create a lean, efficient tool designed for a specific job.

This approach isn't just for creating lightweight agents for deployment. It's also essential for:

  • Developers building their own custom processors or exporters, as it provides a clean environment for testing.

  • Operators who need to build specialized collectors with unique debugging tools to troubleshoot complex telemetry issues.

In this guide, I'll walk you through the entire process, step by step. Let's get started!

Prerequisites

To follow through with this article, you'll need a recent version of Go and Docker installed locally.

Understanding the OpenTelemetry Collector Builder (OCB)

Before you decide what goes into your custom Collector, you need to understand the mechanics of the tool that builds it.

The OpenTelemetry Collector Builder is a command-line utility that automates the creation of a custom Collector binary. It takes a manifest file (which we'll define soon) and handles everything from generating source code to compiling the final executable.

Specifically, it creates a main.go and other boilerplate files, resolves dependencies via a go.mod, and then invokes the Go compiler to produce a statically-linked and fully custom Collector binary.

In short, OCB turns what would otherwise be a complex, manual process into something declarative and repeatable.

To get started with the OCB, you need to install it first. For stable and reproducible builds, you should use one of the two officially recommended installation methods.

For CI/CD environments, the preferred method is to use the official Docker image, which ensures a clean and consistent build environment every time. First, pull the latest image:

bash
1
docker pull otel/opentelemetry-collector-builder:latest

To run the builder, you'll mount your manifest.yaml and an output directory into the container. The final binary will appear in the ./dist directory on your local machine:

bash
12345
docker run --rm \
-v "$(pwd)/manifest.yaml:/manifest.yaml" \
-v "$(pwd)/dist:/dist" \
otel/opentelemetry-collector-builder:latest \
--config=/manifest.yaml

This command mounts your manifest file and output directory into the container, then runs the build. The resulting binary will then appear in your local ./dist directory.

For local use, downloading a pre-compiled binary is the recommended option. Just head to the OpenTelemetry Collector Releases, grab the latest version of ocb for your platform, and place it somewhere in your system’s PATH.

Downloading the OCB binary from GitHub

Once installed, you can invoke it directly:

bash
1
ocb --help
text
1234567
OpenTelemetry Collector Builder (v0.131.0)
ocb generates a custom OpenTelemetry Collector binary using the
build configuration given by the "--config" argument. If no build
configuration is provided, ocb will generate a default Collector.
[...]

With the builder installed, it's time to define the manifest for your custom Collector.

Building a custom Collector binary

The foundation of your custom Collector is a manifest file which defines the exact components that should be included in the final binary:

yaml
12345678910111213141516
# manifest.yaml
dist:
module: github.com/dash0-examples/otelcol-prod
name: otelcol-prod
description: A minimal OpenTelemetry Collector.
output_path: ./dist
version: 1.0.0
receivers:
- gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.131.0
processors:
- gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.131.0
exporters:
- gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.131.0

This manifest has four top-level sections that define your custom collector. It starts with the dist which contains metadata about the distribution itself, such as the name of the final binary (otelcol-prod), its version, and where the build artifacts should be placed (./dist).

It's not about OpenTelemetry functionality but rather about the binary you are creating. The dist tags are all optional and are for customizing the defaults:

Understanding the dist section of the OpenTelemetry Collector Builder

After the dist section, the manifest defines the core components that make up your custom Collector. Each list specifies the exact components to include, and where to fetch them.

The gomod field identifies the Go module path and version for each component, allowing the ocb tool to fetch the exact source code needed to guarantee reproducibility.

With this basic setup, you can receive data through the OTLP receiver, process it efficiently using the batch processor, and export it to the console with the debug exporter.

To see it in action, run the ocb tool and point it to your manifest file as follows:

bash
1
ocb --config manifest.yaml

The build will commence and you'll see the following output if everything goes well:

text
1234567
2025-08-06T13:24:34.595+0100 INFO internal/command.go:98 OpenTelemetry Collector Builder {"version": "v0.131.0"}
2025-08-06T13:24:34.596+0100 INFO internal/command.go:103 Using config file {"path": "manifest.yaml"}
2025-08-06T13:24:34.597+0100 INFO builder/config.go:160 Using go {"go-executable": "/Users/ayo/.local/share/mise/installs/go/1.24.3/bin/go"}
2025-08-06T13:24:34.599+0100 INFO builder/main.go:99 Sources created {"path": "./dist"}
2025-08-06T13:24:59.822+0100 INFO builder/main.go:200 Getting go modules
2025-08-06T13:25:28.673+0100 INFO builder/main.go:110 Compiling
2025-08-06T13:25:36.011+0100 INFO builder/main.go:139 Compiled {"binary": "./dist/otelcol-prod"}

As specified in the dist section of your manifest, you now have a folder named dist containing both the source code and the compiled binary (otelcol-prod) for your custom Collector.

The directory structure should look like this:

text
12345678
dist
├── components.go
├── go.mod
├── go.sum
├── main_others.go
├── main_windows.go
├── main.go
└── otelcol-prod

You can now run the otelcol-prod binary and provide a Collector configuration for the included components:

yaml
1234567891011121314151617181920
# otelcol.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
batch:
exporters:
debug:
verbosity: detailed
service:
pipelines:
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
bash
1
./otelcol-prod --config otelcol.yaml

Then, once you start sending data, you should observe it flowing through the pipeline. Thanks to the debug exporter, you'll see the trace data printed directly to the console:

Basic OpenTelemetry Pipeline with custom Collector

Finding and referencing Collector components

To build a useful collector, you need to add components like receivers, processors, and exporters. But how do you find them, and how do you figure out the exact gomod path to use in your manifest?

This process can seem tricky if you're not a Go developer, but it follows a simple and consistent pattern. You'll find most of what you need in the following two packages:

To find a component, you can browse the Directories in these packages. For example, to find the otlpreceiver, you would navigate to the receiver directory in the core package.

Finding the OpenTelemetry Collector component

Once you've located a component, you only need to copy the module path and take note of the version:

Copying the module path of an OpenTelemetry component

You can then combine the module path and the version tag in your manifest.yaml as follows:

yaml
12
receivers:
- gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.131.0

The exact set of components you'll choose to include depends on your data sources, where you're sending the data, and how you need to process it along the way.

Here is a recommended "starter pack" to consider for your custom collector:

Receivers

  • OTLP Receiver: For receiving telemetry data from your applications using the native OpenTelemetry Protocol.
  • Prometheus Receiver: For scraping Prometheus metrics.
  • File Log Receiver: Allows you to ingest log records into your pipeline from files.

Processors

Exporters

Connectors

Just ensure that each selected component is placed under the correct top-level field in your manifest: receivers, processors, exporters, connectors, extensions, and providers.

Containerizing your custom Collector

Running a local Collector binary is fine for testing, but in your release pipelines and production environments, you'll likely want to deploy it in a container.

A good practice is using a multi-stage Dockerfile. This approach uses a temporary builder container, which has all the necessary tools to compile your the Collector binary. Then, it copies only the final compiled program into a new, minimal production image.

Here's an example of what that looks like:

dockerfile
1234567891011121314151617181920212223242526272829
# Stage 1: Builder
FROM otel/opentelemetry-collector-builder:0.131.0 AS builder
WORKDIR /build
# Copy the manifest file and other necessary files
COPY manifest.yaml .
# Build the custom collector
RUN ocb --config=manifest.yaml
# Stage 2: Final Image
FROM cgr.dev/chainguard/static:latest
# Copy the Collector binary from the builder stage
COPY --from=builder /build/dist/otelcol-prod .
# Copy the Collector configuration file
COPY otelcol.yaml /etc/otelcol-prod/otelcol.yaml
# Expose necessary ports
EXPOSE 4317 4318
# Run the container as the built-in, unprivileged 'nonroot' user.
USER nonroot
# Set the default command to run when the container starts.
ENTRYPOINT ["/otelcol-prod"]
CMD ["--config=/etc/otelcol-prod/otelcol.yaml"]

With this Dockerfile, you'll get the following image which can be subsequently deployed to a container registry and integrated in your deployment workflow.

text
12
REPOSITORY TAG IMAGE ID CREATED SIZE
otelcol-prod 1.0.0 f650c1650880 2 minutes ago 28.7MB

Final thoughts

You now have the skills to choose the right components for your needs, build a lean and secure binary, and package it for deployment in any modern environment.

The result is a Collector that is smaller, more secure, and more efficient than any generic distribution.

To test and validate your OpenTelemetry Collector configuration, you can use the free OTelBin tool.

Thanks for reading!

Authors
Ayooluwa Isaiah
Ayooluwa Isaiah