> For the complete documentation index, see [llms.txt](https://docs.finout.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.finout.io/kubernetes-integrations/kubernetes/finout-eks-kubernetes-agent/install-the-finout-eks-kubernetes-agent.md).

# Install the Finout EKS Kubernetes Agent

This guide walks you through installing the [Finout EKS Kubernetes Agent](/kubernetes-integrations/kubernetes/finout-eks-kubernetes-agent.md) on your clusters. The agent collects workload metrics from your clusters and exports them to Finout for Kubernetes cost allocation.

**Availability:** Generally available for EKS.&#x20;

For an overview of what the agent does and how it works, see [Finout Kubernetes Agent overview](/kubernetes-integrations/kubernetes/finout-kubernetes-agent.md).

### Overview

The EKS Kubernetes Agent installation involves three steps:

1. **Generate an API token** – Create a dedicated authentication token for the agent in the Finout app.
2. **Deploy via Helm** – Paste your token credentials into the provided Helm command and run it against your EKS cluster.
3. **Notify Finout Support** – Let the Finout Support team know the agent is deployed and specify which AWS Cost Centers you'd like to enrich with Kubernetes cost allocation data.

### Prerequisites

Before you begin, confirm the following:

* **Finout account with Kubernetes** EKS **Agent role enabled:** Contact your Finout Customer Success manager to have the EKS Kubernetes Agent role enabled on your account.
* **Helm 3.8 or later:** Required for OCI-based chart pulls (`oci://`).
* **StorageClass:** The agent buffers metric files on a 10 GiB PersistentVolume. A default StorageClass must be available, or you must provide one via `--set centralCollector.persistence.storageClassName=<name>`.
* **Outbound HTTPS access:** The agent uploads metrics over HTTPS. Ensure your cluster nodes can reach `app.finout.io` on port 443 and AWS S3 endpoints.

### Step 1 - Generate an API token

1. In the Finout app, **click your username** in the top-right corner and select **Admin Portal**.<br>

   <figure><img src="https://3858159242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWqjB2puKXPDR7L86FX2e%2Fuploads%2FiJYvzgCOWPwPHpxNEJeT%2FScreenshot%202026-06-17%20at%2010.58.55.png?alt=media&amp;token=6672a669-0800-480b-8337-610d243fad9d" alt="" width="288"><figcaption></figcaption></figure>

2. Navigate to **API Tokens**.<br>

   <figure><img src="https://3858159242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWqjB2puKXPDR7L86FX2e%2Fuploads%2F24wEUcSuUZMy5GEhPdDv%2FScreenshot%202026-06-17%20at%2010.59.52.png?alt=media&amp;token=07423380-befa-4bec-af44-63b761176225" alt="" width="257"><figcaption></figcaption></figure>

   <br>

3. Click **Generate Token**.

4. Fill in the token details:
   * **Description**
   * **Role:** Select **Finout K8s Agent**.
   * **Expiration:** Set to **Never expire**.<br>

     <img src="https://3858159242-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWqjB2puKXPDR7L86FX2e%2Fuploads%2FCHaQZEu1gTYZvN1jIF0g%2Funknown.png?alt=media&amp;token=5e92d3fc-68e8-49e3-915d-43b611cd94d6" alt="" width="451">

5. Click **Generate**.

6. **Copy both credentials** and store them somewhere safe:
   * **Client ID**
   * **Secret Key**

{% hint style="warning" %}
The Secret Key is shown only once. If you leave this screen without copying it, you'll need to generate a new token.
{% endhint %}

### Step 2 - Deploy the Kubernetes Agent via Helm

In the following command, replace `FINOUT_CLIENT_ID` and `FINOUT_SECRET_KEY` with the credentials generated in Step 1, and set `<NAME_OF_YOUR_CLUSTER>` to the name of the cluster you want to deploy the agent on:

```bash
helm upgrade --install finout-agent \
  oci://public.ecr.aws/finout/charts/finout-k8s-agent:0.8.2 \
  --namespace finout-agent \
  --create-namespace \
  --set centralCollector.uploader.auth.clientId="FINOUT_CLIENT_ID" \
  --set centralCollector.uploader.auth.secretKey="FINOUT_SECRET_KEY" \
  --set centralCollector.uploader.clusterId="<NAME_OF_YOUR_CLUSTER>"
```

{% hint style="success" %}
**Recommended:** Set a `priorityClass` to ensure the agent is scheduled and remains running, even under node resource pressure, avoiding gaps in metric collection. See [Advanced Configurations](#schedule-the-agent-reliably-with-a-priorityclass-recommended) below.
{% endhint %}

#### Resource Sizing

The agent's resource needs scale with your cluster: kube-state-metrics and the central collector grow with pod count and label density, and the node collector with pods per node.&#x20;

The chart ships with defaults that fit most clusters — deliberately not all of them, so small clusters don't reserve resources they'll never use.

Start from the tier recommendations below, then monitor the agent's components and [adjust](#override-the-agents-default-resource-requests) when they approach their limits.

**Default configuration:**

| Component          | Kind                   | Scope    | CPU req / lim | Memory req / lim |
| ------------------ | ---------------------- | -------- | ------------- | ---------------- |
| Node Collector     | DaemonSet              | Per node | 100m / 500m   | 350Mi / 700Mi    |
| Central Collector  | Deployment (1 replica) | Cluster  | 400m / 1000m  | 2Gi / 4Gi        |
| kube-state-metrics | Deployment (1 replica) | Cluster  | 300m / 600m   | 2Gi / 4Gi        |

**Recommended configuration:**

| Cluster Tier | Pods            | Nodes     | KSM CPU req / lim | KSM Memory req / lim | Central Collector CPU req / lim | Central Collector Memory req / lim |
| ------------ | --------------- | --------- | ----------------- | -------------------- | ------------------------------- | ---------------------------------- |
| Small        | < 2,000         | < 50      | 150m / 1000m      | 512Mi / 2Gi          | 150m / 500m                     | 512Mi / 2Gi                        |
| Medium       | 2,000 – 10,000  | 50 – 250  | 250m / 1000m      | 1Gi / 3Gi            | 250m / 500m                     | 1Gi / 3Gi                          |
| Large        | 10,000 – 25,000 | 250 – 800 | 300m / 1000m      | 2Gi / 4Gi            | 300m / 600m                     | 2Gi / 4Gi                          |
| Extra Large  | > 25,000        | > 800     | 500m / 1000m      | 3Gi / 6Gi            | 500m / 1000m                    | 3Gi / 6Gi                          |

Once the command completes successfully, the agent begins scraping metrics from your cluster and uploading them to Finout.

#### Advanced Configurations (Optional)

#### **Schedule the agent reliably with a PriorityClass (Recommended)**

On small or heavily-loaded clusters, the collector pods may fail to schedule when nodes don't have enough free CPU or memory. Giving them a higher scheduling priority lets Kubernetes preempt lower-priority pods to make room, so the agent schedules reliably. \
\
The Helm chart can create a (cluster-scoped) PriorityClass for you.&#x20;

**How to configure:**

Add the following to your Helm command: `--set priorityClass.create=true`

Both the node collector and the central collector will automatically use the created PriorityClass.

{% hint style="info" %}
The priority defaults to `1000000` - above ordinary application pods, but below Kubernetes' system-critical classes, so cluster-critical pods are never preempted.&#x20;
{% endhint %}

**To use a different value:** \
`--set priorityClass.create=true --set priorityClass.value=500000000`

{% hint style="info" %}
Note: creating a PriorityClass is a cluster-scoped operation, so the identity running Helm must have permission to create PriorityClasses.
{% endhint %}

**Use an existing PriorityClass**

&#x20;If your cluster already defines a PriorityClass you'd like the agent to use, reference it directly instead of having the chart create one.

`--set nodeCollector.priorityClassName=<your-priority-class> --set centralCollector.priorityClassName=<your-priority-class>`

A priorityClassName set this way always takes precedence over the chart-created class.

#### **Override the agent's default resource requests**

You can override the [chart's default](#resource-footprint) for every agent's component.&#x20;

{% hint style="info" %}
For recommended values by cluster size, see the tier table in the [Resource Footprint](#resource-footprint)
{% endhint %}

**How to configure:**

To update the resources, extend the helm command with the following `--set` flags (or put the equivalent keys in your values file), and set the values that match your cluster tier:\
\
&#x20;   `--set kube-state-metrics.resources.requests.cpu=300m \`\
&#x20;   `--set kube-state-metrics.resources.requests.memory=2Gi \`\
&#x20;   `--set kube-state-metrics.resources.limits.cpu=600m \`\
&#x20;   `--set kube-state-metrics.resources.limits.memory=4Gi \`\
&#x20;   `--set centralCollector.resources.requests.cpu=400m \`\
&#x20;   `--set centralCollector.resources.requests.memory=2Gi \`\
&#x20;   `--set centralCollector.resources.limits.cpu=1000m \`\
&#x20;   `--set centralCollector.resources.limits.memory=4Gi \`\
&#x20;   `--set nodeCollector.resources.requests.cpu=100m \`\
&#x20;   `--set nodeCollector.resources.requests.memory=350Mi \`\
&#x20;   `--set nodeCollector.resources.limits.cpu=500m \`\
&#x20;   `--set nodeCollector.resources.limits.memory=700Mi`

#### Increase the metrics buffer volume

The central collector buffers rotated metric files on a 10 GiB PersistentVolume before uploading them to Finout. On large clusters, or when uploads stall for an extended period, this buffer can fill up. Increase the volume size to give the collector more headroom.

**How to configure:**

Add the following to your Helm command, replacing `<SIZE>` with the size in GiB:

`--set centralCollector.persistence.size=<SIZE>Gi`

Example: `--set centralCollector.persistence.size=20Gi`

#### **Use an existing Kubernetes secret for credentials**

By default, the Helm chart creates a Kubernetes Secret using the `clientId` and `secretKey` values you provide. If your organization manages secrets and deployments externally, you can skip secret creation and reference an existing secret instead.

**How to configure?**

1. Add the following to your Helm command:\
   \
   `--set centralCollector.uploader.auth.createSecret=false --set`\
   `centralCollector.uploader.auth.existingSecretName=`
2. When using this option, omit the credential flags from your [base command](#step-2-deploy-the-kubernetes-agent-via-helm):\
   \
   `--set centralCollector.uploader.auth.clientId=$CLIENT_ID --set centralCollector.uploader.auth.secretKey=$SECRET_KEY`

{% hint style="warning" %}
Your existing secret must contain the `clientId` and `secretKey` keys and be present in the same namespace as the agent before installation.
{% endhint %}

#### **Central Collector Deployment Scheduling Configuration**

To schedule the central collector pod on specific nodes, use `centralCollector.nodeSelector.`\
\
Example: `--set centralCollector.nodeSelector.workload-type=monitoring`&#x20;

#### **Set affinity on the central collector**

To control pod placement using affinity rules, pass a JSON affinity object with `--set-json`.

Example: `--set-json 'centralCollector.affinity={"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"topology.kubernetes.io/zone","operator":"In","values":["us-east-1a"]}]}]}}}'`

#### **Set tolerations on the central collector**

To allow the central collector pod to be scheduled on tainted nodes, pass a JSON tolerations array with `--set-json.`

Example: `--set-json 'centralCollector.tolerations=[{"key":"dedicated","operator":"Equal","value":"monitoring","effect":"NoSchedule"}]'`

#### **Prefer to review manifests before applying?**&#x20;

Use `helm template` to render the manifests locally first:

```bash
helm template finout-agent \
  oci://public.ecr.aws/finout/charts/finout-k8s-agent:0.8.2 \
  --no-hooks \
  --namespace finout-agent \
  --create-namespace \
  --set centralCollector.uploader.auth.clientId="FINOUT_CLIENT_ID" \
  --set centralCollector.uploader.auth.secretKey="FINOUT_SECRET_KEY" \
  --set centralCollector.uploader.clusterId="<NAME_OF_YOUR_CLUSTER>" \
  > finout-agent-manifests.yaml
```

Once reviewed, you can deploy the agent using standard Kubernetes tooling:

```bash
kubectl create namespace finout-agent
kubectl apply -f finout-agent-manifests.yaml
```

### Uninstall The Kubernetes EKS Agent

If installed via Helm:

```bash
helm uninstall finout-agent --namespace finout-agent
kubectl delete namespace finout-agent
```

If installed via kubectl apply:

```bash
kubectl delete namespace finout-agent
```

### **Step 3 - Notify Finout Support**

Let the Finout Support team know the agent is deployed and specify which AWS and/or GCP Cost Centers you'd like to enrich with Kubernetes cost allocation data.

{% hint style="warning" %}
If you are already enriching an AWS or GCP Cost Center using Kubernetes data from Prometheus or Datadog, you cannot enrich the same Cost Center using the Kubernetes Agent without migrating away from your existing method first.
{% endhint %}

### **Kubernetes Data Availability**

[Kubernetes cost and usage data](/kubernetes-integrations/kubernetes/how-finout-calculates-kubernetes-costs.md) will appear across Finout within 48 hours, matching the standard AWS billing data delivery window.

#### Health Metrics Availability&#x20;

Once deployed, the Finout Kubernetes agent exposes Prometheus health metrics that let you confirm, from inside your cluster, that metrics are leaving it and reaching Finout.

**Exposed Metrics**

<table><thead><tr><th>Metric</th><th width="131.49609375">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>finout_uploader_oldest_pending_file_age_seconds</code></td><td>Gauge</td><td><p>The age of the oldest file still waiting to be uploaded. </p><ul><li>Stays low (seconds–minutes) while data flows.</li><li>Climbs steadily the moment uploads stall. </li><li>Absent when nothing is pending.</li></ul></td></tr><tr><td><code>finout_uploader_files_pending</code></td><td>Gauge</td><td>Number of rotated metric files on the disk waiting to be uploaded.</td></tr><tr><td><code>finout_uploader_files_uploaded</code></td><td>Counter</td><td>Number of files successfully uploaded to Finout since the process started.</td></tr><tr><td><code>finout_uploader_upload_failures</code></td><td>Counter</td><td><p>Number of failed upload attempts since the process started. </p><p><em>Counts attempts, not distinct files — a file that keeps failing increments this on every retry.</em></p></td></tr><tr><td><code>finout_uploader_disk_used_bytes</code></td><td>Gauge</td><td><p>Used bytes on the metrics data volume. </p><p>Absent if the volume can't be read.</p></td></tr><tr><td><code>finout_uploader_disk_free_bytes</code></td><td>Gauge</td><td>Free bytes on the metrics data volume.</td></tr><tr><td><code>finout_uploader_disk_total_bytes</code></td><td>Gauge</td><td>Total writable bytes on the metrics data volume.</td></tr></tbody></table>

**How To Access**

The `central-collector` Service serves the metrics in Prometheus text format at `/metrics` on port `8889`.

{% hint style="info" %}
The `central-collector` Service ships with `prometheus.io/scrape` annotations on port `8889` (on by default), So, an annotation-based Prometheus in your cluster automatically discovers the endpoint. No operator required.&#x20;
{% endhint %}

**To check the endpoint manually:**

1. **Port-forward** the Service:

   `kubectl -n finout-agent port-forward svc/finout-agent-central-collector 8889:8889`
2. **Scrape** the endpoint:

   `curl localhost:8889/metrics`

**Configuration**

Health metrics are enabled by default, controlled under `centralCollector.uploader.healthMetrics` in `values.yaml`:

```yaml
centralCollector:
  uploader:
    healthMetrics:
      enabled: true
      port: 8889

      # Annotation-based discovery (default). Works without the Prometheus Operator.
      annotations:
        enabled: true

      # Prometheus Operator ServiceMonitor. Requires the ServiceMonitor CRD.
      serviceMonitor:
        enabled: false
        additionalLabels: {}   # Must match your Prometheus's serviceMonitorSelector,
                                # or the ServiceMonitor is silently ignored.
                                # kube-prometheus-stack defaults to `release: <prometheus-release-name>`.
        interval: 30s
        scrapeTimeout: 10s
```

{% hint style="info" %}
If ServiceMonitor is your only scrape path, set `annotations.enabled` to `false` to avoid duplicate scrapes of the same endpoint.
{% endhint %}

You can also set these values with `--set` flags on your Helm command, the same way as the other options above.

For more information, please see the [FAQs](/kubernetes-integrations/kubernetes/finout-kubernetes-agent/kubernetes-agent-faqs.md) article.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.finout.io/kubernetes-integrations/kubernetes/finout-eks-kubernetes-agent/install-the-finout-eks-kubernetes-agent.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
