TL;DR — Quick Summary

Vector is a Rust-powered observability pipeline by Datadog. Collect logs, metrics, and traces from any source and route them to any sink with VRL transforms.

Vector is a high-performance observability data pipeline written in Rust, maintained by Datadog. It collects logs, metrics, and traces from any source, transforms them with VRL (Vector Remap Language), and routes them to any sink — all in a single statically linked binary that uses a fraction of the memory consumed by Logstash or Fluentd. This guide covers the full Vector stack: architecture, installation, configuration, VRL transforms, deployment patterns, delivery guarantees, and a production pipeline that collects Nginx logs and routes them to Loki and ClickHouse.

Prerequisites

  • Linux server (Ubuntu 22.04+, Debian 12+, RHEL 9+) or macOS for local testing.
  • Docker or Kubernetes cluster (optional, for container-based deployments).
  • Basic familiarity with YAML configuration.
  • Target sinks running and accessible (Loki, Elasticsearch, ClickHouse, S3, etc.).

Vector Architecture

Vector processes observability data as a directed acyclic graph (DAG) of sources → transforms → sinks.

  • Sources — ingest data from files, journals, sockets, HTTP, Kafka, cloud APIs.
  • Transforms — parse, enrich, filter, route, aggregate, or deduplicate events.
  • Sinks — deliver data to storage, search, metrics, and alerting systems.

Under the hood, Vector achieves its throughput through several Rust-native design choices:

  • Zero-copy parsing — log lines are parsed in-place without extra heap allocations.
  • Adaptive concurrency — Vector auto-tunes the number of in-flight requests to each sink using an AIMD algorithm, preventing overload without manual tuning.
  • End-to-end acknowledgments — a log line is not deleted from its source (or buffer) until all configured sinks confirm receipt.
  • Disk buffers — events overflow to disk when the in-memory queue fills, surviving process restarts and preventing loss during sink outages.

Vector supports two event types internally: log (structured key-value map with a timestamp) and metric (named value with optional tags and a kind — counter, gauge, histogram, set).


Installation

apt (Debian/Ubuntu)

curl -1sLf 'https://repositories.timber.io/public/vector/cfg/setup/bash.deb.sh' \
  | sudo -E bash
sudo apt install -y vector
sudo systemctl enable --now vector

yum/dnf (RHEL/Fedora/Rocky)

curl -1sLf 'https://repositories.timber.io/public/vector/cfg/setup/bash.rpm.sh' \
  | sudo -E bash
sudo yum install -y vector
sudo systemctl enable --now vector

Docker

docker run -d \
  --name vector \
  -v /var/log:/var/log:ro \
  -v $(pwd)/vector.yaml:/etc/vector/vector.yaml:ro \
  timberio/vector:latest-alpine

Kubernetes DaemonSet via Helm

helm repo add vector https://helm.vector.dev
helm repo update
helm install vector vector/vector \
  --namespace vector \
  --create-namespace \
  --values vector-values.yaml

A minimal vector-values.yaml for DaemonSet mode:

role: "Agent"
customConfig:
  data_dir: /vector-data-dir
  sources:
    kubernetes_logs:
      type: kubernetes_logs
  sinks:
    loki:
      type: loki
      inputs: ["kubernetes_logs"]
      endpoint: "http://loki.monitoring:3100"
      labels:
        namespace: "{{ '{{' }}kubernetes.namespace_labels.name{{ '}}' }}"

Homebrew (macOS)

brew tap vectordotdev/brew
brew install vector

Configuration

Vector reads vector.yaml (or vector.toml, vector.json) from /etc/vector/ by default. The top-level structure:

data_dir: /var/lib/vector   # disk buffer and state storage

sources:
  my_source:
    type: file
    include: ["/var/log/nginx/*.log"]

transforms:
  my_transform:
    type: remap
    inputs: ["my_source"]
    source: |
      . = parse_json!(string!(.message))

sinks:
  my_sink:
    type: loki
    inputs: ["my_transform"]
    endpoint: "http://loki:3100"
    labels:
      job: nginx

Every component has a unique string key. inputs wires components together — a sink’s inputs lists the transform or source it reads from.


Sources Reference

SourceType keyDescription
File tailingfileTail log files with glob patterns, tracks position
systemd journaljournaldRead from journald with unit filters
Docker containersdocker_logsCollect from Docker daemon socket
Kubernetes podskubernetes_logsNative k8s log collection with pod metadata
Syslog UDP/TCPsyslogReceive RFC 3164 / RFC 5424 syslog messages
HTTP serverhttp_serverExpose an HTTP endpoint to receive pushed logs
KafkakafkaConsume from one or more Kafka topics
StatsDstatsdReceive StatsD metrics over UDP
Host metricshost_metricsCollect CPU, memory, disk, network from the host
Internal metricsinternal_metricsVector’s own throughput and error counters
sources:
  nginx_access:
    type: file
    include: ["/var/log/nginx/access.log"]
    read_from: beginning

  journal:
    type: journald
    include_units: ["nginx.service", "postgresql.service"]

  k8s:
    type: kubernetes_logs
    auto_partial_merge: true

VRL — Vector Remap Language

VRL is the transform language at Vector’s core. It is a statically typed, expression-based language designed to be fast, safe, and readable. Every VRL program receives an event as . (dot) and must return the mutated event.

Parsing JSON

transforms:
  parse_json:
    type: remap
    inputs: ["nginx_access"]
    source: |
      . = parse_json!(string!(.message))

The ! suffix on a function call means “abort the event if this fails.” Events that fail are routed to the component’s dropped output, preventing bad data from poisoning downstream sinks.

Parsing Syslog

transforms:
  parse_syslog:
    type: remap
    inputs: ["syslog_in"]
    source: |
      parsed = parse_syslog!(.message)
      .severity = parsed.severity
      .facility = parsed.facility
      .appname = parsed.appname
      .message = parsed.message

Field Access, Coercion, and String Interpolation

source: |
  .level = upcase(string!(.level))
  .response_time_ms = to_int!(.response_time) * 1000
  .url = "https://" + string!(.host) + string!(.path)
  .log_line = "{{ '{{' }}{{ '}}' }}{{ '{{' }}.appname{{ '}}' }}: {{ '{{' }}.message{{ '}}' }}"

Conditional Logic and PII Redaction

source: |
  if .status_code >= 500 {
    .severity = "error"
  } else if .status_code >= 400 {
    .severity = "warn"
  } else {
    .severity = "info"
  }

  # Redact credit card numbers from message field
  .message = redact(.message, filters: [
    { type: "pattern", pattern: r'\b(?:\d[ -]*?){13,16}\b' }
  ])

  # Remove a field entirely
  del(.internal_trace_id)

Regex Parsing for Nginx Combined Log Format

transforms:
  parse_nginx:
    type: remap
    inputs: ["nginx_access"]
    source: |
      parsed, err = parse_regex(.message, r'^(?P<remote_addr>\S+) - (?P<user>\S+) \[(?P<time>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) (?P<protocol>[^"]+)" (?P<status>\d+) (?P<bytes>\d+)')
      if err == null {
        . = merge(., parsed)
        .status = to_int!(.status)
        .bytes = to_int!(.bytes)
        del(.message)
      }

Transforms Reference

TransformPurpose
remapFull VRL transform — parse, enrich, filter, mutate
filterDrop events that don’t match a VRL condition
routeSplit stream into named lanes by condition
aggregateCombine multiple events into one (batching, windowing)
dedupeDrop duplicate events within a time window
sampleKeep a configurable percentage of events
reduceMerge a series of partial events into one complete event
metric_to_logConvert metrics to log events for debugging
log_to_metricDerive counters/gauges from log fields

Route Transform — Send Errors and Requests Separately

transforms:
  router:
    type: route
    inputs: ["parse_nginx"]
    route:
      errors: .status >= 500
      slow: .response_time_ms > 1000

sinks:
  loki_errors:
    type: loki
    inputs: ["router.errors"]
    endpoint: "http://loki:3100"
    labels:
      stream: errors

  loki_slow:
    type: loki
    inputs: ["router.slow"]
    endpoint: "http://loki:3100"
    labels:
      stream: slow_requests

  loki_all:
    type: loki
    inputs: ["router._unmatched"]
    endpoint: "http://loki:3100"
    labels:
      stream: access

Sinks Reference

SinkType keyUse Case
LokilokiGrafana log storage
Elasticsearch / OpenSearchelasticsearchFull-text search and analytics
ClickHouseclickhouseHigh-volume analytical log storage
S3 / R2aws_s3Long-term archival
KafkakafkaRe-publish to downstream consumers
Prometheus Exporterprometheus_exporterExpose metrics on /metrics
Prometheus Remote Writeprometheus_remote_writePush metrics to Mimir/Thanos
Datadog Logsdatadog_logsSend to Datadog ingestion
Splunk HECsplunk_hec_logsSend to Splunk HTTP Event Collector
ConsoleconsoleLocal debugging (stdout)
HTTPhttpGeneric webhook / custom receiver

Buffering and Delivery Guarantees

By default, Vector uses in-memory buffers (fastest, lost on crash). For production:

sinks:
  clickhouse_sink:
    type: clickhouse
    inputs: ["parse_nginx"]
    endpoint: "http://clickhouse:8123"
    database: logs
    table: nginx_access
    buffer:
      type: disk
      max_size: 268435456   # 256 MB
      when_full: block      # or "drop_newest"
    acknowledgements:
      enabled: true

With acknowledgements.enabled: true, Vector waits for the sink to confirm receipt before advancing the read position in the source. Combined with disk buffers, this achieves durable at-least-once delivery.


Unit Testing Transforms

Vector has a built-in test runner. Define tests in the same vector.yaml:

tests:
  - name: "parse_nginx parses 200 request correctly"
    inputs:
      - insert_at: parse_nginx
        type: log
        log_fields:
          message: '127.0.0.1 - - [23/Mar/2026:10:00:00 +0000] "GET /api/health HTTP/1.1" 200 42'
    outputs:
      - extract_from: parse_nginx
        conditions:
          - type: vrl
            source: |
              assert_eq!(.status, 200)
              assert_eq!(.method, "GET")
              assert_eq!(.path, "/api/health")

Run tests before deploying:

vector test /etc/vector/vector.yaml

Monitoring Vector

# Real-time component throughput (like top, but for Vector)
vector top

# Validate configuration without starting
vector validate /etc/vector/vector.yaml

# Generate a GraphViz topology diagram
vector graph /etc/vector/vector.yaml | dot -Tsvg > topology.svg

Expose Vector’s own metrics to Prometheus:

sources:
  internal_metrics:
    type: internal_metrics
    scrape_interval_secs: 15

sinks:
  prometheus:
    type: prometheus_exporter
    inputs: ["internal_metrics"]
    address: "0.0.0.0:9598"

Key metrics to alert on: vector_component_errors_total, vector_buffer_byte_size, vector_component_received_events_total, vector_component_sent_events_total.


Production Pipeline: Nginx Logs → Loki + ClickHouse

data_dir: /var/lib/vector

sources:
  nginx_raw:
    type: file
    include: ["/var/log/nginx/access.log"]
    read_from: beginning

transforms:
  parse_nginx:
    type: remap
    inputs: ["nginx_raw"]
    source: |
      parsed, err = parse_regex(.message,
        r'^(?P<remote_addr>\S+) - (?P<user>\S+) \[(?P<time_local>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) (?P<protocol>[^"]+)" (?P<status>\d+) (?P<bytes_sent>\d+) "(?P<referer>[^"]*)" "(?P<user_agent>[^"]*)"')
      if err == null {
        . = merge(., parsed)
        .status = to_int!(.status)
        .bytes_sent = to_int!(.bytes_sent)
        .timestamp = now()
        del(.message)
      }
  enrich:
    type: remap
    inputs: ["parse_nginx"]
    source: |
      .host = get_hostname!()
      if .status >= 500 {
        .level = "error"
      } else if .status >= 400 {
        .level = "warn"
      } else {
        .level = "info"
      }
  router:
    type: route
    inputs: ["enrich"]
    route:
      errors: .status >= 500

sinks:
  loki_errors:
    type: loki
    inputs: ["router.errors"]
    endpoint: "http://loki:3100"
    labels:
      job: nginx
      stream: errors
      host: "{{ '{{' }}.host{{ '}}' }}"
    buffer:
      type: disk
      max_size: 134217728
    acknowledgements:
      enabled: true

  loki_access:
    type: loki
    inputs: ["router._unmatched"]
    endpoint: "http://loki:3100"
    labels:
      job: nginx
      stream: access
    buffer:
      type: memory
      max_events: 50000

  clickhouse_access:
    type: clickhouse
    inputs: ["router._unmatched", "router.errors"]
    endpoint: "http://clickhouse:8123"
    database: logs
    table: nginx_access
    batch:
      max_bytes: 10485760
      timeout_secs: 10
    buffer:
      type: disk
      max_size: 268435456
    acknowledgements:
      enabled: true

Vector vs Alternatives

FeatureVectorFluentdFluent BitLogstashFilebeatPromtailGrafana Alloy
LanguageRustRubyCJVMGoGoGo
Memory usage~20 MB~200 MB~1 MB~512 MB~50 MB~30 MB~60 MB
ThroughputVery HighMediumHighMediumHighMediumHigh
Transform languageVRLFluentd DSLLuaRuby/GrokMinimalStagesAlloy DSL
Metrics pipelineYesPluginLimitedPluginNoNoYes
Disk buffersNativePluginYesNativeNativeNoYes
Unit testingBuilt-inNoNoNoNoNoLimited
Kubernetes nativeYesYesYesNoYesYesYes
LicenseMPL-2.0Apache 2.0Apache 2.0SSPLElasticApache 2.0Apache 2.0

Gotchas and Edge Cases

  • VRL ! vs ?parse_json! aborts the event on error (sends to dropped); parse_json? swallows the error and returns null. Choose intentionally.
  • File source position tracking — Vector stores offsets in data_dir. Deleting this directory causes re-ingestion of existing log files.
  • Kubernetes log rotation — set glob_minimum_cooldown_ms to avoid missing events during log rotation in high-throughput pods.
  • ClickHouse data types — Vector infers types from JSON; predefine your ClickHouse table schema and set skip_unknown_fields: true to avoid ingestion errors.
  • Loki label cardinality — avoid using high-cardinality fields (user IDs, request IDs) as Loki labels; use structured metadata instead.
  • Adaptive concurrency with slow sinks — if a sink has high latency, Vector will automatically reduce concurrency; set request.concurrency: fixed:N to override.

Summary

  • Vector is a Rust-based observability pipeline with a sources → transforms → sinks DAG model.
  • VRL handles all parsing, enrichment, filtering, and routing in a safe, testable language.
  • Deployment modes: agent (per host), aggregator (centralized), sidecar (Kubernetes pod).
  • Disk buffers + acknowledgments provide durable at-least-once delivery with no data loss on crashes.
  • Unit tests in vector.yaml validate transforms before production deployment.
  • vector top gives real-time per-component throughput; the internal Prometheus endpoint enables dashboarding.
  • Vector handles logs and metrics in a single pipeline, replacing Fluentd, Logstash, Filebeat, and Promtail simultaneously.