# Kubernetes Manifest

This guide covers deploying Qtap in cloud-connected mode using a Kubernetes manifest, managed through the Qplane control plane. Use this approach when you need full control over your Kubernetes resources or can't use Helm directly.

## Preflight Check

If you'd like to verify your environment's compatibility, use the [following script](https://github.com/qpoint-io/preflight/tree/main):

{% code overflow="wrap" %}

```bash
curl -sSL https://github.com/qpoint-io/preflight/releases/latest/download/preflight.sh | sudo bash
```

{% endcode %}

## Prerequisites

* Kubernetes cluster on Linux hosts with supported kernel (5.10+)
* `kubectl` configured for your cluster
* `helm` (for generating the base manifest)
* A valid registration token from [app.qpoint.io](https://app.qpoint.io) (Settings → Installation)

## Generating the Base Manifest

Use the Helm chart to generate a static Kubernetes manifest:

```bash
helm repo add qpoint https://helm.qpoint.io/
helm repo update
```

Generate the manifest with your registration token stored in a Kubernetes secret:

```bash
helm template qtap qpoint/qtap \
  --set registrationTokenSecretRefName="qtap-token" \
  --set logLevel=warn \
  > qtap-manifest.yaml
```

To see all available chart values:

```bash
helm show values qpoint/qtap
```

## Creating the Registration Token Secret

Store your registration token as a Kubernetes secret:

```bash
kubectl create namespace qpoint
```

```bash
kubectl create secret generic qtap-token \
  --from-literal=token='<your-registration-token>' \
  -n qpoint
```

{% hint style="warning" %}
Keep your registration token secure. Do not commit it to version control or include it directly in manifest files.
{% endhint %}

## Deploying

Apply the generated manifest:

```bash
kubectl apply -f qtap-manifest.yaml -n qpoint
```

## Verifying the Deployment

1. Check that pods are running:

```bash
kubectl get pods -n qpoint
```

You should see `qtap-xxxx` pods in the Running state — one per node (DaemonSet).

2. Check the logs:

```bash
kubectl logs -n qpoint daemonset/qtap --tail 30
```

Look for a successful registration message indicating the agent has connected to Qplane.

3. Confirm the agent appears in the [Qplane dashboard](https://app.qpoint.io) under your environment.

## Example Manifest

Here's a complete manifest for cloud-connected deployment with a registration token secret. You can use this directly instead of generating from Helm:

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: qtap
  namespace: qpoint
  labels:
    app.kubernetes.io/name: qtap
    app.kubernetes.io/instance: qtap
automountServiceAccountToken: true
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: qtap
  namespace: qpoint
  labels:
    app.kubernetes.io/name: qtap
    app.kubernetes.io/instance: qtap
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: qtap
      app.kubernetes.io/instance: qtap
  template:
    metadata:
      labels:
        app.kubernetes.io/name: qtap
        app.kubernetes.io/instance: qtap
    spec:
      hostPID: true
      hostNetwork: true
      serviceAccountName: qtap
      containers:
        - name: qtap
          image: "us-docker.pkg.dev/qpoint-edge/public/qtap:v0"
          imagePullPolicy: IfNotPresent
          securityContext:
            privileged: true
            allowPrivilegeEscalation: true
            runAsUser: 0
            runAsGroup: 0
            runAsNonRoot: false
            readOnlyRootFilesystem: false
            capabilities:
              add:
                - CAP_BPF
                - CAP_SYS_ADMIN
          env:
            - name: REGISTRATION_TOKEN
              valueFrom:
                secretKeyRef:
                  name: qtap-token
                  key: token
            - name: STATUS_LISTEN
              value: "0.0.0.0:10001"
            - name: LOG_LEVEL
              value: "warn"
            - name: LOG_ENCODING
              value: "json"
            - name: TINI_SUBREAPER
              value: "1"
          ports:
            - name: status
              containerPort: 10001
              protocol: TCP
          startupProbe:
            httpGet:
              path: /readyz
              port: status
            initialDelaySeconds: 3
            periodSeconds: 5
            timeoutSeconds: 2
            successThreshold: 1
            failureThreshold: 20
          readinessProbe:
            httpGet:
              path: /readyz
              port: status
            initialDelaySeconds: 3
            periodSeconds: 5
            timeoutSeconds: 2
            successThreshold: 1
            failureThreshold: 1
          livenessProbe:
            httpGet:
              path: /healthz
              port: status
            initialDelaySeconds: 3
            periodSeconds: 10
            timeoutSeconds: 2
            successThreshold: 1
            failureThreshold: 3
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 1000m
              memory: 1Gi
          volumeMounts:
            - name: sys
              mountPath: /sys
              readOnly: true
            - name: containerd-socket
              mountPath: /run/containerd/containerd.sock
      volumes:
        - name: sys
          hostPath:
            path: /sys
            type: Directory
        - name: containerd-socket
          hostPath:
            path: /run/containerd/containerd.sock
            type: Socket
```

{% hint style="info" %}
No ConfigMap is needed for cloud-connected mode. Qplane pushes configuration (stacks, plugins, filters) to the agent after registration. Any changes you make in the Qplane UI are applied automatically.
{% endhint %}

## Adding Tags

Tags help you filter and organize agents in the Qplane dashboard. Add them as extra arguments in the container spec:

```yaml
          args:
            - "--tags=Environment:Production,Cluster:EKS-1"
```

See [Organizations & Environments](https://docs.qpoint.io/getting-started/configuration/organizations-and-environments#agent-tags) for when to use tags vs separate installations.

## Adding S3 Credentials

If your Qplane environment uses an S3 object store for payload storage, inject the credentials alongside the registration token:

```bash
kubectl create secret generic qpoint-s3-creds \
  --from-literal=AWS_ACCESS_KEY_ID='<access-key>' \
  --from-literal=AWS_SECRET_ACCESS_KEY='<secret-key>' \
  -n qpoint
```

Add `envFrom` to the container spec:

```yaml
          envFrom:
            - secretRef:
                name: qpoint-s3-creds
```

## Uninstalling

```bash
kubectl delete -f qtap-manifest.yaml -n qpoint
kubectl delete secret qtap-token -n qpoint
```

## Understanding the Manifest

The manifest creates the following resources:

| Resource           | Purpose                                |
| ------------------ | -------------------------------------- |
| **ServiceAccount** | Identity for Qtap pods                 |
| **DaemonSet**      | Runs Qtap on every node in the cluster |

Key DaemonSet settings:

* **`hostPID: true`** and **`hostNetwork: true`** — Required for eBPF to observe host processes and network traffic
* **Privileged security context** — Required for loading BPF programs (`CAP_BPF`, `CAP_SYS_ADMIN`)
* **`/sys` mount** — Access to kernel interfaces for eBPF
* **containerd socket** — Enables container attribution (maps traffic to container names and labels). Optional; remove if not using containerd.
* **Probes** — Startup, readiness, and liveness checks on the `/readyz` and `/healthz` endpoints

## Common Customizations

* **Node selectors or tolerations** — Target specific nodes or schedule on tainted nodes
* **Resource limits** — Adjust CPU and memory based on your traffic volume
* **Container runtime socket** — Change from containerd to CRI-O (`/var/run/crio/crio.sock`) if applicable
* **Image tag** — Pin to a specific version (e.g., `qtap:v0.17.1`) instead of the rolling `v0` tag

## Troubleshooting

1. **Pods not starting:**

```bash
kubectl describe pod -n qpoint -l app.kubernetes.io/name=qtap
```

Check for security policy violations or missing secrets.

2. **Agent not appearing in Qplane:**

```bash
kubectl logs -n qpoint daemonset/qtap --tail 50
```

Verify the registration token is correct and the cluster can reach `api.qpoint.io:443`.

3. **Kernel compatibility:**

```bash
kubectl debug node/<node-name> -it --image=ubuntu -- uname -r
```

Kernel must be 5.10 or later.
