Deploy

Deploy the Receiver app to Kubernetes via Helm.

The chart deploys your forwarder with the 10x Engine as a sidecar process. The workload kind (DaemonSet, Deployment, StatefulSet) and the exact integration shape are determined by the upstream chart for your forwarder. Pick yours below.

Step 1: Prerequisites
Requirement Description
Log10x License Your license key (get one)
Helm Helm CLI installed
kubectl Configured to access your cluster
GitHub Token Personal access token for config repo (create one)
Output Destination Elasticsearch, Splunk, or other log backend configured
Step 2: Add Helm Repository

Fluentd uses the upstream fluent/fluentd chart. The 10x sidecar is injected via a Helm post-renderer with a kustomize overlay.

helm repo add fluent https://fluent.github.io/helm-charts
helm repo update
helm search repo fluent/fluentd

Fluent Bit uses the upstream fluent/fluent-bit chart. The integration is a values overlay added to your existing Fluent Bit deployment.

helm repo add fluent https://fluent.github.io/helm-charts
helm repo update
helm search repo fluent/fluent-bit

Filebeat uses the upstream elastic/filebeat chart. The integration is an image swap: replace the chart's default docker.elastic.co/beats/filebeat image with the prebuilt log10x/filebeat-10x image, which runs Filebeat under a filebeat ... 2>&1 | tenx run ... pipe inside the container. The 10x engine runs as a child process of Filebeat's entrypoint, not as a separate container. The rest of the integration is a values overlay (image: + daemonset.filebeatConfig.filebeat.yml).

helm repo add elastic https://helm.elastic.co
helm repo update
helm search repo elastic/filebeat

The OpenTelemetry Collector uses the upstream open-telemetry/opentelemetry-collector chart. The integration is a values overlay added to your existing Collector deployment.

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm search repo open-telemetry/opentelemetry-collector

Vector uses the upstream vector/vector chart. The integration is a values overlay added to your existing Vector deployment.

helm repo add vector https://helm.vector.dev
helm repo update
helm search repo vector/vector

Logstash uses the upstream elastic/logstash chart. The integration is a values overlay added to your existing Logstash deployment.

helm repo add elastic https://helm.elastic.co
helm repo update
helm search repo elastic/logstash

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based. For VM infrastructure, see the Splunk UF receiver guide.

For Kubernetes, use the Fluent Bit or OTel Collector tab. For VM infrastructure, see the Datadog Agent receiver guide.

View all chart values:

helm show values fluent/fluentd
helm show values fluent/fluent-bit
helm show values elastic/filebeat
helm show values open-telemetry/opentelemetry-collector
helm show values vector/vector
helm show values elastic/logstash

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based. For VM infrastructure, see the Splunk UF receiver guide.

For Kubernetes, use the Fluent Bit or OTel Collector tab. For VM infrastructure, see the Datadog Agent receiver guide.

Step 3: Configure Deployment Settings

Create a new file called my-receiver.yaml in your working directory. This Helm values file will be used in all subsequent steps. Pick your forwarder below; each tab is self-contained.

Fluentd's setup has two parts: a values file for the upstream chart, and a small kustomize overlay that injects the 10x sidecar. Both live in your GitOps repo and stay versioned together.

a. Values file. Standard upstream-chart options:

my-receiver.yaml
kind: Deployment            # or DaemonSet for host-log tailing
replicaCount: 1

# Use the plain fluent/fluentd image (variant images that bundle the
# ES/Splunk/etc. plugins also work; `fluent-plugin-prometheus` is
# NOT required since the overlay replaces the default probes).
image:
  repository: fluent/fluentd
  tag: v1.18-debian-1

# No host log mounts in this example (the source below uses the
# `dummy` plugin). For real container-log tailing flip these to
# true and switch `kind` to DaemonSet.
mountVarLogDirectory: false
mountDockerContainersDirectory: false

rbac:
  create: false
serviceAccount:
  create: true
service:
  enabled: false
podSecurityPolicy:
  enabled: false

# Replace the chart's default config with the @INGEST / @OUTPUT
# routing pattern. `@INGEST` runs your enrichment filters once and
# forwards events to 10x on :24224; `@OUTPUT` receives processed
# events back on :24225 and writes them to your real destinations.
fileConfigs:

  01_sources.conf: |-
    # Replace this dummy source with your real sources (tail,
    # http, k8s, …). Every source MUST route to `@INGEST`.
    <source>
      @type dummy
      @id in_dummy
      tag k8s.demo
      rate 1
      dummy {"log":"hello from fluentd dummy plugin","level":"info"}
      auto_increment_key seq
      @label @INGEST
    </source>

  02_filters.conf: |-
    <label @INGEST>
      # Enrichment filters go here: k8s_metadata, parsers,
      # record_transformer, etc. They fire exactly once per event;
      # the return path skips this label.
      <filter **>
        @type record_transformer
        enable_ruby false
        <record>
          cluster "${ENV['CLUSTER_NAME'] || 'unset'}"
        </record>
      </filter>

      # Hand off to the 10x sidecar. `keepalive true` is
      # important for the sidecar topology; without it Fluentd
      # opens a fresh TCP connection per chunk (the default is
      # `keepalive false`), generating one connect/disconnect
      # cycle per `flush_interval` on the loopback path.
      # `require_ack_response false` is safe because both
      # processes share the pod's network namespace; for
      # cross-host forwarding flip it to `true` and pair with a
      # file-backed buffer.
      <match **>
        @type forward
        @id out_to_tenx
        keepalive true
        keepalive_timeout 60s
        send_timeout 5s
        require_ack_response false
        <server>
          host 127.0.0.1
          port 24224
        </server>
        <buffer>
          @type memory
          flush_interval 1s
          flush_thread_count 1
        </buffer>
      </match>
    </label>

  03_dispatch.conf: |-
    # Forward source receiving processed events back from 10x.
    # Bind on 0.0.0.0 so kubelet's tcpSocket probe can reach it
    # from the pod IP. The sidecar still connects via 127.0.0.1.
    <source>
      @type forward
      @id in_from_tenx
      bind 0.0.0.0
      port 24225
      @label @OUTPUT
    </source>

  04_outputs.conf: |-
    # `@OUTPUT` is the terminal label; replace `stdout` with your
    # destinations (out_elasticsearch, out_splunk_hec, out_kafka2,
    # out_s3, …). Keep this label filter-free.
    <label @OUTPUT>
      <match **>
        @type stdout
        @id out_stdout
      </match>
    </label>

b. Kustomize overlay. Four files in a tenx-kustomize/ directory next to your values file:

tenx-kustomize/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - helm-output.yaml          # populated by post-render.sh at install time
patches:
  - path: sidecar-patch.yaml
    # The chart names the Deployment `<release>-fluentd`; the
    # examples below use `my-receiver` as the release name (Step 7).
    target: { kind: Deployment, name: my-receiver-fluentd }
tenx-kustomize/sidecar-patch.yaml
# Adds the log10x sidecar container to the Fluentd pod. Kubernetes
# restarts the container automatically if the 10x process exits.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-receiver-fluentd     # match your kustomization.yaml target
spec:
  template:
    spec:
      containers:
        - name: log10x
          image: log10x/edge-10x:latest
          imagePullPolicy: IfNotPresent
          args:
            - "@run/input/forwarder/fluentd"
            - "@apps/receiver"
            # ─── Actions ────────────────────────────────────────
            # The Receiver applies a per-pattern action (pass,
            # sample, compact, tier_down, offload, drop). An agent
            # picks the action per service via the log10x MCP
            # `configure_engine` tool, which writes a cap/action CSV
            # the engine hot-reloads through the GitOps repo. The
            # args here set the engine-wide default before any CSV.
            #
            # Default (no extra args): pass: events go through
            # unchanged.
            #
            # Add `receiverOptimize true` to default to the compact
            # action: the `log` field's value is replaced with a
            # losslessly-encoded form and a separate `tenx-template`
            # event is emitted per pattern.
            #
            # Add `symbolMessageHashField <name>` to also surface
            # the symbol-pattern hash as a top-level field on each
            # event (useful as a dedup key, metric dimension, or
            # correlation ID).
          env:
            - name: TENX_LICENSE_FILE
              value: /etc/tenx/license/license.jwt
          volumeMounts:
            - name: tenx-license
              mountPath: /etc/tenx/license
              readOnly: true
          resources:
            requests: { cpu: 100m, memory: 256Mi }
            limits:   { cpu: 500m, memory: 512Mi }
      volumes:
        - name: tenx-license
          secret:
            secretName: receiver-credentials
            items:
              - key: license-jwt
                path: license.jwt
tenx-kustomize/post-render.sh
#!/usr/bin/env bash
# Bridges Helm's stdin/stdout post-renderer protocol to kustomize's
# file-based model. Captures Helm's rendered manifests, then runs
# `kubectl kustomize` to apply the patches.
set -euo pipefail
DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
cat > "$DIR/helm-output.yaml"
exec kubectl kustomize "$DIR"
tenx-kustomize/post-render.cmd
@echo off
REM Windows shim. Helm.exe can't directly exec a .sh on Windows.
REM Use this path as `--post-renderer` on Windows; Linux/macOS use
REM the .sh directly.
bash "%~dp0post-render.sh"

Make the shell script executable: chmod +x tenx-kustomize/post-render.sh.

Airgapped mode (optional). For clusters with no path to the Log10x SaaS (security policy, isolated network), append TENX_AIRGAPPED=true to the log10x container's env: block in tenx-kustomize/sidecar-patch.yaml:

tenx-kustomize/sidecar-patch.yaml
env:
  - name: TENX_LICENSE_FILE
    value: /etc/tenx/license/license.jwt
  - name: TENX_AIRGAPPED       # ← add this
    value: "true"              # ← add this

The engine then verifies the license JWT locally against the embedded public key and makes zero outbound calls to the Log10x gateway. Fluentd's output destinations are unaffected. demo and limited license types cannot run airgapped; the engine warns and falls back to online.

The Fluent Bit integration is a values overlay (extraContainers + config: replacement) layered on top of your existing Fluent Bit values via helm -f. The 10x sidecar and Fluent Bit run as peer containers in the same pod and talk over loopback TCP.

Fluent Bit routes purely on tag, so the bypass (what keeps Fluent Bit's filters and the ingest [OUTPUT] forward from re-firing on events coming back from log10x) is tag-prefix namespacing: the egress [INPUT] forward uses Tag_Prefix tenx. to prepend tenx. to every returning tag. Filters and the ingest [OUTPUT] forward then Match app.* (or whatever your source-tag scheme is) and skip the returning tenx.* events.

my-receiver.yaml
# Layer this on top of your existing Fluent Bit values:
#   helm upgrade --install my-receiver fluent/fluent-bit \
#     -f your-existing-fluent-bit-values.yaml \
#     -f my-receiver.yaml \
#     --namespace logging

# 10x sidecar container in the same pod as `fluent-bit`. They share
# the pod's network namespace, so 127.0.0.1:24224 / :24225 are
# reachable between containers without any shared volume.
extraContainers:
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/fluentbit"
      - "@apps/receiver"
      # ─── Actions ────────────────────────────────────────────────
      # Per-pattern action (pass, sample, compact, tier_down,
      # offload, drop) is set by the agent via the log10x MCP
      # `configure_engine` tool as a cap/action CSV the engine
      # hot-reloads through GitOps. These args set the engine-wide
      # default before any CSV.
      #
      # Default (no extra args): pass: events go through unchanged.
      #
      # Add `receiverOptimize true` to default to the compact action: the
      # `log` field's value is replaced with a losslessly-encoded
      # form and a separate `tag: tenx-template` event is emitted
      # per first-seen pattern.
      #
      # Add `symbolMessageHashField <name>` to also surface the
      # symbol-pattern hash as a top-level field on each event
      # (useful as a dedup key, metric dimension, or correlation ID).
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
    resources:
      requests: { cpu: 100m, memory: 256Mi }
      limits:   { cpu: 500m, memory: 512Mi }

extraVolumes:
  - name: tenx-license
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt

# Replace the chart's default config with the sidecar pattern.
# `inputs`/`filters`/`outputs` are inline strings that the chart
# mounts into /fluent-bit/etc/conf/. Keep `[SERVICE] HTTP_Server On`
# so the chart's default health probe (httpGet :2020) keeps working.
config:
  service: |
    [SERVICE]
        Daemon Off
        Flush {{ .Values.flush }}
        Log_Level {{ .Values.logLevel }}
        Parsers_File /fluent-bit/etc/parsers.conf
        Parsers_File /fluent-bit/etc/conf/custom_parsers.conf
        HTTP_Server On
        HTTP_Listen 0.0.0.0
        HTTP_Port {{ .Values.metricsPort }}
        Health_Check On

  # Replace these with your real sources (tail, http, systemd, …).
  # Tag with ANY scheme that does not start with `tenx.`; the
  # egress input below prepends `tenx.` to returning tags, and the
  # ingest `forward` output below matches on the non-`tenx.*` tag.
  inputs: |
    [INPUT]
        Name tail
        Path /var/log/containers/*.log
        multiline.parser docker, cri
        Tag kube.*
        Mem_Buf_Limit 5MB
        Skip_Long_Lines On

    # Egress: receive processed events back from 10x. `Tag_Prefix
    # tenx.` is the bypass; every returning event gets `tenx.`
    # prepended to its original tag, so filters and the ingest
    # `forward` output (which Match `kube.*`) skip it.
    #
    # Listen on 0.0.0.0 so kubelet's httpGet probe (on :2020) and
    # the 10x sidecar (on 127.0.0.1:24225) can both reach it.
    [INPUT]
        Name forward
        Listen 0.0.0.0
        Port 24225
        Tag_Prefix tenx.

  # Enrichment filters Match the user-tag scheme only (NOT `*`) so
  # they don't re-fire on returning `tenx.*` events.
  filters: |
    [FILTER]
        Name kubernetes
        Match kube.*
        Merge_Log On
        Keep_Log Off
        K8S-Logging.Parser On
        K8S-Logging.Exclude On

  outputs: |
    # Hand off to the 10x sidecar.
    [OUTPUT]
        Name forward
        Match kube.*
        Host 127.0.0.1
        Port 24224
        Retry_Limit False

    # Destinations Match the `tenx.*` namespace; replace this stdout
    # block with your real destinations (es, splunk, kafka, s3, …).
    # The `tenx.` prefix on the wire is what keeps enrichment from
    # re-firing and the ingest `forward` output from looping events.
    [OUTPUT]
        Name stdout
        Match tenx.*
        Format json_lines

Airgapped mode (optional). For clusters with no path to the Log10x SaaS (security policy, isolated network), append TENX_AIRGAPPED=true to the log10x container's env: block under extraContainers in my-receiver.yaml:

my-receiver.yaml
env:
  - name: TENX_LICENSE_FILE
    value: /etc/tenx/license/license.jwt
  - name: TENX_AIRGAPPED       # ← add this
    value: "true"              # ← add this

The engine then verifies the license JWT locally against the embedded public key and makes zero outbound calls to the Log10x gateway. Fluent Bit's output destinations are unaffected. demo and limited license types cannot run airgapped; the engine warns and falls back to online.

The Filebeat integration is a values overlay applied to the upstream elastic/filebeat chart. The integration is an image swap: the chart's default Filebeat image is replaced with the prebuilt log10x/filebeat-10x image, which wraps Filebeat's entrypoint in filebeat ... 2>&1 | tenx run .... The 10x engine runs as a child process of Filebeat's entrypoint inside the same container; there is no separate sidecar container.

Every filebeat.input MUST carry the tenx-receive.js script processor: it tags the event, writes it to Filebeat's stdout (which the in-container engine reads), and cancels it locally. Processed events come back to Filebeat through a unix input loaded via filebeat.config.inputs from a snippet bundled with the image. output.console is not supported: it would write to the same stdout pipe that carries events to the engine and corrupt the stream.

my-receiver.yaml
# Layer this on top of any existing Filebeat values:
#   helm upgrade --install my-receiver elastic/filebeat \
#     -f your-existing-filebeat-values.yaml \
#     -f my-receiver.yaml \
#     --namespace logging

# Image swap: the log10x-built Filebeat image runs the engine as a
# child of Filebeat's entrypoint (filebeat -e 2>&1 | tenx run ...).
image: "log10x/filebeat-10x"
imageTag: "latest"          # pin to a release tag in production
imagePullPolicy: IfNotPresent

daemonset:
  extraEnvs:
    # Point the engine at the mounted license file (Step 5).
    - name: TENX_LICENSE_FILE
      value: /etc/tenx/license/license.jwt

    # ─── Actions ────────────────────────────────────────────────
    # Per-pattern action (pass, sample, compact, tier_down,
    # offload, drop) is set by the agent via the log10x MCP
    # `configure_engine` tool as a cap/action CSV the engine
    # hot-reloads through GitOps. TENX_RUN_ARGS sets the engine-wide
    # default before any CSV.
    #
    # Default (no override): pass: events go through unchanged.
    #
    # Override TENX_RUN_ARGS to change the default action:
    # - Add `receiverOptimize true` to default to the compact
    #   action: the value of the `message` field is replaced with a
    #   losslessly-encoded form and a separate `tenx-template`
    #   event is emitted per first-seen pattern.
    # - Add `symbolMessageHashField <name>` to also emit the
    #   symbol-pattern hash as a top-level field (useful as a
    #   dedup key, metric dimension, or correlation ID).
    #
    # - name: TENX_RUN_ARGS
    #   value: "@run/input/forwarder/filebeat @apps/receiver receiverOptimize true"

  extraVolumes:
    - name: tenx-license
      secret:
        secretName: receiver-credentials
        items:
          - key: license-jwt
            path: license.jwt

  extraVolumeMounts:
    - name: tenx-license
      mountPath: /etc/tenx/license
      readOnly: true

  # `filebeat.config.inputs` loads the unix-socket input that
  # receives processed events back from the engine. Every entry in
  # `filebeat.inputs` must carry the tenx-receive.js script
  # processor; without it the event never reaches the engine.
  filebeatConfig:
    filebeat.yml: |
      filebeat.config.inputs:
        enabled: true
        path: ${TENX_MODULES}/pipelines/run/modules/input/forwarder/filebeat/conf/tenxNix.yml

      filebeat.inputs:
      - type: container
        paths:
          - /var/log/containers/*.log
        processors:
        - add_kubernetes_metadata:
            host: ${NODE_NAME}
            matchers:
            - logs_path:
                logs_path: "/var/log/containers/"
        # Hand every event off to 10x via Filebeat's stdout.
        # tenx-receive.js cancels the event locally so destinations
        # only ship the version that comes back on the unix socket.
        - script:
            lang: javascript
            file: ${TENX_MODULES}/pipelines/run/modules/input/forwarder/filebeat/script/tenx-receive.js

      # Use any non-stdout output. output.console must NOT be used:
      # it would collide with the stdout pipe carrying events to
      # the engine. See Step 6 for filling in your destination.
      output.elasticsearch:
        hosts: '["https://elasticsearch-master:9200"]'

Airgapped mode (optional). For clusters with no path to the Log10x SaaS (security policy, isolated network), append TENX_AIRGAPPED=true to daemonset.extraEnvs in my-receiver.yaml:

my-receiver.yaml
daemonset:
  extraEnvs:
    - name: TENX_LICENSE_FILE
      value: /etc/tenx/license/license.jwt
    - name: TENX_AIRGAPPED       # ← add this
      value: "true"              # ← add this

The engine then verifies the license JWT locally against the embedded public key and makes zero outbound calls to the Log10x gateway. Filebeat's output destinations are unaffected. demo and limited license types cannot run airgapped; the engine warns and falls back to online.

Layer this additive overlay on top of your existing Collector values: the chart's config: is deep-merged, so your existing receivers/processors/exporters stay; the overlay adds the sidecar container, the otlp/tenx exporter, the otlp/tenx receiver for the return path, and rewires your logs: pipeline through 10x. The 10x sidecar and the Collector run as peer containers in the same pod and talk over loopback TCP. Both directions are OTLP/gRPC, so the core otel/opentelemetry-collector image is sufficient. The OTLP wire carries the full record (resource attributes, scope info, log-record attributes, body) so k8s metadata round-trips back to your destinations.

my-receiver.yaml
# Layer this on top of your existing Collector values:
#   helm upgrade --install my-receiver open-telemetry/opentelemetry-collector \
#     -f your-existing-otel-values.yaml \
#     -f my-receiver.yaml \
#     --namespace logging

# 10x sidecar in the same pod as the Collector. Both containers
# share the pod network, so they reach each other over 127.0.0.1.
extraContainers:
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/otel-collector"
      - "@apps/receiver"
      # ─── Actions ────────────────────────────────────────────────
      # Per-pattern action (pass, sample, compact, tier_down,
      # offload, drop) is set by the agent via the log10x MCP
      # `configure_engine` tool as a cap/action CSV the engine
      # hot-reloads through GitOps. These args set the engine-wide
      # default before any CSV.
      #
      # Default (no extra args): pass: the record goes through
      # unchanged.
      #
      # Add `receiverOptimize true` to default to the compact action: the
      # message field is replaced with a losslessly-encoded form
      # and a separate `tag: tenx-template` event is emitted per
      # first-seen pattern.
      #
      # Add `symbolMessageHashField <name>` to also emit the
      # symbol-pattern hash as a top-level field (surfaces as an
      # attribute on the returning OTel log record, useful as a
      # dedup key, metric dimension, or correlation ID).
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
    resources:
      requests: { cpu: 100m, memory: 256Mi }
      limits:   { cpu: 500m, memory: 512Mi }

extraVolumes:
  - name: tenx-license
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt

# `config:` is deep-merged with the chart defaults and with the
# `config:` block in your existing values file. The overlay adds
# the new receiver/exporter, rewires your `logs` pipeline through
# `otlp/tenx`, and appends a `logs/from-tenx` pipeline for the
# return path.
config:
  receivers:
    # Receive processed events back from the 10x sidecar over
    # OTLP/gRPC. Bind on 0.0.0.0 so the port is reachable from the
    # pod IP (the sidecar still connects via 127.0.0.1).
    otlp/tenx:
      protocols:
        grpc:
          endpoint: 0.0.0.0:24225

  exporters:
    # Hand off to the 10x sidecar over OTLP/gRPC.
    otlp/tenx:
      endpoint: 127.0.0.1:4317
      tls:
        insecure: true

  service:
    pipelines:
      # Replace your existing `logs` pipeline. Spell out every
      # receiver/processor/exporter; Helm replaces these lists
      # wholesale on merge.
      logs:
        receivers: [filelog]                       # ← your existing receivers
        processors:
          - memory_limiter
          - batch
        exporters: [otlp/tenx]

      # New egress pipeline. Keep it processor-free so enrichment
      # runs exactly once.
      logs/from-tenx:
        receivers: [otlp/tenx]
        exporters: [elasticsearch]                 # ← your existing destinations

      # Optional: drop the chart-default `metrics` and `traces`
      # pipelines (which fan chart-internal data into the `debug`
      # exporter) by setting them to null.
      # metrics: null
      # traces: null

# Probes use the chart defaults (httpGet :13133/ on the
# health_check extension). The chart's values schema only allows
# httpGet probes, so leave these untouched.

Airgapped mode (optional). For clusters with no path to the Log10x SaaS (security policy, isolated network), append TENX_AIRGAPPED=true to the log10x container's env: block under extraContainers in my-receiver.yaml:

my-receiver.yaml
env:
  - name: TENX_LICENSE_FILE
    value: /etc/tenx/license/license.jwt
  - name: TENX_AIRGAPPED       # ← add this
    value: "true"              # ← add this

The engine then verifies the license JWT locally against the embedded public key and makes zero outbound calls to the Log10x gateway. The Collector's exporters are unaffected. demo and limited license types cannot run airgapped; the engine warns and falls back to online.

The Vector integration is a values overlay (extraContainers + customConfig) layered on top of your existing Vector values via helm -f. The 10x sidecar and Vector run as peer containers in the same pod and talk over loopback TCP.

my-receiver.yaml
# Layer this on top of your existing Vector values:
#   helm upgrade --install my-receiver vector/vector \
#     -f your-existing-vector-values.yaml \
#     -f my-receiver.yaml \
#     --namespace logging

# 10x sidecar container in the same pod as `vector`. They share the
# pod's network namespace, so 127.0.0.1:9000 / :9001 are reachable
# between containers without any shared volume.
extraContainers:
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/vector"
      - "@apps/receiver"
      # ─── Actions ────────────────────────────────────────────────
      # Per-pattern action (pass, sample, compact, tier_down,
      # offload, drop) is set by the agent via the log10x MCP
      # `configure_engine` tool as a cap/action CSV the engine
      # hot-reloads through GitOps. These args set the engine-wide
      # default before any CSV.
      #
      # Default (no extra args): pass: events go through unchanged.
      #
      # Add `receiverOptimize true` to default to the compact action: the
      # `message` field's value is replaced with a losslessly-encoded
      # form and a separate `tag: tenx-template` event is emitted
      # per first-seen pattern.
      #
      # Add `symbolMessageHashField <name>` to also surface the
      # symbol-pattern hash as a top-level field on each event
      # (useful as a dedup key, metric dimension, or correlation ID).
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
    resources:
      requests: { cpu: 100m, memory: 256Mi }
      limits:   { cpu: 500m, memory: 512Mi }

extraVolumes:
  - name: tenx-license
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt

# The vector chart auto-publishes container ports from each entry
# in `customConfig.sources`, so the 9001 port for `tenx_out` is
# already declared on the `vector` container and reachable from
# the pod IP. The 10x sidecar connects out to it on 127.0.0.1, no
# port declaration of its own needed.

# Replace the chart's default config with the sidecar pattern.
# Sources route to the `ingest` transform; sinks consume `tenx_out`
# so enrichment runs exactly once. The to-tenx and from-tenx legs
# are structurally disconnected; Vector's DAG IS the bypass.
customConfig:
  data_dir: /vector-data-dir

  sources:
    # Replace this with your real sources (file, kubernetes_logs,
    # journald, http_server, ...). All sources MUST feed the
    # `ingest` transform; anything wired directly to a destination
    # sink skips 10x.
    app_logs:
      type: kubernetes_logs

    # Receive processed events back from the 10x sidecar.
    # Bind on 0.0.0.0 so kubelet's tcpSocket probe can reach the
    # port from the pod IP. The 10x sidecar still connects via
    # 127.0.0.1 (same pod, same network namespace).
    tenx_out:
      type: fluent
      mode: tcp
      address: 0.0.0.0:9001

  transforms:
    # Enrichment runs here exactly once before handoff to 10x.
    # The return path skips this transform via the DAG wiring below.
    ingest:
      type: remap
      inputs: [app_logs]
      source: |
        .cluster = get_env_var("CLUSTER_NAME") ?? "unset"
        .tag = .source_type

  sinks:
    # Hand off to the 10x sidecar.
    tenx_in:
      type: socket
      inputs: [ingest]
      mode: tcp
      address: 127.0.0.1:9000
      encoding: { codec: json }
      framing: { method: newline_delimited }

    # Destinations consume `tenx_out` ONLY (never the raw sources
    # or `ingest`). That structural wiring is the enrichment bypass.
    destinations:
      type: elasticsearch         # or splunk_hec, kafka, aws_s3, ...
      inputs: [tenx_out]
      # ... destination-specific options ...

Airgapped mode (optional). For clusters with no path to the Log10x SaaS (security policy, isolated network), append TENX_AIRGAPPED=true to the log10x container's env: block under extraContainers in my-receiver.yaml:

my-receiver.yaml
env:
  - name: TENX_LICENSE_FILE
    value: /etc/tenx/license/license.jwt
  - name: TENX_AIRGAPPED       # ← add this
    value: "true"              # ← add this

The engine then verifies the license JWT locally against the embedded public key and makes zero outbound calls to the Log10x gateway. Vector's destinations are unaffected. demo and limited license types cannot run airgapped; the engine warns and falls back to online.

The Logstash integration is a values overlay (extraContainers + logstashConfig + logstashPipeline) layered on top of the elastic/logstash chart. The 10x sidecar and Logstash run as peer containers in the same pod and talk over loopback TCP.

my-receiver.yaml
# 10x sidecar container in the same pod as `logstash`. They share
# the pod's network namespace, so 127.0.0.1:5044 / :5045 are
# reachable between containers without any shared volume.
extraContainers: |
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/logstash"
      - "@apps/receiver"
      # ─── Actions ────────────────────────────────────────────────
      # Per-pattern action (pass, sample, compact, tier_down,
      # offload, drop) is set by the agent via the log10x MCP
      # `configure_engine` tool as a cap/action CSV the engine
      # hot-reloads through GitOps. These args set the engine-wide
      # default before any CSV.
      #
      # Default (no extra args): pass: events go through unchanged.
      #
      # Add `receiverOptimize true` to default to the compact action: the
      # `message` field's value is replaced with a losslessly-encoded
      # form and a separate `tag: tenx-template` event is emitted
      # per first-seen pattern.
      #
      # Add `symbolMessageHashField <name>` to also surface the
      # symbol-pattern hash as a top-level field on each event
      # (useful as a dedup key, metric dimension, or correlation ID).
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
    resources:
      requests: { cpu: 100m, memory: 256Mi }
      limits:   { cpu: 500m, memory: 512Mi }

secretMounts:
  - name: tenx-license
    secretName: receiver-credentials
    path: /etc/tenx/license/license.jwt
    subPath: license-jwt

# Multi-pipeline driver: Logstash runs the `ingest` and
# `destinations` pipelines side-by-side. Filters in `ingest`
# only see events on the way in; the `destinations` pipeline
# is filter-free so each event is enriched exactly once.
logstashConfig:
  pipelines.yml: |
    - pipeline.id: ingest
      path.config: "/usr/share/logstash/pipeline/tenx-ingest.conf"
    - pipeline.id: destinations
      path.config: "/usr/share/logstash/pipeline/tenx-destinations.conf"

# Pipeline configs. Replace the `file` input below with your real
# sources; replace the destinations pipeline's `stdout` output
# with your real destinations.
logstashPipeline:
  tenx-ingest.conf: |
    input {
      # Replace with your real Logstash inputs (file, beats, http, ...).
      file {
        path  => "/var/log/containers/*.log"
        codec => "json"
        start_position => "beginning"
      }
    }
    filter {
      # Enrichment filters run once per event. The `tag` field
      # becomes the event's source inside 10x. Set it to any
      # value that identifies the stream.
      mutate {
        add_field => { "cluster" => "${CLUSTER_NAME:unset}" }
        add_field => { "tag" => "k8s.containers" }
      }
    }
    output {
      # Hand off to the 10x sidecar.
      tcp {
        host  => "127.0.0.1"
        port  => 5044
        codec => json_lines
      }
    }
  tenx-destinations.conf: |
    input {
      # Bind on 0.0.0.0 so the port is reachable from outside
      # loopback (kubelet probe). The 10x sidecar connects via
      # 127.0.0.1 from inside the same pod.
      tcp {
        host  => "0.0.0.0"
        port  => 5045
        codec => json_lines
      }
    }
    output {
      # Replace `stdout` with your real destinations (elasticsearch,
      # opensearch, splunk_hec, kafka, s3, ...). Keep this pipeline
      # filter-free; enrichment lives in `tenx-ingest.conf`.
      stdout { codec => json_lines }
    }

Airgapped mode (optional). For clusters with no path to the Log10x SaaS (security policy, isolated network), append TENX_AIRGAPPED=true to the log10x container's env: block under extraContainers in my-receiver.yaml:

my-receiver.yaml
env:
  - name: TENX_LICENSE_FILE
    value: /etc/tenx/license/license.jwt
  - name: TENX_AIRGAPPED       # ← add this
    value: "true"              # ← add this

The engine then verifies the license JWT locally against the embedded public key and makes zero outbound calls to the Log10x gateway. Logstash's outputs are unaffected. demo and limited license types cannot run airgapped; the engine warns and falls back to online.

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based. For VM infrastructure, see the Splunk UF receiver guide.

For Kubernetes, use the Fluent Bit or OTel Collector tab. For VM infrastructure, see the Datadog Agent receiver guide.

Step 4: Load Configuration

Replace the config folder baked into the Log10x image with your own, either by cloning a git repository into an in-pod emptyDir via an init container, or by mounting a PersistentVolumeClaim that already holds the config. Both methods plug the config into the container running the 10x process; the exact chart hooks differ per forwarder.

If you skip this step, the default configuration bundled with the Log10x image is used.

  1. Fork the Config Repository (git path only) or pre-populate a PVC with the config (PVC path)
  2. Edit the app configuration to match your metric output and enrichment options
  3. Pick your forwarder and method below:

Fluentd's 10x sidecar lives in tenx-kustomize/sidecar-patch.yaml from Step 3 (the chart is wrapped in a Helm post-renderer with a kustomize overlay). Both methods are additions to that same patch file; the chart's values overlay isn't involved.

Git Repository. Clone a config repo into a shared emptyDir before the sidecar starts.

Add the git-token key to your Step 5 Secret (--from-literal=git-token=YOUR-GIT-TOKEN), then replace tenx-kustomize/sidecar-patch.yaml with the version below; the ← add markers show what's new relative to Step 3:

tenx-kustomize/sidecar-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-receiver-fluentd
spec:
  template:
    spec:
      # ← add: init container clones the repo into /data; the
      # emptyDir below makes /data === /etc/tenx/git in the sidecar.
      initContainers:
        - name: tenx-git-config
          image: log10x/git-config-fetcher:1.0.0
          env:
            - name: GIT_TOKEN
              valueFrom:
                secretKeyRef:
                  name: receiver-credentials
                  key: git-token
          args:
            - "--config-repo"
            - "https://github.com/YOUR-ACCOUNT/config.git"
            - "--config-branch"     # Optional, omit for repo default
            - "my-receiver-config"
            # If you also publish a symbol library, add:
            # - "--symbols-repo"
            # - "https://github.com/YOUR-ACCOUNT/symbols.git"
            # - "--symbols-branch"  # Optional
            # - "main"
          volumeMounts:
            - name: tenx-git
              mountPath: /data

      containers:
        - name: log10x
          image: log10x/edge-10x:latest
          imagePullPolicy: IfNotPresent
          args:
            - "@run/input/forwarder/fluentd"
            - "@apps/receiver"
          env:
            - name: TENX_LICENSE_FILE
              value: /etc/tenx/license/license.jwt
            - name: TENX_CONFIG               # ← add
              value: /etc/tenx/git/config
            # If you fetched a symbols repo too, also add:
            # - name: TENX_SYMBOLS_PATH
            #   value: /etc/tenx/git/config/data/shared/symbols
          volumeMounts:
            - name: tenx-license
              mountPath: /etc/tenx/license
              readOnly: true
            - name: tenx-git                  # ← add
              mountPath: /etc/tenx/git
              readOnly: true
          resources:
            requests: { cpu: 100m, memory: 256Mi }
            limits:   { cpu: 500m, memory: 512Mi }

      volumes:
        - name: tenx-license
          secret:
            secretName: receiver-credentials
            items:
              - key: license-jwt
                path: license.jwt
        - name: tenx-git                      # ← add
          emptyDir: {}

Persistent Volume. Mount an existing PVC. No init container, no git token, no external network access.

Replace tenx-kustomize/sidecar-patch.yaml from Step 3 with this version (again, ← add marks what's new):

tenx-kustomize/sidecar-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-receiver-fluentd
spec:
  template:
    spec:
      containers:
        - name: log10x
          image: log10x/edge-10x:latest
          imagePullPolicy: IfNotPresent
          args:
            - "@run/input/forwarder/fluentd"
            - "@apps/receiver"
          env:
            - name: TENX_LICENSE_FILE
              value: /etc/tenx/license/license.jwt
            - name: TENX_CONFIG               # ← add
              value: /etc/tenx/config
            # If your PVC also carries a symbol library, also add:
            # - name: TENX_SYMBOLS_PATH
            #   value: /etc/tenx/symbols
          volumeMounts:
            - name: tenx-license
              mountPath: /etc/tenx/license
              readOnly: true
            - name: tenx-config-volume        # ← add
              mountPath: /etc/tenx/config
              readOnly: true
          resources:
            requests: { cpu: 100m, memory: 256Mi }
            limits:   { cpu: 500m, memory: 512Mi }

      volumes:
        - name: tenx-license
          secret:
            secretName: receiver-credentials
            items:
              - key: license-jwt
                path: license.jwt
        - name: tenx-config-volume            # ← add
          persistentVolumeClaim:
            claimName: my-config-pvc

The 10x process runs in the log10x sidecar from Step 3 (added via the chart's extraContainers:). The env + volumeMounts additions go inside that sidecar's spec; the pod-level volume goes under the chart's extraVolumes:; the init container (git path only) goes under the chart's top-level initContainers:.

Git Repository. Add the git-token key to your Step 5 Secret (--from-literal=git-token=YOUR-GIT-TOKEN), then append to your my-receiver.yaml from Step 3. The ← add markers show the four surgical changes:

my-receiver.yaml (additions)
extraContainers:
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/git/config
      # If you also fetched a symbols repo:
      # - name: TENX_SYMBOLS_PATH
      #   value: /etc/tenx/git/config/data/shared/symbols
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-git                        # ← add
        mountPath: /etc/tenx/git
        readOnly: true

extraVolumes:
  - name: tenx-license                        # already from Step 3
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt
  - name: tenx-git                            # ← add
    emptyDir: {}

# Top-level `initContainers:` hook in fluent/fluent-bit, runs once
# per pod start and clones the repo into the shared emptyDir.
initContainers:                               # ← add
  - name: tenx-git-config
    image: log10x/git-config-fetcher:1.0.0
    env:
      - name: GIT_TOKEN
        valueFrom:
          secretKeyRef:
            name: receiver-credentials
            key: git-token
    args:
      - "--config-repo"
      - "https://github.com/YOUR-ACCOUNT/config.git"
      - "--config-branch"                     # Optional
      - "my-receiver-config"
      # If publishing a symbols repo, also add:
      # - "--symbols-repo"
      # - "https://github.com/YOUR-ACCOUNT/symbols.git"
      # - "--symbols-branch"                  # Optional
      # - "main"
    volumeMounts:
      - name: tenx-git
        mountPath: /data

Persistent Volume. Same shape, minus the init container and the git token. The PVC just gets mounted into the sidecar.

my-receiver.yaml (additions)
extraContainers:
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/config
      # If the PVC also carries a symbol library:
      # - name: TENX_SYMBOLS_PATH
      #   value: /etc/tenx/symbols
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-config-volume              # ← add
        mountPath: /etc/tenx/config
        readOnly: true

extraVolumes:
  - name: tenx-license                        # already from Step 3
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt
  - name: tenx-config-volume                  # ← add
    persistentVolumeClaim:
      claimName: my-config-pvc

Filebeat runs the 10x engine inside its only container (image swap). All hooks live under daemonset: in the elastic/filebeat chart, same scope as the license wiring from Step 3, except extraInitContainers:, which is at the chart root in this chart.

Git Repository. Add the git-token key to your Step 5 Secret (--from-literal=git-token=YOUR-GIT-TOKEN), then append to your my-receiver.yaml from Step 3:

my-receiver.yaml (additions)
daemonset:
  extraEnvs:
    - name: TENX_LICENSE_FILE                 # already from Step 3
      value: /etc/tenx/license/license.jwt
    - name: TENX_CONFIG                       # ← add
      value: /etc/tenx/git/config
    # If you also fetched a symbols repo:
    # - name: TENX_SYMBOLS_PATH
    #   value: /etc/tenx/git/config/data/shared/symbols

  extraVolumes:
    - name: tenx-license                      # already from Step 3
      secret:
        secretName: receiver-credentials
        items:
          - key: license-jwt
            path: license.jwt
    - name: tenx-git                          # ← add
      emptyDir: {}

  extraVolumeMounts:
    - name: tenx-license                      # already from Step 3
      mountPath: /etc/tenx/license
      readOnly: true
    - name: tenx-git                          # ← add
      mountPath: /etc/tenx/git
      readOnly: true

# `extraInitContainers:` is at the chart root, NOT under daemonset:.
extraInitContainers:                          # ← add
  - name: tenx-git-config
    image: log10x/git-config-fetcher:1.0.0
    env:
      - name: GIT_TOKEN
        valueFrom:
          secretKeyRef:
            name: receiver-credentials
            key: git-token
    args:
      - "--config-repo"
      - "https://github.com/YOUR-ACCOUNT/config.git"
      - "--config-branch"                     # Optional
      - "my-receiver-config"
      # If publishing a symbols repo, also add:
      # - "--symbols-repo"
      # - "https://github.com/YOUR-ACCOUNT/symbols.git"
      # - "--symbols-branch"                  # Optional
      # - "main"
    volumeMounts:
      - name: tenx-git
        mountPath: /data

Persistent Volume. Append to your my-receiver.yaml from Step 3:

my-receiver.yaml (additions)
daemonset:
  extraEnvs:
    - name: TENX_LICENSE_FILE                 # already from Step 3
      value: /etc/tenx/license/license.jwt
    - name: TENX_CONFIG                       # ← add
      value: /etc/tenx/config
    # If the PVC also carries a symbol library:
    # - name: TENX_SYMBOLS_PATH
    #   value: /etc/tenx/symbols

  extraVolumes:
    - name: tenx-license                      # already from Step 3
      secret:
        secretName: receiver-credentials
        items:
          - key: license-jwt
            path: license.jwt
    - name: tenx-config-volume                # ← add
      persistentVolumeClaim:
        claimName: my-config-pvc

  extraVolumeMounts:
    - name: tenx-license                      # already from Step 3
      mountPath: /etc/tenx/license
      readOnly: true
    - name: tenx-config-volume                # ← add
      mountPath: /etc/tenx/config
      readOnly: true

The 10x process runs in the log10x sidecar from Step 3 (added via the chart's extraContainers:). The env + volumeMounts additions go inside that sidecar's spec; the pod-level volume goes under the chart's extraVolumes:; the init container (git path only) goes under the chart's top-level initContainers:.

Git Repository. Add the git-token key to your Step 5 Secret (--from-literal=git-token=YOUR-GIT-TOKEN), then append:

my-receiver.yaml (additions)
extraContainers:
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/git/config
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-git                        # ← add
        mountPath: /etc/tenx/git
        readOnly: true

extraVolumes:
  - name: tenx-license                        # already from Step 3
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt
  - name: tenx-git                            # ← add
    emptyDir: {}

initContainers:                               # ← add
  - name: tenx-git-config
    image: log10x/git-config-fetcher:1.0.0
    env:
      - name: GIT_TOKEN
        valueFrom:
          secretKeyRef:
            name: receiver-credentials
            key: git-token
    args:
      - "--config-repo"
      - "https://github.com/YOUR-ACCOUNT/config.git"
      - "--config-branch"                     # Optional
      - "my-receiver-config"
    volumeMounts:
      - name: tenx-git
        mountPath: /data

Persistent Volume:

my-receiver.yaml (additions)
extraContainers:
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/config
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-config-volume              # ← add
        mountPath: /etc/tenx/config
        readOnly: true

extraVolumes:
  - name: tenx-license                        # already from Step 3
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt
  - name: tenx-config-volume                  # ← add
    persistentVolumeClaim:
      claimName: my-config-pvc

The 10x process runs in the log10x sidecar from Step 3 (added via the chart's extraContainers:). The env + volumeMounts additions go inside that sidecar's spec; the pod-level volume goes under the chart's extraVolumes:; the init container (git path only) goes under the chart's top-level initContainers:.

Git Repository. Add the git-token key to your Step 5 Secret (--from-literal=git-token=YOUR-GIT-TOKEN), then append:

my-receiver.yaml (additions)
extraContainers:
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/git/config
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-git                        # ← add
        mountPath: /etc/tenx/git
        readOnly: true

extraVolumes:
  - name: tenx-license                        # already from Step 3
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt
  - name: tenx-git                            # ← add
    emptyDir: {}

initContainers:                               # ← add
  - name: tenx-git-config
    image: log10x/git-config-fetcher:1.0.0
    env:
      - name: GIT_TOKEN
        valueFrom:
          secretKeyRef:
            name: receiver-credentials
            key: git-token
    args:
      - "--config-repo"
      - "https://github.com/YOUR-ACCOUNT/config.git"
      - "--config-branch"                     # Optional
      - "my-receiver-config"
    volumeMounts:
      - name: tenx-git
        mountPath: /data

Persistent Volume:

my-receiver.yaml (additions)
extraContainers:
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/config
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-config-volume              # ← add
        mountPath: /etc/tenx/config
        readOnly: true

extraVolumes:
  - name: tenx-license                        # already from Step 3
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt
  - name: tenx-config-volume                  # ← add
    persistentVolumeClaim:
      claimName: my-config-pvc

The 10x process runs in the log10x sidecar from Step 3 (added via the chart's extraContainers: |, string form, tpl'd by the chart). The env + volumeMounts additions go inside that sidecar's spec; the pod-level volume goes under the chart's extraVolumes:; the init container (git path only) goes under the chart's extraInitContainers: hook.

Git Repository. Add the git-token key to your Step 5 Secret (--from-literal=git-token=YOUR-GIT-TOKEN), then append:

my-receiver.yaml (additions)
extraContainers: |
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/git/config
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-git                        # ← add
        mountPath: /etc/tenx/git
        readOnly: true

# `secretMounts:` from Step 3 still mounts the license. `extraVolumes:`
# is a separate hook in this chart and is where the new emptyDir goes.
extraVolumes:                                 # ← add
  - name: tenx-git
    emptyDir: {}

# Note the elastic-chart naming: `extraInitContainers:`, not
# `initContainers:`.
extraInitContainers: |                        # ← add
  - name: tenx-git-config
    image: log10x/git-config-fetcher:1.0.0
    env:
      - name: GIT_TOKEN
        valueFrom:
          secretKeyRef:
            name: receiver-credentials
            key: git-token
    args:
      - "--config-repo"
      - "https://github.com/YOUR-ACCOUNT/config.git"
      - "--config-branch"                     # Optional
      - "my-receiver-config"
    volumeMounts:
      - name: tenx-git
        mountPath: /data

Persistent Volume:

my-receiver.yaml (additions)
extraContainers: |
  - name: log10x
    # ... rest of the spec unchanged from Step 3 ...
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
      - name: TENX_CONFIG                     # ← add
        value: /etc/tenx/config
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true
      - name: tenx-config-volume              # ← add
        mountPath: /etc/tenx/config
        readOnly: true

extraVolumes:                                 # ← add
  - name: tenx-config-volume
    persistentVolumeClaim:
      claimName: my-config-pvc

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based.

For Kubernetes, use the Fluent Bit or OTel Collector tab.

Step 5: Configure Secrets

Store sensitive credentials in Kubernetes Secrets. Only add secrets for metric outputs you've configured.

Create the secret:

kubectl create secret generic receiver-credentials \
  --from-file=license-jwt=./license.jwt \
  --from-literal=git-token=YOUR_GIT_TOKEN \
  --from-literal=elasticsearch-username=elastic \
  --from-literal=elasticsearch-password=YOUR_ES_PASSWORD \
  --from-literal=datadog-api-key=YOUR_DATADOG_API_KEY

Note: Only include credentials for outputs you've configured. The license-jwt key is required; the sidecar mounts it as a file and points TENX_LICENSE_FILE at it. The git-token key is only required if you picked the Git Repository path in Step 4; the git-config-fetcher init container reads it via a secretKeyRef.

Add secret references to your my-receiver.yaml:

Fluentd uses the shared receiver-credentials secret (for the 10x license and any metric-output credentials). The Log10x sidecar image is on public Docker Hub, so no image-pull secret is needed.

a. The 10x license is mounted as a file by the sidecar via the volumes entry in sidecar-patch.yaml (Step 3). It reads the license-jwt key from receiver-credentials and the sidecar reads TENX_LICENSE_FILE; no change needed in my-receiver.yaml.

b. Forwarder-side metric output credentials (Datadog, Elasticsearch, etc.) ride on the chart's env: block:

my-receiver.yaml
env:
  # For Datadog metrics
  - name: DD_API_KEY
    valueFrom:
      secretKeyRef:
        name: receiver-credentials
        key: datadog-api-key

  # For Elasticsearch metrics
  # - name: ELASTIC_API_KEY
  #   valueFrom:
  #     secretKeyRef:
  #       name: receiver-credentials
  #       key: elastic-api-key

  # For AWS CloudWatch metrics
  # - name: AWS_ACCESS_KEY_ID
  #   valueFrom:
  #     secretKeyRef:
  #       name: receiver-credentials
  #       key: aws-access-key-id

  # For SignalFx metrics
  # - name: SIGNALFX_ACCESS_TOKEN
  #   valueFrom:
  #     secretKeyRef:
  #       name: receiver-credentials
  #       key: signalfx-access-token

The log10x/edge-10x image is on public DockerHub, so Fluent Bit needs no image-pull secret.

a. The 10x license is mounted as a file by the sidecar from the receiver-credentials secret's license-jwt key (Step 3); no extra config here.

b. Destination-side credentials for outputs configured under config.outputs (Elasticsearch, Splunk HEC, Kafka SASL, ...) ride on the chart's env: list with ${VAR} interpolation inside the Fluent Bit config:

env:
  - name: ELASTIC_PASSWORD
    valueFrom:
      secretKeyRef:
        name: receiver-credentials
        key: elasticsearch-password
daemonset:
  extraEnvs:
    # For Elasticsearch output
    - name: ELASTICSEARCH_USERNAME
      valueFrom:
        secretKeyRef:
          name: receiver-credentials
          key: elasticsearch-username
    - name: ELASTICSEARCH_PASSWORD
      valueFrom:
        secretKeyRef:
          name: receiver-credentials
          key: elasticsearch-password

    # For Datadog metrics (optional)
    # - name: DD_API_KEY
    #   valueFrom:
    #     secretKeyRef:
    #       name: receiver-credentials
    #       key: datadog-api-key
extraEnvs:
  # For Datadog metrics
  - name: DD_API_KEY
    valueFrom:
      secretKeyRef:
        name: receiver-credentials
        key: datadog-api-key

The log10x/edge-10x image is on public DockerHub, so Vector needs no image-pull secret.

a. The 10x license is mounted as a file by the sidecar from the receiver-credentials secret's license-jwt key (Step 3); no extra config here.

b. Destination-side credentials (Elasticsearch, Splunk HEC, Kafka SASL, ...) ride on Vector's own sink config in the customConfig block, typically via env-vars resolved with ${VAR} interpolation. Wire them through the chart's env: list:

env:
  - name: ELASTIC_PASSWORD
    valueFrom:
      secretKeyRef:
        name: receiver-credentials
        key: elasticsearch-password

The log10x/edge-10x image is on public DockerHub, so Logstash needs no image-pull secret.

a. The 10x license is mounted as a file by the sidecar from the receiver-credentials secret's license-jwt key (Step 3); no extra config here.

b. Destination-side credentials (Elasticsearch, Splunk HEC, Kafka SASL, ...) ride on the chart's extraEnvs: list. Reference them in your tenx-destinations.conf output blocks via ${VAR} interpolation:

extraEnvs:
  - name: ELASTICSEARCH_PASSWORD
    valueFrom:
      secretKeyRef:
        name: receiver-credentials
        key: elasticsearch-password

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based. For VM infrastructure, see the Splunk UF receiver guide.

For Kubernetes, use the Fluent Bit or OTel Collector tab. For VM infrastructure, see the Datadog Agent receiver guide.

Step 6: Forwarder

Configure your forwarder for log collection and output destinations. The 10x receiver filters events before they reach your final destination.

Replace the placeholder stdout <match> under @OUTPUT (in 04_outputs.conf from Step 3) with your real destination(s). The @OUTPUT label runs only on events returning from the 10x sidecar, keep it filter-free so the enrichment filters in @INGEST aren't re-applied on the return path.

my-receiver.yaml (updated 04_outputs.conf entry)
fileConfigs:
  04_outputs.conf: |-
    <label @OUTPUT>
      <match **>
        @type elasticsearch
        host elasticsearch-master
        port 9200
        logstash_format true
        logstash_prefix logs-filtered
      </match>
    </label>

The original Fluentd tag survives the round trip through 10x, so destinations that route on $TAG (Splunk index, S3 path, Kafka topic, …) see the same value they would have without 10x in the path.

Fluent Bit's forwarder config lives in the config: block of my-receiver.yaml from Step 3. Keep the egress [INPUT] forward Tag_Prefix tenx. and the ingest [OUTPUT] forward Match kube.* as-is; replace the placeholder [OUTPUT] stdout block (Match tenx.*) with your real destination(s). The destination MUST Match tenx.* (or whatever tag namespace you chose on egress); anything matching kube.* would receive events twice (once on the way in, once on the way back) or loop them back to 10x.

config:
  outputs: |
    # Hand off to the 10x sidecar (unchanged).
    [OUTPUT]
        Name forward
        Match kube.*
        Host 127.0.0.1
        Port 24224
        Retry_Limit False

    # Replace the stdout placeholder with your real destination.
    # Note: Match tenx.* (not *), otherwise events loop.
    [OUTPUT]
        Name es
        Match tenx.*
        Host elasticsearch-master
        Logstash_Format On

The original Fluent Bit tag is preserved as the suffix of the returning wire tag (tenx.kube.foo), so destinations that route on the suffix behave the same as without 10x in the path.

Filebeat's forwarder config lives in the daemonset.filebeatConfig.filebeat.yml string from Step 3. Replace the placeholder output.elasticsearch: block at the bottom of that string with your real destination; keep the filebeat.config.inputs: and the script processor on each input intact, those are what wire the integration. output.console is not supported (it collides with the stdout pipe carrying events to the engine); any other output (elasticsearch, logstash, kafka, file, …) is fine.

# Replace the output block in Step 3's filebeat.yml with your real
# destination. Example for an Elasticsearch destination using the
# credentials wired up via extraEnvs in Step 5:
daemonset:
  filebeatConfig:
    filebeat.yml: |
      # ... filebeat.config.inputs and filebeat.inputs from Step 3 ...

      output.elasticsearch:
        hosts: '["https://elasticsearch-master:9200"]'
        username: '${ELASTICSEARCH_USERNAME}'
        password: '${ELASTICSEARCH_PASSWORD}'
        ssl.certificate_authorities: ["/usr/share/filebeat/certs/ca.crt"]
        indices:
        - index: "logs-filtered-%{+yyyy.MM.dd}"

The original Filebeat record structure is preserved end-to-end, so destinations that route on metadata fields (kubernetes.namespace, host.name, …) behave the same as without 10x in the path.

The Collector forwarder config lives in the config: block of my-receiver.yaml from Step 3. Keep your existing receivers; rewire the logs: pipeline through the otlp/tenx exporter and route the new logs/from-tenx pipeline to your real destinations:

config:
  receivers:
    # Replace with your real Collector receivers
    filelog:
      include: [/var/log/pods/*/*/*.log]
      operators:
        - type: container
          id: container-parser

  exporters:
    # Your real destination(s), consumed by `logs/from-tenx`
    elasticsearch:
      endpoints: ["https://elasticsearch-master:9200"]
      logs_index: logs-filtered

  service:
    pipelines:
      logs:
        receivers: [filelog]
        processors: [memory_limiter, batch]
        exporters: [otlp/tenx]
      logs/from-tenx:
        receivers: [otlp/tenx]
        exporters: [elasticsearch]

Vector's forwarder config lives in the customConfig block of my-receiver.yaml from Step 3. The overlay already wires the tenx_in socket sink and the tenx_out fluent source; keep customConfig.sources pointing at your real log inputs and replace the destinations sink with your real destination(s), consuming inputs: [tenx_out] so events arrive only after the 10x sidecar has processed them. Don't wire destinations to the raw sources or to ingest; that bypasses the round trip and skips the bypass guarantee.

Logstash's forwarder config lives in the logstashPipeline block of my-receiver.yaml from Step 3. Keep the tenx-ingest.conf ending in tcp { host => "127.0.0.1" port => 5044 codec => json_lines } (handoff to 10x) as-is, and replace the placeholder stdout output in tenx-destinations.conf with your real destination(s). The destinations pipeline runs only on events returning from the 10x sidecar, keep it filter-free so the enrichment in tenx-ingest.conf isn't re-applied on the return path.

my-receiver.yaml (updated tenx-destinations.conf entry)
logstashPipeline:
  tenx-destinations.conf: |
    input {
      tcp {
        host  => "0.0.0.0"
        port  => 5045
        codec => json_lines
      }
    }
    output {
      elasticsearch {
        hosts => ["elasticsearch-master:9200"]
        index => "logs-filtered-%{+YYYY.MM.dd}"
      }
    }

The tag field added by the ingest pipeline survives the round trip, so destinations that route on it (Splunk index, S3 prefix, Kafka topic, …) see the same value they would have without 10x in the path.

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based. For VM infrastructure, see the Splunk UF receiver guide.

For Kubernetes, use the Fluent Bit or OTel Collector tab. For VM infrastructure, see the Datadog Agent receiver guide.

Step 7: Deploy

Create your namespace (if needed) and deploy:

kubectl create namespace logging

Helm installs the upstream chart; the post-renderer applies the kustomize overlay (from Step 3) on the way to the cluster, injecting the 10x sidecar.

Linux / macOS
helm install my-receiver fluent/fluentd \
  -f my-receiver.yaml \
  --post-renderer ./tenx-kustomize/post-render.sh \
  --namespace logging
Windows (PowerShell / cmd)
helm install my-receiver fluent/fluentd ^
  -f my-receiver.yaml ^
  --post-renderer .\tenx-kustomize\post-render.cmd ^
  --namespace logging

helm get manifest my-receiver -n logging returns the patched form (with the sidecar added); Argo/Flux diff against this final state, no drift.

Layer the Log10x overlay (my-receiver.yaml from Step 3) on top of any existing Fluent Bit values:

helm upgrade --install my-receiver fluent/fluent-bit \
  -f my-receiver.yaml \
  --namespace logging

Fluent Bit uses the upstream chart with a values overlay. The overlay drops the 10x sidecar into the same pod via extraContainers and rewrites the chart's config via config:. Both containers share the pod network and pair over loopback TCP on :24224 (Fluent Forward in) / :24225 (Fluent Forward back).

Layer the Log10x overlay (my-receiver.yaml from Step 3) on top of any existing Filebeat values:

helm upgrade --install my-receiver elastic/filebeat \
  -f my-receiver.yaml \
  --namespace logging

Filebeat uses the upstream elastic/filebeat chart with a values overlay. The overlay swaps the chart's default Filebeat image for log10x/filebeat-10x, which runs Filebeat under a filebeat ... 2>&1 | tenx run ... pipe inside the container; Filebeat and the 10x engine share a process tree inside the single container.

Layer the Log10x overlay (my-receiver.yaml from Step 3) on top of any existing Collector values:

helm upgrade --install my-receiver open-telemetry/opentelemetry-collector \
  -f my-receiver.yaml \
  --namespace logging

The Collector uses the upstream chart with a values overlay. The overlay drops the 10x sidecar into the same pod via extraContainers and adds the sidecar receiver/exporter to the chart's config:. Both containers share the pod network and pair over loopback TCP on :4317 (OTLP in) / :24225 (Fluent Forward back).

Layer the Log10x overlay (my-receiver.yaml from Step 3) on top of your existing Vector values:

helm upgrade --install my-receiver vector/vector \
  -f your-existing-vector-values.yaml \
  -f my-receiver.yaml \
  --namespace logging

Vector uses the upstream chart with a values overlay. The overlay drops the 10x sidecar container into the same pod via extraContainers and rewrites the chart's config via customConfig. Both containers share the pod network and pair over loopback TCP on :9000 (NDJSON in) / :9001 (Fluent Forward back).

Layer the Log10x overlay (my-receiver.yaml from Step 3) on top of any existing Logstash values:

helm upgrade --install my-receiver elastic/logstash \
  -f my-receiver.yaml \
  --namespace logging

Logstash uses the upstream elastic/logstash chart with a values overlay. The overlay drops the 10x sidecar container into the same pod via extraContainers and provides the multi-pipeline config via logstashConfig + logstashPipeline. Both containers share the pod network and pair over loopback TCP on :5044 (NDJSON in) / :5045 (NDJSON back).

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based. For VM infrastructure, see the Splunk UF receiver guide.

For Kubernetes, use the Fluent Bit or OTel Collector tab. For VM infrastructure, see the Datadog Agent receiver guide.

Step 8: Verify

Verify the install in three phases: pods Ready → 10x processor alive → processed events flowing. A probe passes when its commands exit 0.

Phase A: pods Ready

Pick your forwarder for the right kubectl label selector:

kubectl -n logging wait --for=condition=Ready pod -l app.kubernetes.io/instance=my-receiver --timeout=5m
kubectl -n logging wait --for=condition=Ready pod -l app=my-receiver-filebeat,release=my-receiver --timeout=5m
kubectl -n logging wait --for=condition=Ready pod -l app=my-receiver-logstash,release=my-receiver --timeout=10m

Phase B: 10x receiver plugin alive

Look for receiver initialization lines on the container that runs the 10x process. Pick your forwarder:

Fluentd's sidecar pattern runs 10x in its own container alongside fluentd in the same pod; check the dedicated container's startup banner (the 📥 / 📝 lines indicate the receiver is wired):

kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c log10x --tail=200 | grep -E '🪵|🚀|📥|🚦|📈|📝'

Fluent Bit's sidecar pattern runs 10x in its own container (log10x) alongside fluent-bit in the same pod; check the dedicated container's startup banner (the 📥 / 📝 lines indicate the receiver is wired):

kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c log10x --tail=200 | grep -E '🪵|🚀|📥|🚦|📈|📝'

The Filebeat container's stdout carries the engine's startup banner; the 📥 line confirms it is reading events off the Filebeat stdout pipe, the 📝 line confirms the unix-socket emitter back to Filebeat is wired:

kubectl -n logging logs -l app=my-receiver-filebeat,release=my-receiver -c filebeat --tail=200 | grep -E '🪵|🚀|📥|🚦|📈|📝'

Logstash's sidecar pattern runs 10x in its own container (log10x) alongside logstash in the same pod; check the dedicated container's startup banner (the 📥 / 📝 lines indicate the receiver is wired):

kubectl -n logging logs -l app=my-receiver-logstash,release=my-receiver -c log10x --tail=200 | grep -E '🪵|🚀|📥|🚦|📈|📝'
kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c opentelemetry-collector --tail=200 | grep -iE 'tenx|10x|sample|compact|mute'

For Vector, 10x runs as a separate sidecar container (log10x) alongside the vector container; check its logs directly. The 📥 line confirms the Vector socket input is listening; the 📝 line confirms the Forward output is wired back to Vector's fluent source.

kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c log10x --tail=200 | grep -E '🪵|🚀|📥|🚦|📈|📝'

Phase C: processed events flowing

Confirm the forwarder is actually writing processed events to its destination. For a real destination, check the destination's UI (Elasticsearch index, Splunk sourcetype, Datadog logs view). For mock/stdout output, grep for the TENX-MOCK marker:

kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver --tail=200 | grep -F TENX-MOCK

View mute/sample counts in the dashboard:

Once running, view your mute/sample activity in the Receiver Dashboard.

Step 9: Teardown

Uninstall the Helm release:

helm -n logging uninstall my-receiver

Clean up derived resources. Pick your forwarder for the right label selector:

kubectl -n logging delete pvc -l app.kubernetes.io/instance=my-receiver --ignore-not-found
kubectl -n logging delete secret receiver-credentials --ignore-not-found
kubectl -n logging delete pvc -l release=my-receiver --ignore-not-found
kubectl -n logging delete secret receiver-credentials --ignore-not-found

Verify nothing remains:

kubectl -n logging get all,configmap,secret,pvc -l app.kubernetes.io/instance=my-receiver
kubectl -n logging get all,configmap,secret,pvc -l release=my-receiver

Delete the namespace (optional):

kubectl delete namespace logging --ignore-not-found
Quickstart Full Sample

Fluentd's full deploy is a values file + a four-file kustomize overlay. The values file replaces the chart's default config with the @INGEST / @OUTPUT pattern; the kustomize overlay injects the 10x sidecar via Helm's --post-renderer. Layout:

./
├── my-receiver.yaml          (values, see below)
└── tenx-kustomize/
    ├── kustomization.yaml
    ├── sidecar-patch.yaml
    ├── post-render.sh
    └── post-render.cmd       (Windows shim)

The four kustomize files are listed verbatim in Step 3: Configure Deployment Settings. Re-use them as-is; only my-receiver.yaml typically changes per deployment.

my-receiver.yaml
kind: Deployment            # or DaemonSet for host-log tailing
replicaCount: 1

image:
  repository: fluent/fluentd
  tag: v1.18-debian-1

mountVarLogDirectory: false
mountDockerContainersDirectory: false

rbac:
  create: false
serviceAccount:
  create: true
service:
  enabled: false
podSecurityPolicy:
  enabled: false

# Secrets (license JWT + metric-output credentials) live in the
# `receiver-credentials` Secret from Step 5.

fileConfigs:

  01_sources.conf: |-
    # Replace this dummy source with your real sources (tail, http,
    # k8s, …). Every source MUST route to @INGEST.
    <source>
      @type dummy
      tag k8s.demo
      rate 1
      dummy {"log":"hello from fluentd dummy plugin","level":"info"}
      auto_increment_key seq
      @label @INGEST
    </source>

  02_filters.conf: |-
    <label @INGEST>
      <filter **>
        @type record_transformer
        <record>
          cluster "${ENV['CLUSTER_NAME'] || 'unset'}"
        </record>
      </filter>
      <match **>
        @type forward
        keepalive true
        keepalive_timeout 60s
        send_timeout 5s
        require_ack_response false
        <server>
          host 127.0.0.1
          port 24224
        </server>
        <buffer>
          @type memory
          flush_interval 1s
        </buffer>
      </match>
    </label>

  03_dispatch.conf: |-
    <source>
      @type forward
      bind 0.0.0.0
      port 24225
      @label @OUTPUT
    </source>

  04_outputs.conf: |-
    <label @OUTPUT>
      <match **>
        @type elasticsearch
        host elasticsearch-master
        port 9200
        logstash_format true
        logstash_prefix logs-filtered
      </match>
    </label>

Install:

helm install my-receiver fluent/fluentd \
  -f my-receiver.yaml \
  --post-renderer ./tenx-kustomize/post-render.sh \
  --namespace logging

To set the engine-wide default action (pass / pass+hash / compact / compact+hash), edit the args list in tenx-kustomize/sidecar-patch.yaml; see the inline comments in that file (Step 3). Per-pattern actions come from the agent's configure_engine cap/action CSV through GitOps.

The Fluent Bit quickstart is a values overlay applied to your existing Fluent Bit chart install. The 10x sidecar and Fluent Bit share the pod network and talk over loopback TCP, with Tag_Prefix tenx. on the egress [INPUT] forward providing the bypass that keeps returning events from re-firing the ingest [OUTPUT] forward and the filters.

Save as my-receiver.yaml and apply with helm upgrade --install my-receiver fluent/fluent-bit -f your-existing-fluent-bit-values.yaml -f my-receiver.yaml.

my-receiver.yaml
extraContainers:
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/fluentbit"
      - "@apps/receiver"
      # Default action: pass. Append `receiverOptimize true` to
      # default to compact, and/or `symbolMessageHashField <name>`
      # for the symbol-pattern hash field. Per-pattern actions come
      # from the agent's `configure_engine` CSV through GitOps.
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true

extraVolumes:
  - name: tenx-license
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt

config:
  service: |
    [SERVICE]
        Daemon Off
        Flush {{ .Values.flush }}
        Log_Level {{ .Values.logLevel }}
        Parsers_File /fluent-bit/etc/parsers.conf
        Parsers_File /fluent-bit/etc/conf/custom_parsers.conf
        HTTP_Server On
        HTTP_Listen 0.0.0.0
        HTTP_Port {{ .Values.metricsPort }}
        Health_Check On

  inputs: |
    # Your real source: tag with anything NOT starting with `tenx.`.
    [INPUT]
        Name tail
        Path /var/log/containers/*.log
        multiline.parser docker, cri
        Tag kube.*
        Mem_Buf_Limit 5MB
        Skip_Long_Lines On

    # Egress: receive processed events back from log10x with the
    # `tenx.` prefix as the bypass.
    [INPUT]
        Name forward
        Listen 0.0.0.0
        Port 24225
        Tag_Prefix tenx.

  filters: |
    [FILTER]
        Name kubernetes
        Match kube.*
        Merge_Log On
        Keep_Log Off

  outputs: |
    # Ingest: ship to log10x.
    [OUTPUT]
        Name forward
        Match kube.*
        Host 127.0.0.1
        Port 24224
        Retry_Limit False

    # Destinations match the `tenx.*` namespace (replace stdout).
    [OUTPUT]
        Name es
        Match tenx.*
        Host elasticsearch-master
        Logstash_Format On

To set the engine-wide default action (pass / pass+hash / compact / compact+hash), edit the args list under extraContainers[0] and helm upgrade --install; see the inline comments. Per-pattern actions come from the agent's configure_engine cap/action CSV through GitOps.

The Filebeat quickstart is a values overlay applied to the upstream elastic/filebeat chart. The 10x engine runs as a child process of Filebeat's entrypoint inside the prebuilt log10x/filebeat-10x image, which wraps filebeat -e 2>&1 | tenx run @run/input/forwarder/filebeat @apps/receiver. The overlay swaps the chart's default image and adds the script processor + return-path unix input to the chart's filebeat.yml.

Save as my-receiver.yaml and apply with helm upgrade --install my-receiver elastic/filebeat -f my-receiver.yaml.

my-receiver.yaml
# Image swap: the log10x-built Filebeat image runs the engine as a
# child of Filebeat's entrypoint.
image: "log10x/filebeat-10x"
imageTag: "latest"          # pin to a release tag in production
imagePullPolicy: IfNotPresent

daemonset:
  extraEnvs:
    # Point the engine at the mounted license file (Step 5).
    - name: TENX_LICENSE_FILE
      value: /etc/tenx/license/license.jwt

    # Destination credentials for the output.elasticsearch block.
    - name: ELASTICSEARCH_USERNAME
      valueFrom:
        secretKeyRef:
          name: receiver-credentials
          key: elasticsearch-username
    - name: ELASTICSEARCH_PASSWORD
      valueFrom:
        secretKeyRef:
          name: receiver-credentials
          key: elasticsearch-password

    # ─── Actions ────────────────────────────────────────────────
    # Per-pattern action (pass, sample, compact, tier_down,
    # offload, drop) is set by the agent via the log10x MCP
    # `configure_engine` cap/action CSV through GitOps. TENX_RUN_ARGS
    # sets the engine-wide default before any CSV.
    #
    # Default (no override): pass: events go through unchanged.
    #
    # Override TENX_RUN_ARGS to change the default action:
    # - Append `receiverOptimize true` to default to compact.
    # - Append `symbolMessageHashField <name>` to surface the
    #   symbol-pattern hash as a top-level field.
    #
    # - name: TENX_RUN_ARGS
    #   value: "@run/input/forwarder/filebeat @apps/receiver receiverOptimize true"

  extraVolumes:
    - name: tenx-license
      secret:
        secretName: receiver-credentials
        items:
          - key: license-jwt
            path: license.jwt

  extraVolumeMounts:
    - name: tenx-license
      mountPath: /etc/tenx/license
      readOnly: true

  # `filebeat.config.inputs` loads the unix-socket input that
  # receives processed events back from the engine. Every entry in
  # `filebeat.inputs` must carry the tenx-receive.js script
  # processor; without it the event never reaches the engine.
  # `output.console` must NOT be used: it shares the stdout pipe
  # with the engine and corrupts the event stream.
  filebeatConfig:
    filebeat.yml: |
      filebeat.config.inputs:
        enabled: true
        path: ${TENX_MODULES}/pipelines/run/modules/input/forwarder/filebeat/conf/tenxNix.yml

      filebeat.inputs:
      - type: container
        paths:
          - /var/log/containers/*.log
        processors:
        - add_kubernetes_metadata:
            host: ${NODE_NAME}
            matchers:
            - logs_path:
                logs_path: "/var/log/containers/"
        - script:
            lang: javascript
            file: ${TENX_MODULES}/pipelines/run/modules/input/forwarder/filebeat/script/tenx-receive.js

      output.elasticsearch:
        hosts: '["https://elasticsearch-master:9200"]'
        username: '${ELASTICSEARCH_USERNAME}'
        password: '${ELASTICSEARCH_PASSWORD}'
        indices:
        - index: "logs-filtered-%{+yyyy.MM.dd}"

To set the engine-wide default action (pass / pass+hash / compact / compact+hash), uncomment the TENX_RUN_ARGS entry under daemonset.extraEnvs; see the inline comments. Per-pattern actions come from the agent's configure_engine cap/action CSV through GitOps.

The Collector quickstart is a values overlay applied to your existing Collector chart install. The 10x sidecar and the Collector share the pod network and talk over loopback TCP.

Save as my-receiver.yaml and apply with helm upgrade --install my-receiver open-telemetry/opentelemetry-collector -f your-existing-otel-values.yaml -f my-receiver.yaml.

my-receiver.yaml
extraContainers:
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/otel-collector"
      - "@apps/receiver"
      # Default action: pass. Append `receiverOptimize true` to
      # default to compact, and/or `symbolMessageHashField <name>`
      # for the symbol-pattern hash field. Per-pattern actions come
      # from the agent's `configure_engine` CSV through GitOps.
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true

extraVolumes:
  - name: tenx-license
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt

config:
  receivers:
    # Replace with your real receivers
    filelog:
      include: [/var/log/pods/*/*/*.log]

    # OTLP/gRPC receiver for processed events coming back from log10x
    otlp/tenx:
      protocols:
        grpc:
          endpoint: 0.0.0.0:24225

  exporters:
    otlp/tenx:
      endpoint: 127.0.0.1:4317
      tls:
        insecure: true

    elasticsearch:
      endpoints: ["https://elasticsearch-master:9200"]
      logs_index: logs-filtered

  service:
    pipelines:
      logs:
        receivers: [filelog]
        processors: [memory_limiter, batch]
        exporters: [otlp/tenx]
      logs/from-tenx:
        receivers: [otlp/tenx]
        exporters: [elasticsearch]

Vector's quickstart is a values overlay applied to your existing Vector chart install. The 10x sidecar and Vector share the pod network and talk over loopback TCP.

Save as my-receiver.yaml and apply with helm upgrade --install my-receiver vector/vector -f your-existing-vector-values.yaml -f my-receiver.yaml.

my-receiver.yaml
extraContainers:
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/vector"
      - "@apps/receiver"
      # Default action: pass. Append `receiverOptimize true` to
      # default to compact, and/or `symbolMessageHashField <name>`
      # for the symbol-pattern hash field. Per-pattern actions come
      # from the agent's `configure_engine` CSV through GitOps.
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true

extraVolumes:
  - name: tenx-license
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt

# The vector chart auto-publishes ports from each `customConfig.sources`
# entry, so 9001 is already declared on the `vector` container.
# The sidecar connects out on 127.0.0.1, no port of its own needed.

customConfig:
  data_dir: /vector-data-dir

  sources:
    # Replace with your real sources
    app_logs:
      type: kubernetes_logs

    # Receive processed events back from the 10x sidecar (bind on
    # 0.0.0.0 so kubelet's tcpSocket probe can reach the port from
    # the pod IP).
    tenx_out:
      type: fluent
      mode: tcp
      address: 0.0.0.0:9001

  transforms:
    ingest:
      type: remap
      inputs: [app_logs]
      source: |
        .cluster = get_env_var("CLUSTER_NAME") ?? "unset"
        .tag = .source_type

  sinks:
    tenx_in:
      type: socket
      inputs: [ingest]
      mode: tcp
      address: 127.0.0.1:9000
      encoding: { codec: json }
      framing: { method: newline_delimited }

    # Replace `console` with your real destination: consume
    # `tenx_out` so events arrive only after the round trip.
    destinations:
      type: console
      inputs: [tenx_out]
      encoding: { codec: json }

See the Vector forwarder guide for the architecture, the four-mode contract, and the matching local-install walkthrough.

Logstash's quickstart is a values overlay applied to the upstream elastic/logstash chart. The 10x sidecar and Logstash share the pod network and talk over loopback TCP.

Save as my-receiver.yaml and apply with helm upgrade --install my-receiver elastic/logstash -f my-receiver.yaml.

my-receiver.yaml
extraContainers: |
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/logstash"
      - "@apps/receiver"
      # Default action: pass. Append `receiverOptimize true` to
      # default to compact, and/or `symbolMessageHashField <name>`
      # for the symbol-pattern hash field. Per-pattern actions come
      # from the agent's `configure_engine` CSV through GitOps.
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true

secretMounts:
  - name: tenx-license
    secretName: receiver-credentials
    path: /etc/tenx/license/license.jwt
    subPath: license-jwt

logstashConfig:
  pipelines.yml: |
    - pipeline.id: ingest
      path.config: "/usr/share/logstash/pipeline/tenx-ingest.conf"
    - pipeline.id: destinations
      path.config: "/usr/share/logstash/pipeline/tenx-destinations.conf"

logstashPipeline:
  tenx-ingest.conf: |
    input {
      # Replace with your real sources
      file {
        path  => "/var/log/containers/*.log"
        codec => "json"
        start_position => "beginning"
      }
    }
    filter {
      mutate {
        add_field => { "cluster" => "${CLUSTER_NAME:unset}" }
        add_field => { "tag" => "k8s.containers" }
      }
    }
    output {
      # Hand off to the 10x sidecar
      tcp {
        host  => "127.0.0.1"
        port  => 5044
        codec => json_lines
      }
    }
  tenx-destinations.conf: |
    input {
      # Bind on 0.0.0.0 so the port is reachable from outside
      # loopback (kubelet probe). The sidecar connects via
      # 127.0.0.1 from inside the same pod.
      tcp {
        host  => "0.0.0.0"
        port  => 5045
        codec => json_lines
      }
    }
    output {
      # Replace `stdout` with your real destination: events arrive
      # here only after the 10x sidecar has processed them.
      stdout { codec => json_lines }
    }

See the Logstash forwarder guide for the architecture, the four-mode contract, and the matching local-install walkthrough.

For Kubernetes, use the Fluent Bit tab. Splunk Connect for Kubernetes is Fluent Bit-based. For VM infrastructure, see the Splunk UF receiver guide.

For Kubernetes, use the Fluent Bit or OTel Collector tab. For VM infrastructure, see the Datadog Agent receiver guide.

Datadog Output Examples

To send filtered events to Datadog, use the file relay pattern: the forwarder writes processed events to a folder that the Datadog Agent monitors. This keeps the Datadog Agent as the shipper (handling buffering, retries, metadata enrichment) while 10x acts on events inline. Pick your forwarder below for the exact wiring.

Replace the destination [OUTPUT] (the one with Match tenx.*) in my-receiver.yaml from Step 3 with a file output. The rest of the values overlay (the extraContainers sidecar, the ingest [OUTPUT] forward, and the egress [INPUT] forward Tag_Prefix tenx.) stays the same; Fluent Bit writes only the processed events (those returning from log10x with the tenx. prefix) to the shared folder.

my-receiver.yaml (config.outputs entry)
config:
  outputs: |
    # Ingest: ship to log10x (unchanged).
    [OUTPUT]
        Name forward
        Match kube.*
        Host 127.0.0.1
        Port 24224
        Retry_Limit False

    # Destination: processed events only, Match tenx.* (NOT *).
    [OUTPUT]
        Name         file
        Match        tenx.*
        Path         /var/log/processed
        Format       plain

Then configure the Datadog Agent to monitor the processed output folder:

datadog-agent conf.d/processed.d/conf.yaml
logs:
  - type: file
    path: /var/log/processed/*.log
    service: myapp
    source: myapp

On EKS, mount a shared emptyDir volume between the Fluent Bit + 10x pod and the Datadog Agent DaemonSet at /var/log/processed.

File-relay pattern: the 10x sidecar wiring from Step 3 stays intact; only the egress pipeline's exporter changes, from your real destination (e.g. elasticsearch) to OTel's file exporter writing to a shared folder that the Datadog Agent monitors. This keeps the Datadog Agent as the forwarder (buffering, retries, metadata enrichment) while 10x acts on events inline.

Replace the exporters: list on the logs/from-tenx pipeline from Step 3; everything else (the otlp/tenx receiver + exporter, the logs ingest pipeline, the extraContainers sidecar) stays the same:

my-receiver.yaml (config.exporters and logs/from-tenx)
config:
  exporters:
    # Egress writer: only processed events (those coming back from
    # log10x via otlp/tenx) land here.
    file/tenx:
      path: /var/log/processed/otel.log

  service:
    pipelines:
      # Ingest pipeline unchanged from Step 3, still hands events
      # off to the log10x sidecar via otlp/tenx.
      logs:
        receivers: [filelog]                       # ← your existing receivers
        processors: [memory_limiter, batch]
        exporters: [otlp/tenx]

      # Egress pipeline: swap the destination from your real
      # exporter (e.g. elasticsearch) to file/tenx.
      logs/from-tenx:
        receivers: [otlp/tenx]
        exporters: [file/tenx]                     # ← was [elasticsearch]

Then configure the Datadog Agent to monitor the processed output folder:

datadog-agent conf.d/processed.d/conf.yaml
logs:
  - type: file
    path: /var/log/processed/*.log
    service: myapp
    source: myapp

On EKS, mount a shared emptyDir volume between the OTel Collector + 10x pod and the Datadog Agent DaemonSet at /var/log/processed.

Splunk HEC Output Examples

To send filtered events to Splunk instead of Elasticsearch, use Splunk HEC output.

Replace the @OUTPUT label's <match> in 04_outputs.conf with a splunk_hec destination. Everything else from the Quickstart Full Sample stays the same; the kustomize overlay is unchanged.

my-receiver.yaml (04_outputs.conf entry)
fileConfigs:
  04_outputs.conf: |-
    <label @OUTPUT>
      <match **>
        @type splunk_hec
        hec_host splunk-hec.example.com
        hec_port 8088
        hec_token "#{ENV['SPLUNK_HEC_TOKEN']}"
        index main
        source kubernetes
      </match>
    </label>

Add the HEC token to your receiver-credentials secret (--from-literal=splunk-hec-token=...) and reference it from the chart's env: block:

env:
  - name: SPLUNK_HEC_TOKEN
    valueFrom:
      secretKeyRef:
        name: receiver-credentials
        key: splunk-hec-token

Replace the destination [OUTPUT] (the one with Match tenx.*) in my-receiver.yaml from Step 3 with a splunk HEC output. Everything else (the sidecar extraContainers, the ingest [OUTPUT] forward, and the egress [INPUT] forward Tag_Prefix tenx.) stays the same.

my-receiver.yaml (config.outputs entry)
config:
  outputs: |
    # Ingest: ship to log10x (unchanged).
    [OUTPUT]
        Name forward
        Match kube.*
        Host 127.0.0.1
        Port 24224
        Retry_Limit False

    # Destination Match tenx.* (NOT *) so it doesn't loop events.
    [OUTPUT]
        Name splunk
        Match tenx.*
        Host splunk-hec.example.com
        Port 8088
        Splunk_Token ${SPLUNK_HEC_TOKEN}
        Splunk_Send_Raw On
        TLS On
        TLS.Verify Off

Add the HEC token to your receiver-credentials secret (--from-literal=splunk-hec-token=...) and reference it from the chart's env: block:

env:
  - name: SPLUNK_HEC_TOKEN
    valueFrom:
      secretKeyRef:
        name: receiver-credentials
        key: splunk-hec-token

Apply as an overlay on your existing Collector chart install: helm upgrade --install my-receiver open-telemetry/opentelemetry-collector -f your-existing-otel-values.yaml -f my-receiver.yaml.

my-receiver.yaml
extraContainers:
  - name: log10x
    image: log10x/edge-10x:latest
    imagePullPolicy: IfNotPresent
    args:
      - "@run/input/forwarder/otel-collector"
      - "@apps/receiver"
    env:
      - name: TENX_LICENSE_FILE
        value: /etc/tenx/license/license.jwt
    volumeMounts:
      - name: tenx-license
        mountPath: /etc/tenx/license
        readOnly: true

extraVolumes:
  - name: tenx-license
    secret:
      secretName: receiver-credentials
      items:
        - key: license-jwt
          path: license.jwt

config:
  receivers:
    filelog:
      include: [/var/log/pods/*/*/*.log]
    otlp/tenx:
      protocols:
        grpc:
          endpoint: 0.0.0.0:24225

  exporters:
    otlp/tenx:
      endpoint: 127.0.0.1:4317
      tls:
        insecure: true

    splunk_hec:
      endpoint: "https://splunk-hec.example.com:8088/services/collector"
      token: "YOUR-HEC-TOKEN"
      index: main
      tls:
        insecure_skip_verify: true

  service:
    pipelines:
      logs:
        receivers: [filelog]
        processors: [memory_limiter, batch]
        exporters: [otlp/tenx]
      logs/from-tenx:
        receivers: [otlp/tenx]
        exporters: [splunk_hec]
my-receiver.yaml (tenx-destinations.conf entry)
logstashPipeline:
  tenx-destinations.conf: |
    input {
      tcp {
        host  => "0.0.0.0"
        port  => 5045
        codec => json_lines
      }
    }
    output {
      http {
        url         => "https://splunk-hec.example.com:8088/services/collector"
        http_method => "post"
        headers     => ["Authorization", "Splunk YOUR-HEC-TOKEN"]
        format      => "json"
      }
    }