Agents
The VectorFlow agent, vf-agent, runs Vector on a host. It enrolls into one
environment with a one-time token, then polls VectorFlow for the pipelines
deployed there, runs each one as its own Vector process, and reports health
back. The agent opens every connection itself, so hosts need no inbound port.
Every agent needs an enrollment token. Create one on the environment’s page in VectorFlow with Generate Token.
On the VectorFlow host
Section titled “On the VectorFlow host”The installer can run an agent next to VectorFlow. From the install directory:
veltro agent enroll(umask 077; printf '%s' '<enrollment-token>' > .suite/vf-agent/token)veltro agent enroll starts the agent, which waits until the token file
appears and then enrolls. If you already have the token, you can set
VF_ENROLLMENT_TOKEN in .env before starting the agent instead; it takes
precedence over the file. veltro down, veltro ps and veltro logs include
the agent.
On other hosts
Section titled “On other hosts”VectorFlow serves an installer that downloads the agent build matching your VectorFlow version. On Linux it installs a systemd service, and on macOS a launchd service:
curl -sSfL https://vectorflow.example.com/vectorflow/api/agent/install | \ sudo bash -s -- --url https://vectorflow.example.com/vectorflow --token <enrollment-token>Useful options:
| Option | Effect |
|---|---|
--labels "region=eu,role=web" |
Node labels, which deploys can target |
--user vfagent |
Run the agent as a non-root user on Linux, created if needed |
--os darwin |
Force the macOS install |
--uninstall |
Remove the agent, its configuration and its service |
Run the same command without options to upgrade an enrolled agent: it downloads with the agent’s own node token, so it keeps working after the enrollment token changes.
On an Intel Mac, install Vector 0.54.0 on PATH yourself before running the
installer. Vector has no Intel Mac build, so on VectorFlow 0.33.0 and later
the installer only fetches Vector for Apple silicon and stops otherwise.
Settings
Section titled “Settings”The agent reads its settings from the environment. The installer writes them for you.
| Variable | Default | Meaning |
|---|---|---|
VF_URL |
none, required | VectorFlow’s address, ending in /vectorflow |
VF_TOKEN |
empty | One-time enrollment token. Not needed once enrolled |
VF_TOKEN_FILE |
empty | File the agent waits for when VF_TOKEN is empty |
VF_DATA_DIR |
/var/lib/vf-agent |
Node token and pipeline configuration |
VF_POLL_INTERVAL |
5s |
How often the agent asks for new configuration |
VF_NODE_LABELS |
empty | Comma-separated key=value labels |
VF_LOG_LEVEL |
info |
debug, info, warn or error |
After enrolling, the agent stores a node token in its data directory and uses that from then on.
Pipeline isolation
Section titled “Pipeline isolation”Each pipeline’s Vector process runs in its own sandbox. A pipeline can read
its own configuration and write its own Vector data, and that’s all it gets
from the agent. It can’t read the agent’s data directory, the node token, the
agent’s environment, or another pipeline’s configuration, certificates or
data, and it can’t connect to another pipeline’s Vector API or metrics port.
That matters because pipeline configurations carry secrets, a pipeline
editor can point a file source at any path on the host, and Vector’s API
serves a pipeline’s live events to anyone who can connect.
VectorFlow also checks pipelines before they reach an agent. It refuses to
save a component whose path could reach the agent’s data or config
directory, Vector’s data directory or system secret files
(file_source_protected_path), and only team admins may add exec sources,
Lua transforms or file sinks. The paths it refuses are listed in
agent-protected-paths.ts.
The sandbox still matters for what those checks can’t see: symlinks, paths an
event fills in at runtime, and agents with a custom data directory.
On Linux and macOS every pipeline gets its own uid and gid from a reserved
range (50000-59999 by default). The agent remembers which pipeline has which
id in pipeline-ids.json in its data directory, so a pipeline keeps its id,
and its file checkpoints and disk buffers, across restarts. The data directory
is laid out like this:
| Path | Owner | Mode |
|---|---|---|
<data dir> and its pipelines/, certs/, vector-data/ |
agent, group vf-pipeline |
0710: pipelines can pass through but not list it |
node-token, pipeline-ids.json and other agent files |
agent | 0600 |
pipelines/<id>.yaml |
agent, group of that pipeline | 0640 |
certs/<id>/ |
agent, group of that pipeline | 0750, files 0640 |
vector-data/<id>/ |
that pipeline’s uid | 0700 |
Pipelines hold no capabilities, so they can’t bypass file permissions. The one
exception is CAP_NET_BIND_SERVICE, kept when the agent has it, so a syslog
source can still listen on port 514. The installer writes the environment file
/etc/vectorflow/agent.env with mode 0600.
On Windows the agent runs Vector with a restricted copy of its own token. The
restricted token has no privileges such as backup or debug, and only reaches
what ordinary users can reach plus the files granted to that one pipeline. The
agent limits its data folder to SYSTEM and Administrators. Machine-wide
environment variables are readable by every local process, so the installer
keeps the enrollment token in a file in the data folder (VF_TOKEN_FILE)
instead, and an agent that finds an old VF_TOKEN machine variable removes it
once it’s enrolled.
On macOS the agent isolates pipelines the same way as on Linux, with the same
ids and data directory layout, when it runs as root: the LaunchDaemon that
sudo install.sh sets up. An agent installed without sudo runs as a
LaunchAgent, can’t switch uids, and reports unavailable. The installer
creates the vf-pipeline group and writes the launchd plist, which carries
the enrollment token, with mode 0600. launchd doesn’t read
/etc/vectorflow/agent.env, so the installer copies every setting in it into
the plist: after editing agent.env, rerun the installer.
Every agent reports its state in two node labels. vf.io/pipeline-isolation
is uid (Linux and macOS), restricted-token (Windows), unavailable or
off. vf.io/pipeline-api-isolation says whether pipelines’ Vector API and
metrics ports are closed to other pipelines: nftables (Linux), pf (macOS),
wfp (Windows), unavailable or off.
| Variable | Default | Meaning |
|---|---|---|
VF_PIPELINE_ISOLATION |
auto |
auto isolates pipelines when the agent can. When it can’t, it runs them as the agent (or with open API ports), logs PIPELINE ISOLATION UNAVAILABLE (or PIPELINE API ISOLATION UNAVAILABLE) with the reason and reports unavailable. required refuses to start pipelines it can’t isolate. off turns isolation off |
VF_PIPELINE_ID_RANGE |
50000-59999 |
uid/gid range for pipelines. Keep it clear of real users and groups; the agent skips ids already in use |
VF_PIPELINE_GROUP |
vf-pipeline |
Group every pipeline belongs to. When it doesn’t exist the agent reserves the first id of the range for it |
VF_PIPELINE_EXTRA_GROUPS |
adm,systemd-journal |
Extra groups for every pipeline, as names or gids. Groups that don’t exist are skipped, and so are gids inside the pipeline id range and the disk, kmem, mem, shadow, sudo, wheel, admin, lxd, incus, incus-admin and libvirt groups, plus operator, procmod and procview on macOS. From VectorFlow 0.33.0 the agent also skips docker and any group that can open the Docker socket, here and in VF_PIPELINE_GROUP, and logs a warning |
Reading host logs
Section titled “Reading host logs”Pipelines can read whatever an ordinary user can, plus files open to their
groups: adm (most of /var/log on Debian and Ubuntu) and systemd-journal.
Pipelines only get groups you configure; nothing in a pipeline’s
configuration adds one. For a log only root can read, grant it to the
vf-pipeline group:
sudo setfacl -m g:vf-pipeline:r /var/log/secureRotation creates new files, so make the grant stick there too: set a default
ACL on the directory (setfacl -d -m g:vf-pipeline:r /var/log/app), or set
the group in the create line of the logrotate config or in rsyslog’s
$FileGroup. To give pipelines a whole group instead, add it to
VF_PIPELINE_EXTRA_GROUPS.
On macOS, grant a log file to vf-pipeline with an ACL:
sudo chmod +a "group:vf-pipeline allow read" /var/log/system.logFrom VectorFlow 0.33.0, a docker_logs source doesn’t need the docker
group. On Linux and macOS the agent runs a read-only Docker API proxy for
each pipeline with a docker_logs source. Only that pipeline can open it,
and it only shows the containers the source selects. Calls docker_logs
doesn’t make, such as starting a container or exec, get a 403. The agent
itself still has to open the socket: a root agent can, a container agent
needs /var/run/docker.sock mounted, and an install.sh --user agent needs
its user in the socket’s group. That makes the agent’s user root on the
host, so set VF_PIPELINE_ISOLATION=required for it too. The agent won’t
start a pipeline whose docker_logs source uses ${...} variables anywhere.
On Windows docker_logs uses Docker’s named pipe, and nothing changes.
On VectorFlow 0.32.x a docker_logs source needs the docker group in
VF_PIPELINE_EXTRA_GROUPS, and every pipeline gets it. That group is root on
the host, so upgrade to 0.33.0 if you can.
Reading container log files from /var/lib/docker/containers with a file
source needs only read access to those files.
Vector API and metrics ports
Section titled “Vector API and metrics ports”Each pipeline’s Vector serves its API (live taps and samples) and its metrics on loopback ports. Vector’s API has no authentication, so the agent firewalls those ports by who’s connecting, and only the agent’s own processes get through:
- Linux: an nftables table of the agent’s own, matching the connecting
socket’s uid. It needs
CAP_NET_ADMIN, which root agents have. - macOS: pf rules matching the connecting socket’s uid, in an anchor under
com.applethat the stock/etc/pf.confevaluates. The agent turns pf on with a reference (pfctl -E), releases the one its previous run took, and loads/etc/pf.confwhen pf has no rules loaded at all. It checks that pf applies the rules by connecting asnobodyto a port it guards, which must be refused, and to one it doesn’t, which must answer. pf turned off, a main ruleset withoutanchor "com.apple/*" all,set skip on lo0or an earlier rule that passes loopback traffic all leave the ports open, and the agent reportsunavailable. - Windows: Windows Filtering Platform filters matching the connecting token.
On Linux and macOS the agent checks its rules every 30 seconds and before it
starts a pipeline, and puts them back if a firewall reload removed them. The
label says unavailable while it can’t, and with
VF_PIPELINE_ISOLATION=required no pipeline starts until it can. Vector’s
own log output goes to the pipeline’s logs in VectorFlow, not to the agent’s
log or the journal.
Pipelines still share the host network: anything else listening on the host is as reachable from a pipeline as from any local user.
Non-root agents
Section titled “Non-root agents”A root agent needs nothing extra. An agent installed with install.sh --user
runs as that user and gets just the capabilities it needs to start pipelines
under their own ids, through its systemd unit:
AmbientCapabilities=CAP_SETUID CAP_SETGID CAP_CHOWN CAP_FOWNER CAP_KILL CAP_NET_ADMINCapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_CHOWN CAP_FOWNER CAP_KILL CAP_NET_ADMINThe agent clears these from what it passes on, so pipelines don’t inherit
them. The container image does the same for VF_AGENT_USER. If you drop one
of these capabilities from the container, the agent starts without
isolation (or without API isolation, for NET_ADMIN) and says so in its log
and node labels.
Kubernetes
Section titled “Kubernetes”The chart runs the agent as root inside the pod with the runtime’s default
capabilities, and no longer adds DAC_READ_SEARCH: a pipeline holding it
could read any file on the node. Pod logs under /var/log/pods are
root:root 0640, so the chart gives pipelines gid 0 as an extra group
(pipelineIsolation.extraGroups: "0,adm") for kubernetes_logs and file
sources. The agent’s own files have no group permissions, so gid 0 doesn’t
open them.
Upgrading
Section titled “Upgrading”An enrolled agent moves to this layout by itself the first time the new version starts, whether it upgrades itself or you rerun the installer. It tightens the permissions above, gives each pipeline an id, and hands each pipeline’s existing Vector data to that id, so file checkpoints and disk buffers carry over.
- Root agents (the installer’s default, the container image’s default, the Helm chart and the agent bundled with Veltro): nothing to do.
install.sh --useragents: an agent that updates itself keeps its old systemd unit, which doesn’t grant the capabilities. It keeps running its pipelines unisolated and reportsunavailable. Rerun the installer with the same--userto rewrite the unit, or add the two lines above in a drop-in (systemctl edit vf-agent) and restart the service.- Custom setups: a unit or container that runs the agent as a non-root user needs the same six capabilities as ambient capabilities.
- Helm: upgrading the chart removes
DAC_READ_SEARCHand adds thepipelineIsolationvalues. If you overrodesecurityContext.capabilities, make sure you aren’t droppingSETUID,SETGID,CHOWN,FOWNERorKILL. - Windows: the agent restricts its data folder at startup. Rerun
install.ps1to move the enrollment token out of the machine environment, or let the agent remove it after enrollment. - macOS: a root agent moves to this layout like a Linux one. Rerun the
installer to create the
vf-pipelinegroup and rewrite the launchd plist with mode0600: an agent that updates itself keeps its old plist, which any local user can read. An agent installed withoutsudokeeps running pipelines as itself; reinstall withsudoto isolate them.
Pipelines that read files only the agent could read before will now log
Permission denied. Grant those files to vf-pipeline as shown above.
Pipelines with a docker_logs source stop reading container logs on 0.32.x
unless you add the docker group. On 0.33.0, remove the docker gid from
VF_PIPELINE_EXTRA_GROUPS. The agent skips it anyway and proxies the socket,
as described under Reading host logs.
Rotate the node token after upgrading
Section titled “Rotate the node token after upgrading”Until pipeline isolation, every pipeline could read the agent’s node token, so treat each agent’s token as exposed and rotate it once the agent runs the new version. Certificates and keys delivered to pipelines before the upgrade were readable the same way; rotate any you’re worried about. The agent can’t rotate its own token, so do it per node:
- Open the node in Fleet and choose Rotate token. VectorFlow shows the new token once. On VectorFlow 0.33.0 and later the old one keeps working until the agent first uses the new one, or for 24 hours at most, and the node page shows the rotation as pending until then. On 0.32.1 and 0.32.2 the old one stops after five minutes.
- Replace the
node-tokenfile in the agent’s data directory with the new token and restart the agent. Delete the old file before writing the new one, so a hard link to the old file can’t end up holding the new token.
Rotating again before the agent switches replaces only the unused token. It doesn’t push back the old token’s deadline. If a token has leaked, revoke it and re-enrol the agent. Revoking ends both tokens at once, while a rotation can leave the old one working for up to 24 hours. On VectorFlow 0.33.1 and later, revoking also closes the agent’s open push stream.
Linux, installed with install.sh:
sudo systemctl stop vf-agentsudo rm /var/lib/vf-agent/node-token# Paste the token, press Enter, then Ctrl-D.sudo sh -c 'umask 077; cat > /var/lib/vf-agent/node-token'# Only for install.sh --user agents:sudo chown <agent user> /var/lib/vf-agent/node-tokensudo systemctl start vf-agentWindows (PowerShell as Administrator):
Stop-Service vf-agent$token = "$env:ProgramData\Veltro\vf-agent\node-token"Remove-Item $tokenRead-Host "New node token" | Set-Content -NoNewline $tokenStart-Service vf-agentContainers and Kubernetes keep the file in the agent’s data volume
(/var/lib/vf-agent, or the chart’s config.dataDir). Replace it the same
way from a shell in the container, for example
kubectl exec -it <agent pod> -- sh -c 'rm /var/lib/vf-agent/node-token; umask 077; cat > /var/lib/vf-agent/node-token',
then restart the container or delete the pod.
If the old token runs out before the agent has the new one, the agent can’t authenticate. Write the new token as above and restart it; the new token stays valid.
OpenTelemetry collectors
Section titled “OpenTelemetry collectors”An OpenTelemetry collector can join the fleet instead of vf-agent. It talks
OpAMP to VectorFlow over HTTP at /vectorflow/api/opamp and signs in with the
environment’s enrollment token as a bearer token. It shows up in Fleet as an
OTel node.
With only the collector’s opamp extension, the collector enrolls and reports
its health, version and effective config, but VectorFlow can’t change its
config. Give each collector’s extension its own fixed instance_uid (a UUIDv7).
Without one the extension makes a new ID on every start, and each restart
enrolls as a new node. Collectors sharing an ID show up as one node.
extensions: opamp: instance_uid: <uuidv7> server: http: endpoint: https://vectorflow.example.com/vectorflow/api/opamp headers: Authorization: Bearer <enrollment-token>
service: extensions: [opamp]For VectorFlow to deliver the config, run the collector under the OpAMP
supervisor (opampsupervisor) instead. The supervisor connects to VectorFlow
and keeps its instance ID in its storage directory, so leave the opamp
extension out of the collector’s own config:
server: endpoint: https://vectorflow.example.com/vectorflow/api/opamp headers: Authorization: Bearer <enrollment-token>capabilities: accepts_remote_config: true reports_effective_config: true reports_health: trueagent: executable: /usr/local/bin/otelcol-contribstorage: directory: /var/lib/opampsupervisorThe collector’s config is one deployed pipeline tagged otel whose node
selector matches the collector’s labels. VectorFlow sends one pipeline per
collector, so if two match, the collector gets only one of them.
Fleet actions work differently for collectors:
| Action | vf-agent | OTel collector |
|---|---|---|
| Status, uptime and status history | Yes | Yes, from the health the collector reports. A collector that sends nothing for 90 seconds, or the fleet threshold if longer, is marked unreachable. Keep its OpAMP polling interval under that |
| Labels | Yes | Yes |
| Deploying pipelines | Yes | Yes, as remote config, under the supervisor |
| Maintenance mode | Stops the node’s pipelines | Holds config changes. The collector keeps running its current config |
| Upgrade | VectorFlow updates the agent | Use the host’s package manager or image. Fleet shows the new version when the collector reconnects |
| Host metrics and node logs | Yes | No. They come from vf-agent heartbeats |
| Rotate or revoke the node token | Yes | No. Collectors have no node token; rotate the environment’s enrollment token to cut them off |
| Delete | Removes the node | Removes the node. A collector that’s still running enrolls again on its next message |
VectorFlow doesn’t offer OpAMP packages, so it can’t upgrade a collector for
you. No upstream collector can install one yet: the OpAMP supervisor refuses to
start with accepts_packages turned on
(opentelemetry-collector-contrib#47272),
its package signature verifier only supports none, and the opamp
extension doesn’t accept packages at all.