Ship Kubernetes logs to UptimeEye

The quickest path on Kubernetes is a log agent running as a DaemonSet: it reads every container's stdout/stderr from the node, enriches each line with pod metadata and ships batches to UptimeEye. You do not change your applications.

The example uses Vector, which needs about 50 MB of memory per node and buffers to disk while the network is down. The same shape works with Fluent Bit or the OpenTelemetry Collector — see the alternatives at the end.

Endpoint: https://logs.uptimeeye.com/v1/ingest/elasticsearch/_bulk

1.Create an ingest key and store it as a Secret

Create a key of type “Log ingest” under API Keys, then put it into the namespace the agent runs in.

shell
kubectl create namespace logging
kubectl -n logging create secret generic uptimeeye-logs \
  --from-literal=key=ue_ingest_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

2.Install Vector as a DaemonSet

The Helm chart ships a kubernetes_logs source that already knows how to read container logs and join pod metadata. The remap step picks the fields you want to filter on; the sink talks the Elasticsearch bulk protocol, which UptimeEye accepts.

values.yaml
role: Agent
# Mount the Secret as files; Vector reads the key via its secret backend
# (it does not expand ${ENV} placeholders in the config).
extraVolumes:
  - name: uptimeeye-logs
    secret: { secretName: uptimeeye-logs }
extraVolumeMounts:
  - name: uptimeeye-logs
    mountPath: /etc/vector/secrets
    readOnly: true
customConfig:
  data_dir: /vector-data-dir
  secret:
    k8s:
      type: directory
      path: /etc/vector/secrets
  sources:
    k8s_logs:
      type: kubernetes_logs
  transforms:
    enrich:
      type: remap
      inputs: [k8s_logs]
      source: |
        .service   = .kubernetes.container_name
        .namespace = .kubernetes.pod_namespace
        .pod       = .kubernetes.pod_name
        .host      = .kubernetes.pod_node_name
        .env       = "prod"
        # Applications that log JSON: lift their fields to the top level.
        parsed, err = parse_json(string!(.message))
        if err == null { . = merge(., parsed) }
        del(.kubernetes); del(.file); del(.source_type)
  sinks:
    uptimeeye:
      type: elasticsearch
      inputs: [enrich]
      endpoints: ["https://logs.uptimeeye.com/v1/ingest/elasticsearch/"]
      api_version: v8
      compression: gzip
      healthcheck: { enabled: false }
      request:
        headers:
          Authorization: "Bearer SECRET[k8s.key]"
      query:
        _msg_field: message
        _time_field: timestamp
        _stream_fields: namespace,service,env
      buffer: { type: disk, max_size: 268435488 }

3.Deploy

shell
helm repo add vector https://helm.vector.dev
helm upgrade --install vector vector/vector -n logging -f values.yaml
kubectl -n logging logs ds/vector --tail=20   # look for "Healthcheck disabled" and no 4xx

4.Search

Within a few seconds every pod appears under its container name. Try namespace:=payments level:error or open the fields panel and click a value.

Fields you get

These show up in the fields panel and can be used in every filter:

  • service (container name)
  • namespace
  • pod
  • host (node)
  • env
  • any JSON field your application logs

Tips

  • Keep stream fields to a handful of low-cardinality values (namespace, service, env). pod changes on every rollout — keep it as a normal field.
  • Exclude noisy namespaces with extra_namespace_label_selector or a filter transform (.namespace != "kube-system").
  • Fluent Bit alternative: use the kubernetes filter and the http output from the Fluent Bit guide with URI /v1/ingest/jsonline?_msg_field=log&_stream_fields=kubernetes.namespace_name,kubernetes.container_name.
  • OpenTelemetry alternative: the Collector's filelog receiver + k8sattributes processor, exporting with otlphttp to /v1/ingest/otlp/v1/logs.

FAQ

Do I need to change my applications?
No. The agent reads stdout/stderr. If your apps already log JSON, the parse_json step turns their keys into searchable fields; plain-text lines stay as the message.
How much does the agent cost in resources?
Vector as a DaemonSet typically needs 30–80 MB memory and a few percent of one core per node. The disk buffer (256 MB in the example) keeps logs while UptimeEye is unreachable.
Where do I get the ingest key?
In the app under API Keys → New API Key → type “Log ingest”. The key starts with ue_ingest_ and is shown once. Management keys (ue_live_) are refused by the ingest endpoint.
How do I check that logs arrive?
Open Logs in the app, pick the 15m range and search for service:=<your service>. New lines are searchable within about a second; Live tail shows them with a ~6 s delay.