Install

# Debian / Ubuntu via official repo
curl -1sLf 'https://repositories.timber.io/public/vector/setup.deb.sh' | sudo bash
sudo apt install vector

# macOS
brew install vectordotdev/brew/vector

# Verify
vector --version

The minimum-viable config

Config in TOML at /etc/vector/vector.toml (or YAML / JSON; pick one):

# Source: read syslog
[sources.syslog]
type = "syslog"
mode = "tcp"
address = "0.0.0.0:514"

# Source: read host journald
[sources.journald]
type = "journald"

# Source: tail files
[sources.app_logs]
type = "file"
include = ["/var/log/myapp/*.log"]
read_from = "end"

# Transform: parse + add fields via VRL
[transforms.parse_app]
type = "remap"
inputs = ["app_logs"]
source = '''
. = parse_json!(.message)
.environment = "production"
.host = get_hostname!()
if .level == "DEBUG" { abort }   # drop debug-level entries
'''

# Sink: forward to VictoriaLogs (see /tutorials/victorialogs-lightweight-logs.html)
[sinks.victorialogs]
type = "elasticsearch"
inputs = ["parse_app", "syslog", "journald"]
endpoints = ["http://victorialogs:9428/insert/elasticsearch/"]
mode = "bulk"
api_version = "v8"
compression = "gzip"

# Sink: errors go to Slack too
[transforms.errors_only]
type = "filter"
inputs = ["parse_app"]
condition = '.level == "ERROR"'

[sinks.slack]
type = "http"
inputs = ["errors_only"]
uri = "https://hooks.slack.com/services/..."
encoding.codec = "json"
request.headers."Content-Type" = "application/json"

Run it

sudo systemctl enable --now vector
sudo systemctl status vector
sudo journalctl -u vector -f

# Validate config without applying
vector validate /etc/vector/vector.toml

# Run in foreground for debugging
sudo vector --config /etc/vector/vector.toml --verbose

VRL: the transform language

VRL is a strict, typed scripting language for log transformation. Errors are caught at config-load time; not at runtime. Common operations:

[transforms.example]
type = "remap"
inputs = ["source"]
source = '''
# Parse the message as JSON
parsed, err = parse_json(.message)
if err == null {
  . = parsed
}

# Drop fields
del(.password)
del(.auth_token)

# Rename
.user = .username
del(.username)

# Type coercion
.count = to_int!(.count_str)
del(.count_str)

# Time parsing
.@timestamp = parse_timestamp!(.ts, "%Y-%m-%dT%H:%M:%S%.fZ")

# Conditional
if .level == "ERROR" {
  .alert = true
}

# Regex extraction
matches = parse_regex!(.message, r'request_id=(?P<request_id>[a-f0-9]+)')
if matches != null {
  .request_id = matches.request_id
}

# IP geo lookup (when geoip-database configured)
.geo = get_enrichment_table_record!("geoip", { "ip": .client_ip })

# Drop the entire event
if .source == "noise" {
  abort
}
'''

VRL is fault-tolerant: if a transform fails on a single event, Vector logs it but continues processing. Compare to Logstash's Ruby filters which can crash the whole pipeline on a bad event.

Source list (selected)

  • journald, file, syslog, docker_logs, kubernetes_logs
  • kafka, nats, pulsar, aws_sqs
  • http_server (receive over HTTP), opentelemetry (OTLP), vector (other Vector instances)
  • prometheus_scrape, prometheus_remote_write
  • postgresql_metrics, mongodb_metrics, redis, mysql_metrics
  • internal_logs, internal_metrics — Vector observing itself

Sink list (selected)

  • Object storage: AWS S3, GCS, Azure Blob
  • Log backends: Loki, VictoriaLogs, Elasticsearch, OpenSearch, Datadog, Splunk, Sumo Logic
  • Metrics backends: Prometheus remote-write, VictoriaMetrics, InfluxDB, Datadog, AWS CloudWatch
  • Trace backends: Datadog APM (limited), OpenTelemetry OTLP
  • Messaging: Kafka, NATS, RabbitMQ, AWS Kinesis, GCP Pub/Sub
  • Generic: HTTP, console, file, syslog forward, vector

The agent + aggregator pattern

For larger fleets, run Vector in two roles:

  • Agent on each host — reads local sources (journald, container logs); sends to an aggregator via the vector sink.
  • Aggregator in the cluster — receives via the vector source; applies heavy transforms; ships to storage.

This separates "be lightweight on every host" from "be the place we centralize parsing logic." Agents stay tiny; aggregators do the work.

Kubernetes integration

# Helm chart
helm repo add vector https://helm.vector.dev
helm install vector vector/vector --values values.yaml

# values.yaml
role: Agent
service:
  enabled: true

customConfig:
  sources:
    kubernetes_logs:
      type: kubernetes_logs

  transforms:
    add_cluster:
      type: remap
      inputs: [kubernetes_logs]
      source: |
        .cluster = "prod-us-east-1"

  sinks:
    loki:
      type: loki
      inputs: [add_cluster]
      endpoint: http://loki:3100
      labels:
        app: '{{`{{ kubernetes.pod_labels.app }}`}}'
        namespace: '{{`{{ kubernetes.pod_namespace }}`}}'

The Kubernetes source enriches every log line with pod / container / namespace / label metadata automatically.

Vector vs alternatives

  • Logstash — Elastic's; JVM-based; heavy. Vector is 3-10x faster on the same workload.
  • Fluentd — Ruby; large plugin ecosystem; smaller than Logstash, larger than Vector.
  • fluent-bit — C-based; lightweight agent; less powerful transforms than Vector.
  • Promtail — Grafana's Loki shipper; tight Loki integration; less general-purpose.
  • OpenTelemetry Collector (see that tutorial) — OTel-spec-shaped; supports metrics + traces + logs in unified pipelines. Vector is more log-focused.

For pure log pipelines with heavy in-flight transformation, Vector is the right pick. For OTel-spec metrics + traces + logs, the OTel Collector. For Loki-specific log shipping with minimal config, Promtail.

When Vector shines

  • You need to drop / reshape / enrich logs before they hit storage (to save cost or improve searchability).
  • You want sources or sinks not covered by Loki / Logstash / fluent-bit.
  • You're moving from Logstash and want a lighter Rust-based replacement.
  • VRL's safety + performance properties matter (high event volumes; pipeline must not crash on bad data).