Skip to content

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.

The installer can run an agent next to VectorFlow. From the install directory:

Terminal window
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.

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:

Terminal window
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.

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.

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

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:

Terminal window
sudo setfacl -m g:vf-pipeline:r /var/log/secure

Rotation 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:

Terminal window
sudo chmod +a "group:vf-pipeline allow read" /var/log/system.log

From 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.

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.apple that the stock /etc/pf.conf evaluates. The agent turns pf on with a reference (pfctl -E), releases the one its previous run took, and loads /etc/pf.conf when pf has no rules loaded at all. It checks that pf applies the rules by connecting as nobody to a port it guards, which must be refused, and to one it doesn’t, which must answer. pf turned off, a main ruleset without anchor "com.apple/*" all, set skip on lo0 or an earlier rule that passes loopback traffic all leave the ports open, and the agent reports unavailable.
  • 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.

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_ADMIN
CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_CHOWN CAP_FOWNER CAP_KILL CAP_NET_ADMIN

The 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.

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.

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 --user agents: an agent that updates itself keeps its old systemd unit, which doesn’t grant the capabilities. It keeps running its pipelines unisolated and reports unavailable. Rerun the installer with the same --user to 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_SEARCH and adds the pipelineIsolation values. If you overrode securityContext.capabilities, make sure you aren’t dropping SETUID, SETGID, CHOWN, FOWNER or KILL.
  • Windows: the agent restricts its data folder at startup. Rerun install.ps1 to 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-pipeline group and rewrite the launchd plist with mode 0600: an agent that updates itself keeps its old plist, which any local user can read. An agent installed without sudo keeps running pipelines as itself; reinstall with sudo to 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.

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:

  1. 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.
  2. Replace the node-token file 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:

Terminal window
sudo systemctl stop vf-agent
sudo 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-token
sudo systemctl start vf-agent

Windows (PowerShell as Administrator):

Terminal window
Stop-Service vf-agent
$token = "$env:ProgramData\Veltro\vf-agent\node-token"
Remove-Item $token
Read-Host "New node token" | Set-Content -NoNewline $token
Start-Service vf-agent

Containers 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.

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: true
agent:
executable: /usr/local/bin/otelcol-contrib
storage:
directory: /var/lib/opampsupervisor

The 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.