Deploy an OpenTelemetry Collector
Run a Collector that receives ArmorIQ traces, batches them, retries failures, and forwards them over TLS to ArmorIQ and to other tools, without writing a secret in a file.
This guide is for operators and platform engineers. You get working Collector configurations that receive traces from ArmorIQ SDKs and forward them with batching, retry, size limits, and TLS. The ArmorIQ API key comes from an environment variable. No file holds a secret.
An OpenTelemetry Collector is a program that you run. It receives trace data, changes it, and sends it on to one or more destinations. A span is one timed step in an agent run, and a trace is all the spans of one run.
When to use a Collector
Use a Collector when you need one of these:
- Send traces to ArmorIQ and to another tool. An SDK has one export choice. For a second destination, the SDK sends to the Collector and the Collector sends to both.
- Use transport features the TypeScript SDK does not have. The TypeScript SDK exporter does not support mTLS, custom CA files, HTTP proxy settings, gRPC, or compression. Set these on the Collector.
- Keep credentials out of application processes. The Collector holds the ArmorIQ API key and the keys for other tools.
- Control memory and queues in one place.
If you only need ArmorIQ dashboards, do not use a Collector. Keep the default armoriq export. See Choose who exports your traces.
How the data flows
- Set the SDK to
exporter=externalwith the Collector as the endpoint. - The Collector receives spans on its OTLP HTTP port.
- The Collector limits memory, groups spans into batches, and sends each batch to every destination.
The SDK does not send your ArmorIQ API key to the Collector. The Collector holds the key and adds it when it forwards to ArmorIQ. ArmorIQ takes your organization and product from that key only. It ignores any such value inside the spans. Use one key for each organization and product that you forward.
What ArmorIQ accepts
Set your Collector inside these limits. ArmorIQ rejects a request that is outside them.
| Rule | Value |
|---|---|
| Route | POST /v1/traces |
| Content type | application/x-protobuf only. OTLP/JSON gets 415. |
| Compression | None, or gzip. Another value gets 415. |
| Authentication | Header x-api-key: <your key>. Authorization: Bearer <your key> also works. |
| Size of the body as sent | At most 1,000,000 bytes. A larger body gets 413. |
| Size after decompression | At most 4,000,000 bytes. |
| Spans in one scope | At most 500 |
| Spans in one request | At most 2,000 |
| Rate | 120 requests each minute for each API key. More gets 429. |
ArmorIQ answers with these codes:
| Answer | Meaning | What the Collector does |
|---|---|---|
200 | Accepted. A partial_success field can list spans that ArmorIQ rejected for a reason that a retry cannot fix. | Does not retry |
400 | The request is malformed or over a limit. | Does not retry |
401 | The API key is missing or not valid. | Does not retry. Fix the key. |
413, 415 | Body too large, or wrong content type or compression. | Does not retry. Fix the config. |
429 | A rate limit or the monthly span quota. For quota, the answer includes Retry-After: 60. | Retries |
503 | ArmorIQ storage or policy is not available for a short time. | Retries |
A retry is safe. ArmorIQ stores each span one time, even when a batch arrives twice.
Configuration for ArmorIQ only
Save this as otelcol.yaml. The file holds no secret. The Collector reads ARMORIQ_API_KEY from its environment.
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
# Turn on TLS for traffic from your applications.
tls:
cert_file: /etc/otelcol/tls/server.crt
key_file: /etc/otelcol/tls/server.key
processors:
# Put memory_limiter first. It refuses data before the Collector runs out of memory.
memory_limiter:
check_interval: 1s
limit_mib: 512
spike_limit_mib: 128
# Keep every batch inside the ArmorIQ limits.
batch:
send_batch_size: 256
send_batch_max_size: 500
timeout: 5s
exporters:
otlphttp/armoriq:
endpoint: https://api.armoriq.ai # the exporter adds /v1/traces
headers:
x-api-key: ${env:ARMORIQ_API_KEY}
compression: gzip
timeout: 15s
tls:
insecure: false
min_version: "1.2"
retry_on_failure:
enabled: true
initial_interval: 1s
max_interval: 30s
max_elapsed_time: 300s
sending_queue:
enabled: true
num_consumers: 4
queue_size: 1000
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlphttp/armoriq]What each part does:
- Batching.
batchsends a group of spans after 256 spans or 5 seconds, whichever comes first.send_batch_max_size: 500splits a larger group. This keeps each request under the ArmorIQ limit of 500 spans for each scope. The batch processor default is 8,192 spans, which is too large for ArmorIQ. - Retry.
retry_on_failureretries a failed send with a growing wait, from 1 second up to 30 seconds. It stops after 5 minutes. Then the Collector drops that batch. - Bounds.
memory_limitercaps memory use.sending_queue.queue_sizecaps how many batches wait for a destination that is slow or down. When the queue is full, the Collector drops new data. It does not use unlimited memory. - TLS. The
https://endpoint uses TLS, andinsecure: falsekeeps certificate checks on. The receivertlsblock protects the link from your applications. - API key.
${env:ARMORIQ_API_KEY}reads the key from the environment when the Collector starts. If the variable is not set, the key is empty and ArmorIQ answers401. - Protocol. The exporter default is protobuf with gzip. Do not set
encoding: json. ArmorIQ rejects JSON.
Newer Collector releases rename the otlphttp exporter to otlp_http. The old name still works as an alias. Use the name that your Collector version documents.
Configuration for ArmorIQ and a second tool
Use one pipeline for each destination. If one destination is slow, the other does not wait.
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
tls:
cert_file: /etc/otelcol/tls/server.crt
key_file: /etc/otelcol/tls/server.key
processors:
memory_limiter:
check_interval: 1s
limit_mib: 512
spike_limit_mib: 128
batch/armoriq:
send_batch_size: 256
send_batch_max_size: 500
timeout: 5s
batch/vendor:
send_batch_size: 512
send_batch_max_size: 1024
timeout: 5s
exporters:
otlphttp/armoriq:
endpoint: https://api.armoriq.ai
headers:
x-api-key: ${env:ARMORIQ_API_KEY}
timeout: 15s
tls:
insecure: false
retry_on_failure:
enabled: true
initial_interval: 1s
max_interval: 30s
max_elapsed_time: 300s
sending_queue:
enabled: true
queue_size: 1000
otlphttp/vendor:
# The full traces URL from the vendor's OTLP documentation.
traces_endpoint: ${env:VENDOR_OTLP_TRACES_ENDPOINT}
headers:
authorization: ${env:VENDOR_OTLP_AUTHORIZATION}
timeout: 15s
tls:
insecure: false
retry_on_failure:
enabled: true
max_elapsed_time: 300s
sending_queue:
enabled: true
queue_size: 1000
service:
pipelines:
traces/armoriq:
receivers: [otlp]
processors: [memory_limiter, batch/armoriq]
exporters: [otlphttp/armoriq]
traces/vendor:
receivers: [otlp]
processors: [memory_limiter, batch/vendor]
exporters: [otlphttp/vendor]The second tool has its own limits. Check its OTLP documentation for the maximum batch size.
Keep content away from the second tool
The SDK sends prompt and tool content to a Collector only when your organization allows external content. See What data is captured. You can also remove content in the Collector before it reaches a tool. The attributes processor is in the Collector contrib distribution:
processors:
attributes/no-content:
actions:
- key: gen_ai.input.messages
action: delete
- key: gen_ai.output.messages
action: delete
- key: gen_ai.tool.call.arguments
action: delete
- key: gen_ai.tool.call.result
action: deleteAdd attributes/no-content to the traces/vendor pipeline, after memory_limiter and before the batch processor.
Run the Collector
Pass the key from your secret store into the environment. Do not write it in a file or a command history.
docker run --rm \
-e ARMORIQ_API_KEY \
-v "$PWD/otelcol.yaml:/etc/otelcol/config.yaml:ro" \
-v "$PWD/tls:/etc/otelcol/tls:ro" \
-p 4318:4318 \
otel/opentelemetry-collector-contrib:<version> \
--config /etc/otelcol/config.yamlReplace <version> with a release tag that you have tested. In Kubernetes, put the key in a Secret and map it to the environment variable with secretKeyRef.
Point the SDK at the Collector
Set the SDK to external and give it the Collector address.
export ARMORIQ_OTEL_EXPORTER=external
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://collector.example.com:4318/v1/traces- The SDK accepts
http:only forlocalhost,127.0.0.1, and::1. For any other host, usehttps:. - A good pattern is a Collector next to each application, for example as a sidecar. The SDK sends to
http://localhost:4318/v1/traceson the same machine. The Collector then sends over TLS to ArmorIQ. This also works for the TypeScript SDK, which has no setting for a custom certificate authority. - If the Collector needs a header, set
OTEL_EXPORTER_OTLP_TRACES_HEADERS. See Choose who exports your traces.
Spans that reach ArmorIQ through external export carry the attributes of an external export, not every ArmorIQ-only attribute. For example, the TypeScript SDK writes the session ID as the standard session.id instead of armoriq.session_id, and ArmorIQ does not store session.id. A trace that arrives this way can lack its session link and other ArmorIQ-only fields. Open the first trace in the dashboard and check it before you rely on the Collector path for ArmorIQ.
Check that it works
- Run one agent request.
- Look at the Collector logs. There must be no export errors for
otlphttp/armoriq. - In ArmorIQ, open Settings > Observability and read Usage and pipeline health. The count of accepted spans must rise. See Organization controls.
Next steps
Choose Who Exports Your Traces
Decide whether ArmorIQ or your application owns the OpenTelemetry tracer provider, and set the destination for your traces.
What Data Is Captured
See exactly which fields ArmorIQ records in each capture mode, which fields are never recorded, and what leaves your network when you export traces.