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_logskafka,nats,pulsar,aws_sqshttp_server(receive over HTTP),opentelemetry(OTLP),vector(other Vector instances)prometheus_scrape,prometheus_remote_writepostgresql_metrics,mongodb_metrics,redis,mysql_metricsinternal_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
vectorsink. - Aggregator in the cluster — receives via the
vectorsource; 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).