---
icon: material/kubernetes
---

Deploy the [Receiver](https://doc.log10x.com/apps/receiver/) app to Kubernetes via [Helm](https://helm.sh){target="\_blank"}.

The chart deploys your forwarder with the 10x Engine as a [sidecar process](https://doc.log10x.com/engine/launcher/sidecar). The workload kind (DaemonSet, Deployment, StatefulSet) and the exact integration shape are determined by the upstream chart for your forwarder. Pick yours below.

Manifests on this page name `log10x/edge-10x:1.1.69`. Substitute the version you want, and see [Image tags](https://doc.log10x.com/install/docker/#image-tags) for why a deployed manifest should never carry `:latest`.

The OTel Collector overlay moves the sidecar's OTLP listener with `TENX_OTEL_COLLECTOR_INPUT_PORT`, which needs an engine image whose shipped `run/input/forwarder/otel-collector/config.yaml` resolves that variable. On an older image the file pins `4317`, the variable is ignored, and the sidecar dies on `bind(..) failed with error(-98): Address already in use` against the Collector's own `otlp` receiver.

???+ tenx-bootstrap "Step 1: Prerequisites"

    | Requirement | Description |
    |-------------|-------------|
    | Log10x License | Your license key ([get one](https://doc.log10x.com/run/bootstrap/#apikey)) |
    | Helm | [Helm CLI](https://helm.sh/docs/intro/install/){target="\_blank"} installed |
    | kubectl | Configured to access your cluster |
    | GitHub Token | Personal access token for config repo ([create one](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens){target="\_blank"}) |
    | Output Destination | Elasticsearch, Splunk, or other log backend configured |

??? tenx-helm "Step 2: Add Helm Repository"

    === ":simple-fluentd: Fluentd"

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

        ``` { .console .copy }
        helm repo add fluent https://fluent.github.io/helm-charts
        helm repo update
        helm search repo fluent/fluentd
        ```

    === ":simple-fluentbit: Fluent-bit"

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

        ``` { .console .copy }
        helm repo add fluent https://fluent.github.io/helm-charts
        helm repo update
        helm search repo fluent/fluent-bit
        ```

    === ":simple-beats: Filebeat"

        Filebeat is the one forwarder where 10x ships its own chart, and that chart is the supported path.

        ``` { .console .copy }
        helm repo add log10x-elastic https://log-10x.github.io/elastic-helm-charts
        helm repo update
        helm search repo log10x-elastic/filebeat
        ```

        ```
        NAME                     CHART VERSION   APP VERSION   DESCRIPTION
        log10x-elastic/filebeat  1.5.0           1.1.39        A Helm chart for Kubernetes for Filebeat + 10x …
        ```

        `log10x-elastic/filebeat` is a fork of `elastic/filebeat` that carries the image swap, the licence wiring and the engine-aware probes as chart values, so an install is `tenx.enabled: true` plus a licence rather than a hand-maintained overlay. It tracks the engine: chart 1.5.0 has appVersion 1.1.39.

        !!! info "The upstream chart is the escape hatch, and it has a probe gap"

            `elastic/filebeat` still works as a values overlay, and existing installs do not have to move. Two things to know before choosing it.

            **Upstream is archived.** [elastic/helm-charts](https://github.com/elastic/helm-charts){target="\_blank"} was archived by Elastic with its last push on 2023-04-24, and the newest chart on `https://helm.elastic.co` is filebeat 8.5.1, published 2022-11-16. Nothing there will be fixed.

            **A frozen engine stays invisible.** The upstream chart's probes test Filebeat only (`curl 127.0.0.1:5066`). An engine that *exits* is caught either way, because the image entrypoint runs the pipe under `set -euo pipefail` and the container dies with it in about three seconds. An engine that *freezes* is the silent case: the pid stays in the process table, nothing breaks the pipe, and the pod holds Ready. Measured on 1.1.39, a `SIGSTOP`ped engine left the pod 1/1 Ready for 150 seconds while 5.2MB backed up in the pipe. `log10x-elastic/filebeat` replaces the probe command with one that also checks the engine is present, not stopped or a zombie, still reading Filebeat's stdout, and still accruing CPU; on the same test it failed the probe 22 seconds after the freeze and restarted the container at 29 seconds. Turn it off with `tenx.healthcheck.enabled: false`.

            The upstream overlay is documented in full from Step 3 onward.

        ``` { .console .copy }
        helm repo add elastic https://helm.elastic.co
        helm repo update
        helm search repo elastic/filebeat
        ```

    === ":simple-opentelemetry: OTel Collector"

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

        ``` { .console .copy }
        helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
        helm repo update
        helm search repo open-telemetry/opentelemetry-collector
        ```

    === ":simple-vector: Vector"

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

        ``` { .console .copy }
        helm repo add vector https://helm.vector.dev
        helm repo update
        helm search repo vector/vector
        ```

    === ":simple-logstash: Logstash"

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

        ``` { .console .copy }
        helm repo add elastic https://helm.elastic.co
        helm repo update
        helm search repo elastic/logstash
        ```

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based. For VM infrastructure, see the [Splunk UF receiver guide](https://doc.log10x.com/run/input/forwarder/splunkUF/).

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab. For VM infrastructure, see the [Datadog Agent receiver guide](https://doc.log10x.com/run/input/forwarder/datadogAgent/).

    **View all chart values:**

    === ":simple-fluentd: Fluentd"

        ``` { .console .copy }
        helm show values fluent/fluentd
        ```

    === ":simple-fluentbit: Fluent-bit"

        ``` { .console .copy }
        helm show values fluent/fluent-bit
        ```

    === ":simple-beats: Filebeat"

        ``` { .console .copy }
        # supported path
        helm show values log10x-elastic/filebeat

        # upstream, for the overlay path
        helm show values elastic/filebeat
        ```

    === ":simple-opentelemetry: OTel Collector"

        ``` { .console .copy }
        helm show values open-telemetry/opentelemetry-collector
        ```

    === ":simple-vector: Vector"

        ``` { .console .copy }
        helm show values vector/vector
        ```

    === ":simple-logstash: Logstash"

        ``` { .console .copy }
        helm show values elastic/logstash
        ```

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based. For VM infrastructure, see the [Splunk UF receiver guide](https://doc.log10x.com/run/input/forwarder/splunkUF/).

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab. For VM infrastructure, see the [Datadog Agent receiver guide](https://doc.log10x.com/run/input/forwarder/datadogAgent/).

??? tenx-config "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.

    === ":simple-fluentd: Fluentd"

        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:

        ``` { .yaml title="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 needed because the probes below are replaced with a tcpSocket
        # check; leaving the chart's defaults in place would require it.
        image:
          repository: fluent/fluentd
          tag: v1.18-debian-1

        # The chart's default liveness/readiness probes are an HTTP GET on
        # `/metrics` at port `metrics` (24231), served by the `@type
        # prometheus` source in the chart's own `01_sources.conf`. This
        # overlay replaces `01_sources.conf`, so that source is gone and
        # both probes would fail forever: the pod never reaches Ready and
        # the kubelet restarts `fluentd` every ~30s. Probe the `in_forward`
        # socket that receives events back from 10x instead.
        #
        # `httpGet: null` is required. Helm merges maps, so adding only
        # `tcpSocket` leaves both handlers on the probe and the API server
        # rejects the Deployment with "may not specify more than 1 handler
        # type".
        livenessProbe:
          httpGet: null
          tcpSocket:
            port: 24225
        readinessProbe:
          httpGet: null
          tcpSocket:
            port: 24225

        # 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.
              # `enable_ruby true` is required for the `ENV[...]` lookup
              # below. With `enable_ruby false` Fluentd logs "unknown
              # placeholder" once per event and the field arrives as null.
              <filter **>
                @type record_transformer
                enable_ruby true
                <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:

        ``` { .yaml title="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 }
        ```

        ``` { .yaml title="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:1.1.69
                  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
        ```

        ``` { .bash title="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"
        ```

        ``` { .bat title="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`:

        ``` { .yaml title="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.Airgapped mode is honored for every license type.

    === ":simple-fluentbit: Fluent-bit"

        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.

        ``` { .yaml title="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:1.1.69
            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`:

        ``` { .yaml title="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.Airgapped mode is honored for every license type.

    === ":simple-beats: Filebeat"

        !!! tip "Supported path: `log10x-elastic/filebeat`"

            The chart carries the image swap, the licence wiring and the engine-aware probes, so a full install is a licence and a mode:

            ``` { .yaml title="my-receiver.yaml"}
            tenx:
              enabled: true
              # Required. The engine does not start without one.
              # Optional: without it the engine evaluates. https://doc.log10x.com/manage/license/
              licenseJwt: "<YOUR-10X-LICENSE-JWT>"
              # receive | optimize | report
              kind: "receive"
              runtimeName: "my-filebeat-receiver"
            ```

            ``` { .console .copy }
            helm upgrade --install my-receiver log10x-elastic/filebeat \
              --version 1.5.0 \
              -f my-receiver.yaml \
              --namespace logging --create-namespace
            ```

            Keep the token out of the values file with `--set-file tenx.licenseJwt=license.jwt`, or point `tenx.licenseSecret` at a Secret you already manage. `tenx.licenseDelivery` defaults to `file`, which projects the Secret to a path and sets `TENX_LICENSE_FILE`, so the token never enters the process environment.

            The chart resolves the image itself from its appVersion and `tenx.variant`: chart 1.5.0 renders `log10x/filebeat-10x:1.1.69-native`. Full values: `helm show values log10x-elastic/filebeat`. Ready-made samples for each mode are in [log-10x/elastic-helm-charts](https://github.com/log-10x/elastic-helm-charts/tree/main/samples){target="\_blank"}.

            **Everything below this box is the upstream-overlay path.** Follow it when you already run `elastic/filebeat` and are not ready to move charts, and read the probe gap in Step 2 first.

        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.

        ``` { .yaml title="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: "1.1.69-native"   # `latest` points at this same digest
        imagePullPolicy: IfNotPresent

        daemonset:
          # Filebeat's chart defaults to 200Mi, which is sized for the Go
          # binary alone. The 10x engine runs INSIDE this container, so the
          # pod is OOMKilled (exit 137) at the default. Verified on k3s.
          resources:
            requests:
              cpu: "200m"
              memory: "512Mi"
            limits:
              cpu: "1"
              memory: "1Gi"

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

            # The engine's log4j config writes to /var/log/tenx, which this
            # container cannot create (non-root; and mounting an emptyDir over
            # /var/log/tenx fails at the OCI layer on this image). Without this
            # the engine aborts with:
            #   error initializing pipeline unit #10:
            #     'logOutput(...forwarder/filebeat/input/log4j2.yaml)'
            #     -> Could not create directory /var/log/tenx
            # REQUIRED. The image entrypoint runs
            #   filebeat "$@" 2>&1 | $TENX_BIN run $TENX_RUN_ARGS
            # so with TENX_RUN_ARGS unset the engine is launched with no app
            # and no forwarder, and never builds a receiver pipeline.
            - name: TENX_RUN_ARGS
              value: "@run/input/forwarder/filebeat @apps/receiver"

            - name: TENX_LOG_PATH
              value: /usr/share/filebeat/data/tenx-log/

            # ─── 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`:

        ``` { .yaml title="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.Airgapped mode is honored for every license type.

    === ":simple-opentelemetry: OTel Collector"

        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: the Collector exports to the sidecar on `:4319` and reads processed events back on `:24225`. 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.

        ``` { .yaml title="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:1.1.69
            imagePullPolicy: IfNotPresent
            args:
              - "@run/input/forwarder/otel-collector"
              # Sizing note: at sustained high volume the 10x sidecar is the container
              # that runs out of memory first, not the collector. Measured on k3s:
              # after ~20.8M log records forwarded to the engine the sidecar was
              # OOMKilled at a 3Gi limit while the collector itself stayed healthy at
              # 1Gi. Size the sidecar for your ingest rate and set requests, not just
              # limits -- the default request of 256Mi against a 3Gi limit lets the
              # scheduler overcommit the node.
              - "@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
              # The sidecar's OTLP listener defaults to :4317, the port the
              # Collector's own `otlp` receiver already holds; two listeners on
              # one port is `bind() failed with error(-98)` and a
              # CrashLoopBackOff. This moves the sidecar to :4319, so the
              # Collector keeps :4317 and every OTLP client already pointed at
              # it keeps working. The shipped
              # run/input/forwarder/otel-collector/config.yaml resolves the
              # port through TenXEnv.get("TENX_OTEL_COLLECTOR_INPUT_PORT",
              # 4317) before the launch macro turns the file into arguments,
              # so one value reaches the parser. Passing
              # `otelCollectorInputPort 4319` as an argument is still rejected
              # with "option 'otelCollectorInputPort' (string) should be
              # specified only once": by then the macro has contributed its
              # own copy.
              - name: TENX_OTEL_COLLECTOR_INPUT_PORT
                value: "4319"
            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:
              # The sidecar listens here, moved off :4317 by
              # TENX_OTEL_COLLECTOR_INPUT_PORT above.
              endpoint: 127.0.0.1:4319
              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`:

        ``` { .yaml title="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.Airgapped mode is honored for every license type.

    === ":simple-vector: Vector"

        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.

        ``` { .yaml title="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:1.1.69
            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 the
        # addresses in `customConfig`, 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. (The chart also emits a
        # `containerPort: 9000` for the `tenx_in` sink even though the
        # sidecar, not Vector, listens there; containerPort is
        # informational, so it is harmless.)

        # `kubernetes_logs` below needs the Agent role: your existing
        # values must set `role: Agent`, which is what gives the pod the
        # /var/log hostPath and the RBAC to read pod metadata. The chart
        # default is `Aggregator`.

        # 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.
            #
            # `.tag` is the per-stream identity 10x groups on, so it must
            # differ per log stream. `.source_type` does NOT: it is the
            # literal string `kubernetes_logs` on every event from every
            # pod, which collapses the whole node into one stream and lets
            # multi-line consolidation splice lines from different pods
            # into a single record. Use the pod name, falling back to the
            # file path and then the source type for non-Kubernetes
            # sources.
            ingest:
              type: remap
              inputs: [app_logs]
              source: |
                .cluster = get_env_var("CLUSTER_NAME") ?? "unset"
                .tag = .kubernetes.pod_name || .file || .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`:

        ``` { .yaml title="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.Airgapped mode is honored for every license type.

    === ":simple-logstash: Logstash"

        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.

        ``` { .yaml title="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:1.1.69
            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`:

        ``` { .yaml title="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.Airgapped mode is honored for every license type.

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based. For VM infrastructure, see the [Splunk UF receiver guide](https://doc.log10x.com/run/input/forwarder/splunkUF/).

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab. For VM infrastructure, see the [Datadog Agent receiver guide](https://doc.log10x.com/run/input/forwarder/datadogAgent/).

??? tenx-githubsync "Step 4: Load Configuration"

    Replace the [config folder](https://github.com/log-10x/config){target="\_blank"} 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](https://github.com/log-10x/config/fork){target="\_blank"} (git path only) or pre-populate a PVC with the config (PVC path)
    2. Edit the [app configuration](https://doc.log10x.com/apps/receiver/run/#config-files) to match your metric output and enrichment options
    3. Pick your forwarder and method below:

    === ":simple-fluentd: Fluentd"

        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.

        **:material-git: 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:

        ``` { .yaml title="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:1.1.69
                  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: {}
        ```

        **:material-harddisk: 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):

        ``` { .yaml title="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:1.1.69
                  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
        ```

    === ":simple-fluentbit: Fluent-bit"

        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:`.

        **:material-git: 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:

        ``` { .yaml title="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
        ```

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

        ``` { .yaml title="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
        ```

    === ":simple-beats: Filebeat"

        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.

        **:material-git: 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:

        ``` { .yaml title="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
        ```

        **:material-harddisk: Persistent Volume.** Append to your `my-receiver.yaml` from Step 3:

        ``` { .yaml title="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
        ```

    === ":simple-opentelemetry: OTel Collector"

        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:`.

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

        ``` { .yaml title="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
        ```

        **:material-harddisk: Persistent Volume**:

        ``` { .yaml title="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
        ```

    === ":simple-vector: Vector"

        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:`.

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

        ``` { .yaml title="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
        ```

        **:material-harddisk: Persistent Volume**:

        ``` { .yaml title="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
        ```

    === ":simple-logstash: Logstash"

        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.

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

        ``` { .yaml title="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
        ```

        **:material-harddisk: Persistent Volume**:

        ``` { .yaml title="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
        ```

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based.

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab.

??? tenx-keyfiles "Step 5: Configure Secrets"

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

    **Create the secret:**

    ``` { .console .copy }
    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`:

    === ":simple-fluentd: Fluentd"

        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:

        ``` { .yaml title="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
        ```

    === ":simple-fluentbit: Fluent-bit"

        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:

        ``` { .yaml }
        env:
          - name: ELASTIC_PASSWORD
            valueFrom:
              secretKeyRef:
                name: receiver-credentials
                key: elasticsearch-password
        ```

    === ":simple-beats: Filebeat"

        ``` { .yaml }
        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
        ```

    === ":simple-opentelemetry: OTel Collector"

        ``` { .yaml }
        extraEnvs:
          # For Datadog metrics
          - name: DD_API_KEY
            valueFrom:
              secretKeyRef:
                name: receiver-credentials
                key: datadog-api-key
        ```

    === ":simple-vector: Vector"

        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:

        ``` { .yaml }
        env:
          - name: ELASTIC_PASSWORD
            valueFrom:
              secretKeyRef:
                name: receiver-credentials
                key: elasticsearch-password
        ```

    === ":simple-logstash: Logstash"

        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:

        ``` { .yaml }
        extraEnvs:
          - name: ELASTICSEARCH_PASSWORD
            valueFrom:
              secretKeyRef:
                name: receiver-credentials
                key: elasticsearch-password
        ```

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based. For VM infrastructure, see the [Splunk UF receiver guide](https://doc.log10x.com/run/input/forwarder/splunkUF/).

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab. For VM infrastructure, see the [Datadog Agent receiver guide](https://doc.log10x.com/run/input/forwarder/datadogAgent/).

??? tenx-forwarderinputs "Step 6: Forwarder"

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

    === ":simple-fluentd: Fluentd"

        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.

        ``` { .yaml title="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.

    === ":simple-fluentbit: Fluent-bit"

        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.

        ``` { .yaml }
        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.

    === ":simple-beats: Filebeat"

        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.

        ``` { .yaml }
        # 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.

    === ":simple-opentelemetry: OTel Collector"

        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:

        ``` { .yaml }
        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]
        ```

    === ":simple-vector: Vector"

        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.

    === ":simple-logstash: Logstash"

        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.

        ``` { .yaml title="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.

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based. For VM infrastructure, see the [Splunk UF receiver guide](https://doc.log10x.com/run/input/forwarder/splunkUF/).

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab. For VM infrastructure, see the [Datadog Agent receiver guide](https://doc.log10x.com/run/input/forwarder/datadogAgent/).

??? tenx-mainconfig "Step 7: Deploy"

    Create your namespace (if needed) and deploy:

    ``` { .console .copy }
    kubectl create namespace logging
    ```

    === ":simple-fluentd: Fluentd"

        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.

        ``` { .console .copy title="Linux / macOS"}
        helm install my-receiver fluent/fluentd \
          -f my-receiver.yaml \
          --post-renderer ./tenx-kustomize/post-render.sh \
          --namespace logging
        ```

        ``` { .console .copy title="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.

    === ":simple-fluentbit: Fluent-bit"

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

        ``` { .console .copy }
        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).

    === ":simple-beats: Filebeat"

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

        ``` { .console .copy }
        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.

    === ":simple-opentelemetry: OTel Collector"

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

        ``` { .console .copy }
        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 `:4319` (OTLP in) / `:24225` (OTLP back). The sidecar sits on `:4319` rather than the OTLP default `:4317` so the Collector's own `otlp` receiver keeps that port.

    === ":simple-vector: Vector"

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

        ``` { .console .copy }
        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).

    === ":simple-logstash: Logstash"

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

        ``` { .console .copy }
        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).

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based. For VM infrastructure, see the [Splunk UF receiver guide](https://doc.log10x.com/run/input/forwarder/splunkUF/).

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab. For VM infrastructure, see the [Datadog Agent receiver guide](https://doc.log10x.com/run/input/forwarder/datadogAgent/).

??? tenx-checklist "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:

    === ":simple-fluentd: Fluentd / :simple-fluentbit: Fluent-bit / :simple-opentelemetry: OTel Collector / :simple-vector: Vector"

        ``` { .console .copy }
        kubectl -n logging wait --for=condition=Ready pod -l app.kubernetes.io/instance=my-receiver --timeout=5m
        ```

    === ":simple-beats: Filebeat"

        ``` { .console .copy }
        kubectl -n logging wait --for=condition=Ready pod -l app=my-receiver-filebeat,release=my-receiver --timeout=5m
        ```

    === ":simple-logstash: Logstash"

        ``` { .console .copy }
        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:

    === ":simple-fluentd: Fluentd"

        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):

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

    === ":simple-fluentbit: Fluent-bit"

        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):

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

    === ":simple-beats: Filebeat"

        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:

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

    === ":simple-logstash: Logstash"

        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):

        ``` { .console .copy }
        kubectl -n logging logs -l app=my-receiver-logstash,release=my-receiver -c log10x --tail=200 | grep -E '🪵|🚀|📥|🚦|📈|📝'
        ```

    === ":simple-opentelemetry: OTel Collector"

        The Collector's 10x sidecar runs in its own container (`log10x`), same as Fluentd's and Fluent Bit's. The 📥 line names the port the sidecar bound; a banner without it means the listener never came up:

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

    === ":simple-vector: Vector"

        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.

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

    **Phase C: processed events flowing**

    The receiver stamps a `tenx_hash` field on every record it hands back, so that field arriving at the destination is the proof that events completed the round trip through 10x. For a real destination, query for the field there: Elasticsearch `exists: tenx_hash`, Splunk `tenx_hash=*`, Datadog `@tenx_hash:*`. For the stdout / `debug` placeholder destination from Step 3, count it on the container that writes to the destination. Pick your forwarder:

    === ":simple-fluentd: Fluentd"

        ``` { .console .copy }
        kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c fluentd --tail=500 | grep -c tenx_hash
        ```

    === ":simple-fluentbit: Fluent-bit"

        ``` { .console .copy }
        kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c fluent-bit --tail=500 | grep -c tenx_hash
        ```

    === ":simple-beats: Filebeat"

        Filebeat runs the engine inside the `filebeat` container, and the engine's startup banner itself contains the string `tenx_hash`. Drop that line so the count reflects records only:

        ``` { .console .copy }
        kubectl -n logging logs -l app=my-receiver-filebeat,release=my-receiver -c filebeat --tail=500 | grep -F tenx_hash | grep -cvF 'fullText('
        ```

    === ":simple-logstash: Logstash"

        ``` { .console .copy }
        kubectl -n logging logs -l app=my-receiver-logstash,release=my-receiver -c logstash --tail=500 | grep -c tenx_hash
        ```

    === ":simple-opentelemetry: OTel Collector"

        ``` { .console .copy }
        kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c opentelemetry-collector --tail=500 | grep -c tenx_hash
        ```

    === ":simple-vector: Vector"

        ``` { .console .copy }
        kubectl -n logging logs -l app.kubernetes.io/instance=my-receiver -c vector --tail=500 | grep -c tenx_hash
        ```

    A non-zero count means events reached the engine and came back. `grep -c` prints `0` and exits 1 when nothing returned, which is the failing case.

    Three things to keep in mind when reading the count:

    - `--tail` has to be explicit. With a label selector `kubectl logs` defaults to the last 10 lines per pod, short enough to report `0` on a healthy install.
    - Do not run this against the `log10x` container. Its startup banner prints `fullText("tenx_hash")` before a single event has flowed, so it reports success on a sidecar that never received anything.
    - Do not compare a sent count against a returned count on a live tail. At low volume the forwarder's flush window holds records in flight, so the two numbers only converge once the source stops and the pipeline drains.

    **View mute/sample counts in the dashboard:**

    Once running, view your mute/sample activity in the [Receiver Dashboard](https://doc.log10x.com/roi-analytics/#receiver).

??? tenx-delete "Step 9: Teardown"

    **Uninstall the Helm release:**

    ``` { .console .copy }
    helm -n logging uninstall my-receiver
    ```

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

    === ":simple-fluentd: Fluentd / :simple-fluentbit: Fluent-bit / :simple-opentelemetry: OTel Collector / :simple-vector: Vector"

        ``` { .console .copy }
        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
        ```

    === ":simple-beats: Filebeat / :simple-logstash: Logstash"

        ``` { .console .copy }
        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:**

    === ":simple-fluentd: Fluentd / :simple-fluentbit: Fluent-bit / :simple-opentelemetry: OTel Collector / :simple-vector: Vector"

        ``` { .console .copy }
        kubectl -n logging get all,configmap,secret,pvc -l app.kubernetes.io/instance=my-receiver
        ```

    === ":simple-beats: Filebeat / :simple-logstash: Logstash"

        ``` { .console .copy }
        kubectl -n logging get all,configmap,secret,pvc -l release=my-receiver
        ```

    **Delete the namespace (optional):**

    ``` { .console .copy }
    kubectl delete namespace logging --ignore-not-found
    ```

??? tenx-run "Quickstart Full Sample"

    === ":simple-fluentd: Fluentd"

        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](#tenx-config). Re-use them as-is; only `my-receiver.yaml` typically changes per deployment.

        ``` { .yaml title="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

        # Required. The chart's default probes GET /metrics on port 24231,
        # served by the `@type prometheus` source in the chart's own
        # `01_sources.conf`, which this overlay replaces. `httpGet: null`
        # is required because Helm merges maps.
        livenessProbe:
          httpGet: null
          tcpSocket:
            port: 24225
        readinessProbe:
          httpGet: null
          tcpSocket:
            port: 24225

        # 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
                enable_ruby true      # required for the ENV[...] lookup
                <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:

        ``` { .console .copy }
        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.

    === ":simple-fluentbit: Fluent-bit"

        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`.

        ``` { .yaml title="my-receiver.yaml"}
        extraContainers:
          - name: log10x
            image: log10x/edge-10x:1.1.69
            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.

    === ":simple-beats: Filebeat"

        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`.

        ``` { .yaml title="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: "1.1.69-native"   # `latest` points at this same digest
        imagePullPolicy: IfNotPresent

        daemonset:
          # Filebeat's chart defaults to 200Mi, which is sized for the Go
          # binary alone. The 10x engine runs INSIDE this container, so the
          # pod is OOMKilled (exit 137) at the default. Verified on k3s.
          resources:
            requests:
              cpu: "200m"
              memory: "512Mi"
            limits:
              cpu: "1"
              memory: "1Gi"

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

            # The engine's log4j config writes to /var/log/tenx, which this
            # container cannot create (non-root; and mounting an emptyDir over
            # /var/log/tenx fails at the OCI layer on this image). Without this
            # the engine aborts with:
            #   error initializing pipeline unit #10:
            #     'logOutput(...forwarder/filebeat/input/log4j2.yaml)'
            #     -> Could not create directory /var/log/tenx
            # REQUIRED. The image entrypoint runs
            #   filebeat "$@" 2>&1 | $TENX_BIN run $TENX_RUN_ARGS
            # so with TENX_RUN_ARGS unset the engine is launched with no app
            # and no forwarder, and never builds a receiver pipeline.
            - name: TENX_RUN_ARGS
              value: "@run/input/forwarder/filebeat @apps/receiver"

            - name: TENX_LOG_PATH
              value: /usr/share/filebeat/data/tenx-log/

            # 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.

    === ":simple-opentelemetry: OTel Collector"

        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`.

        ``` { .yaml title="my-receiver.yaml"}
        extraContainers:
          - name: log10x
            image: log10x/edge-10x:1.1.69
            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
              # Moves the sidecar's OTLP listener off :4317, which the
              # Collector's own `otlp` receiver holds.
              - name: TENX_OTEL_COLLECTOR_INPUT_PORT
                value: "4319"
            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:
              # The sidecar listens here, moved off :4317 by
              # TENX_OTEL_COLLECTOR_INPUT_PORT above.
              endpoint: 127.0.0.1:4319
              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]
        ```

    === ":simple-vector: Vector"

        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`.

        ``` { .yaml title="my-receiver.yaml"}
        extraContainers:
          - name: log10x
            image: log10x/edge-10x:1.1.69
            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 the addresses in
        # `customConfig`, so 9001 is already declared on the `vector`
        # container. The sidecar connects out on 127.0.0.1, no port of its
        # own needed.

        # `kubernetes_logs` needs `role: Agent` in your existing values
        # (the chart default is `Aggregator`, which has neither the
        # /var/log hostPath nor the RBAC).

        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:
            # `.tag` must differ per log stream. `.source_type` is the same
            # literal on every event from every pod, which collapses the
            # node into one stream and lets multi-line consolidation splice
            # lines from different pods into one record.
            ingest:
              type: remap
              inputs: [app_logs]
              source: |
                .cluster = get_env_var("CLUSTER_NAME") ?? "unset"
                .tag = .kubernetes.pod_name || .file || .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](https://doc.log10x.com/run/input/forwarder/vector/) for the architecture, the four-mode contract, and the matching local-install walkthrough.

    === ":simple-logstash: Logstash"

        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`.

        ``` { .yaml title="my-receiver.yaml"}
        extraContainers: |
          - name: log10x
            image: log10x/edge-10x:1.1.69
            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](https://doc.log10x.com/run/input/forwarder/logstash/) for the architecture, the four-mode contract, and the matching local-install walkthrough.

    === ":simple-splunk: Splunk UF"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** tab. [Splunk Connect for Kubernetes](https://github.com/splunk/splunk-connect-for-kubernetes){target="\_blank"} is Fluent Bit-based. For VM infrastructure, see the [Splunk UF receiver guide](https://doc.log10x.com/run/input/forwarder/splunkUF/).

    === ":simple-datadog: Datadog Agent"

        For Kubernetes, use the **:simple-fluentbit: Fluent Bit** or **:simple-opentelemetry: OTel Collector** tab. For VM infrastructure, see the [Datadog Agent receiver guide](https://doc.log10x.com/run/input/forwarder/datadogAgent/).

??? tenx-eventoutputs "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.

    === ":simple-fluentbit: Fluent Bit + Datadog Agent"

        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.

        ``` { .yaml title="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:

        ``` { .yaml title="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`.

    === ":simple-opentelemetry: OTel Collector + Datadog Agent"

        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:

        ``` { .yaml title="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:

        ``` { .yaml title="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`.

??? tenx-eventoutputs "Splunk HEC Output Examples"

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

    === ":simple-fluentd: Fluentd"

        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.

        ``` { .yaml title="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:

        ``` { .yaml }
        env:
          - name: SPLUNK_HEC_TOKEN
            valueFrom:
              secretKeyRef:
                name: receiver-credentials
                key: splunk-hec-token
        ```

    === ":simple-fluentbit: Fluent-bit"

        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.

        ``` { .yaml title="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:

        ``` { .yaml }
        env:
          - name: SPLUNK_HEC_TOKEN
            valueFrom:
              secretKeyRef:
                name: receiver-credentials
                key: splunk-hec-token
        ```

    === ":simple-opentelemetry: OTel Collector"

        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`.

        ``` { .yaml title="my-receiver.yaml"}
        extraContainers:
          - name: log10x
            image: log10x/edge-10x:1.1.69
            imagePullPolicy: IfNotPresent
            args:
              - "@run/input/forwarder/otel-collector"
              - "@apps/receiver"
            env:
              - name: TENX_LICENSE_FILE
                value: /etc/tenx/license/license.jwt
              # Moves the sidecar's OTLP listener off :4317, which the
              # Collector's own `otlp` receiver holds.
              - name: TENX_OTEL_COLLECTOR_INPUT_PORT
                value: "4319"
            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:
              # The sidecar listens here, moved off :4317 by
              # TENX_OTEL_COLLECTOR_INPUT_PORT above.
              endpoint: 127.0.0.1:4319
              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]
        ```

    === ":simple-logstash: Logstash"

        ``` { .yaml title="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"
              }
            }
        ```
