> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aiaxoniq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Installation overview

> What you actually install to send telemetry to aiAxonIQ, how to choose between a collector and direct instrumentation, and which platform guide to open.

There is no aiAxonIQ agent to install. aiAxonIQ speaks standard OTLP, so what
you install is the upstream **OpenTelemetry Collector**, or an OpenTelemetry SDK
inside your application, with an endpoint and a header set.

That is a deliberate property rather than a gap: nothing here is proprietary, and
if you later point the same exporter at a second backend, none of this changes.

## Collector or direct?

Both work, and most production setups end up using both. The decision is about
where you want the endpoint, the key and the retry behaviour to live.

<CardGroup cols={2}>
  <Card title="A collector — recommended" icon="server">
    Your applications export to a collector on the same host or node; the
    collector forwards to aiAxonIQ.

    **Choose this** for anything running on real infrastructure.
  </Card>

  <Card title="Direct from the SDK" icon="code">
    Each application exports straight to aiAxonIQ.

    **Choose this** for a laptop, a single container, or a first experiment.
  </Card>
</CardGroup>

Three reasons the collector is worth the extra component:

* **The license key lives in one place.** With direct export, every workload
  carries an ingest credential in its environment. With a collector, one does.
* **It collects what your application cannot see.** Host CPU, memory, disk,
  network, container and Kubernetes metrics come from the collector. An
  instrumented application knows nothing about the machine it runs on.
* **Batching and retry are configured once**, correctly, instead of per service
  and per language.

<Note>
  Direct SDK export is not wrong, and it is the fastest way to see a trace. The
  cost is that the endpoint and key are then set in every service's environment,
  which becomes the thing you have to change when either one changes.
</Note>

## Choose your platform

<CardGroup cols={2}>
  <Card title="Docker" icon="docker" href="/send-data/platforms/docker">
    One collector container beside your application containers. The shortest
    real install.
  </Card>

  <Card title="Docker Compose" icon="layer-group" href="/send-data/platforms/docker-compose">
    A collector service in your existing stack, reachable by name from every
    other service.
  </Card>

  <Card title="Linux VM" icon="linux" href="/send-data/platforms/linux">
    A native package under systemd, collecting host metrics and journald logs.
  </Card>

  <Card title="Kubernetes" icon="dharmachakra" href="/send-data/platforms/kubernetes">
    A DaemonSet for node telemetry plus a single Deployment for cluster
    telemetry.
  </Card>
</CardGroup>

Not on any of these? [Instrument your application](/send-data/otel/zero-code)
covers exporting directly from your language's SDK, and
[Send data with OpenTelemetry](/send-data/otel/collector) documents the raw
endpoints if you want to build the request yourself.

## What every install needs

<Info>
  **Before you start, you need two values.**

  1. **Your base endpoint** — shown on **Get Started** in the dashboard. These
     pages write it as `$OIQ_ENDPOINT`.
  2. **A license key** — created in **Settings → License Keys**, starting
     `oiq_`. Requires the Admin role. See
     [Create a license key](/get-started/license-keys). These pages write it as
     `$OIQ_LICENSE_KEY`.

  Export both before running anything below:

  ```bash theme={null}
  export OIQ_ENDPOINT="https://app.aiaxoniq.com/otlp"   # or your own
  export OIQ_LICENSE_KEY="oiq_..."
  ```
</Info>

| Deployment                               | Base endpoint                    |
| :--------------------------------------- | :------------------------------- |
| **aiAxonIQ Cloud**                       | `https://app.aiaxoniq.com/otlp`  |
| **Self-hosted** behind the bundled nginx | `https://app.<your-domain>/otlp` |

Both forms carry the `/otlp` prefix because nginx serves OTLP under it and
strips it before forwarding, so the receiver still sees `/v1/logs`. Dropping the
prefix is the most common setup mistake: the request reaches the dashboard
instead of the receiver and comes back as an HTML 404 rather than an ingest
error.

## What arrives, and from where

Knowing which component produces which signal saves the most common
disappointment — an empty Infrastructure or Kubernetes page while application
telemetry flows perfectly.

| Signal                                           | Comes from                                        |
| :----------------------------------------------- | :------------------------------------------------ |
| Traces                                           | Your application's SDK                            |
| Application metrics                              | Your application's SDK                            |
| Application logs                                 | Your SDK, or the collector reading container logs |
| **Host metrics** — CPU, memory, disk, network    | **The collector's `hostmetrics` receiver**        |
| **Container metrics**                            | **The collector on that host**                    |
| **Kubernetes objects** — pods, nodes, namespaces | **The cluster collector**                         |

<Warning>
  Instrumenting your applications alone will never populate the Infrastructure
  or Kubernetes pages. Those are descriptions of the machine and the cluster,
  and only a collector running there can see them.
</Warning>

## Verify any install

Whatever you install, the same two checks confirm it end to end.

The collector publishes its own metrics on port `8888`. Three counters separate
the two things that can be wrong:

```bash theme={null}
curl -s localhost:8888/metrics | grep -E 'otelcol_(receiver_accepted|exporter_sent|exporter_send_failed)'
```

| Counter                          | Rising means                               |
| :------------------------------- | :----------------------------------------- |
| `otelcol_receiver_accepted_*`    | Your application is reaching the collector |
| `otelcol_exporter_sent_*`        | The collector is reaching aiAxonIQ         |
| `otelcol_exporter_send_failed_*` | It is trying and being refused             |

Accepted rising while sent stays flat is the classic signature of a wrong
endpoint or a missing key in the exporter block. It is worth checking before
anything else, because from inside your application everything looks fine.

Then confirm the data landed: open **Logs**, set the range to the last 15
minutes, and search for a service you expect. Full detail in
[Verify your data arrived](/get-started/verify-data).

## Troubleshooting

<AccordionGroup>
  <Accordion title="The collector starts, then logs 'Exporting failed. Will retry'" icon="arrows-rotate">
    Read the `error` field on that line — it names the cause exactly.

    ```text theme={null}
    Exporting failed. Will retry the request after interval.
      {"kind": "exporter", "name": "otlphttp/aiaxoniq",
       "error": "failed to make an HTTP request: Post \"…/v1/metrics\":
                 dial tcp: lookup … : no such host", "interval": "5.2s"}
    ```

    * `no such host` — the endpoint hostname does not resolve. Check
      `$OIQ_ENDPOINT` against the value on **Get Started**.
    * `connection refused` — the host resolves but nothing is listening on that
      port.
    * `401 Unauthorized` — the key is missing, malformed or revoked.
    * `404` — the endpoint already ends in `/v1/…`. It must be the **base** URL;
      the collector appends the signal path itself.

    The collector retries with backoff and does not drop data while it retries,
    so a transient failure here is not a loss.
  </Accordion>

  <Accordion title="Nothing at all in the logs after 'Everything is ready'" icon="ear-listen">
    The collector is running and receiving nothing. That is an application-side
    problem, not a collector one — your services are not exporting to it.

    Check that your application's `OTEL_EXPORTER_OTLP_ENDPOINT` points at the
    collector's OTLP port (`4318` for HTTP, `4317` for gRPC), not at aiAxonIQ.
  </Accordion>

  <Accordion title="401 with a key you know is correct" icon="key">
    Two causes that are not about the key's value:

    * **A trailing newline.** A key read from a file created by a shell heredoc,
      or a Kubernetes Secret made with `--from-file`, carries the newline as
      part of the value. Use `--from-literal`, or `printf` rather than `echo`.
    * **A validation outage.** If the receiver cannot reach the service that
      validates keys it fails closed and returns the same `401`. A sudden `401`
      across every service at once, with a key you have not changed, is far more
      likely to be this. Check `$OIQ_ENDPOINT/health` first.
  </Accordion>

  <Accordion title="Config changes appear to do nothing" icon="file-pen">
    The collector reads its configuration only at startup. Restart it after any
    edit, and confirm the file you edited is the one mounted into the process —
    a bind mount pointing at a path that does not exist silently yields the
    image's default config rather than an error.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={3}>
  <Card title="Quickstart first" icon="rocket" href="/get-started/quickstart">
    Prove the path with curl before installing anything.
  </Card>

  <Card title="Endpoints and errors" icon="list" href="/send-data/endpoints">
    Every path, status code and limit.
  </Card>

  <Card title="Troubleshooting" icon="triangle-exclamation" href="/support/troubleshooting">
    Stage-by-stage diagnosis.
  </Card>
</CardGroup>
