Documentation
Agents
The agent is the only part of Provibr that runs on your own hardware. It holds the credentials for the systems you connect, carries out the work the panel asks for, and reports back what it sees. The platform never dials in.
What an agent is
One agent covers one network: the place from which your hypervisors and panels are reachable. Everything Provibr does to your infrastructure passes through it.
- The connection is outbound. The agent opens it to the platform and keeps it open; nothing has to be reachable from the internet, and no port has to be forwarded to it.
- Credentials live on your side. They are pushed to the agent once and sealed in an encrypted vault on that host; the platform stores only the names of the fields you filled in.
- It reports what it observes. Machines are polled on a short rhythm and a full comparison runs every few minutes, which is how the panel notices a machine that was stopped outside Provibr.
- It only touches what you handed over. A resource with no record in Provibr is ignored, counted and forgotten again. The agent never adopts a machine on its own.
The host it needs
Nothing exotic, and nothing to install first. The agent is one statically linked binary: it carries its own C library, so there is no runtime, no interpreter and no distribution package that has to match. A small virtual machine is enough. The sizes we measured are further down this page.
| Requirement | What you need | Why |
|---|---|---|
| Processor | 64-bit x86 (x86_64, also called amd64) | We publish one Linux build and this is the architecture it is built for. The installer checks uname -m before it downloads anything and stops on any other architecture, rather than installing something that cannot run. There is no ARM build yet. |
| Operating system | Any Linux distribution | Nothing links against your system libraries, so the distribution, its version and its package manager make no difference. The next section lists the ones this build was actually started on. |
| Service manager | systemd, or your own | The installer writes a systemd unit and enables it. On a host without systemd it still installs and enrols everything, and then prints the command to start the agent under whatever you use instead. Half an installation would be worse than an honest one. |
| Rights while installing | root, once | Installing creates the provibr-agent service account, two directories and the unit. After that the agent runs as that unprivileged account, and the update script it is not allowed to write to is what keeps it that way. |
| Outbound connections | TCP 50051 and 50052 to gw.provibr.net | Port 50051 is used once, while enrolling. Port 50052 carries the session and stays open. Both are TLS. If your firewall filters outgoing traffic, those two rules are all it has to allow. |
| Inbound connections | None | The agent opens the connection itself, so nothing has to be reachable from the internet and no port has to be forwarded to it. Installing machines over the network is the one exception, and even then it only listens on the local segment. |
| Reach into your own network | To the systems you connect | Your hypervisor, control panel or game panel is spoken to over its own API, from this host. What the agent cannot reach, Provibr cannot manage. |
| Memory | 512 MB is plenty | We measured 18 MB with a thousand machines spread over five systems, which is the heaviest case in the table below. The peak is somewhere else: unlocking the licence file while enrolling briefly takes about 70 MB, because that step is deliberately expensive to brute-force. |
| Disk | 1 GB is plenty | The binary is 17 MB and everything the agent keeps beside it stayed under 40 kB in our measurements. Installing machines over the network is the exception: it caches boot media, up to 20 GB by default. |
| Clock | Roughly right | Certificates and licence files carry validity dates and both ends check them, so a host whose clock is days out cannot enrol. Any NTP client will do. |
Which Linux
There is no package to install and no distribution to match, so what follows is not a list of supported systems. It is the list of systems this build was actually started on. Anything else with a 64-bit x86 Linux kernel should behave the same, and if it does not we would like to hear about it.
| System | Version | Notes |
|---|---|---|
| Debian | 13 (Trixie) | |
| Debian | 12 (Bookworm) | The distribution the full test ran on: installed, enrolled, connected and polling. |
| Ubuntu | 24.04 LTS | Ubuntu 24.04 is where the kernel began restricting exactly the namespaces that automations need. The agent ships an AppArmor profile for it; everything else works untouched. |
| Ubuntu | 22.04 LTS | |
| AlmaLinux | 9 | |
| AlmaLinux | 8 | A far older C library than the one this build was made on. It makes no difference, because the agent brings its own. |
| Fedora | 42 | |
| openSUSE Leap | 15 | |
| Amazon Linux | 2023 | |
| Alpine Linux | 3.21 | musl and no glibc at all, the sharpest test there is for a statically linked binary. |
How much machine
The agent itself is small and stays small. What grows is the inventory it watches: every twenty seconds it reads the full list of machines from each connected system, compares it with the round before and forwards only what changed. Every five minutes it forwards the whole picture instead, which is the safety net for anything a lost message would otherwise have cost, and that is also the round in which it asks your nodes how much capacity they have.
| Situation | Memory | Processor |
|---|---|---|
| Connected, nothing attached yet | 8 MB | 0.02% of one core |
| One system, 10 machines | 12 MB | 0.03% |
| One system, 250 machines | 14 MB | 0.06% |
| One system, 1,000 machines | 15 MB | 0.14% |
| Five systems, 200 machines each | 18 MB | 0.19% |
Traffic is the other number worth having. Those thousand machines cost about 250 kB every twenty seconds towards your own systems, roughly a gigabyte a day on your own network, while ten machines cost 2.5 kB a round. Of that thousand-machine figure about 17 kB a round travels on to us, some 70 MB a day, because only what changed is forwarded.
Does the kind of system matter? Barely. Whether the agent is talking to a hypervisor, a hosting control panel or a game panel, the work is the same: one call per system per round, one comparison, one message. Two differences are real but small: a hypervisor is also asked for the addresses inside its guests, at most once every five minutes per running machine, and a panel that measures nothing sends no usage figures. What differs enormously is the machine on the other side, and that one is sized by what it runs, not by the agent.
The sizes below are therefore not minimums we measured; the agent fits in far less than this. They are what we would order, because an operating system, its logs and your own tooling want more room than the agent does.
| What it has to handle | vCPU | Memory | Disk |
|---|---|---|---|
| Up to a few hundred machines | 1 | 1 GB | 10 GB |
| Up to a few thousand machines | 2 | 2 GB | 10 GB |
| Also installing machines over the network | 2 | 2 GB | 40 GB |
What the extras ask for
Everything above is what an agent needs in order to watch and control your systems. Four of its abilities are worth a word on top of that, and each of them degrades honestly: leave a requirement unmet and the agent keeps doing the rest, and tells you which ability it is not offering.
- Installing machines over the network
- This one needs the agent to sit on the same network segment as the machine being installed, because it answers that machine's boot request directly. Its unit already carries the two capabilities it needs for the boot ports; they are granted narrowly and only used while an installation is running. What it does add is disk: boot media are cached on the host, up to 20 GB by default, oldest thrown out first.
- Running automations
- Automations are your own TypeScript, so they run inside a sandbox built from kernel namespaces, and the runtime for that is not part of the agent: one pinned version of Bun has to sit in the agent's root-owned directory, where the account the agent runs as cannot replace it. Ubuntu 24.04 and later restrict exactly the namespace this needs; the agent ships an AppArmor profile that gives that one permission back for that one binary. If either piece is missing the agent says so at startup, does not offer the ability and carries on. Each run gets at least 4 GB of address space. That is virtual, not memory that has to exist. What a running automation really costs on your host is something we have not measured.
- Restarting the host from the panel
- Restarting is an authorisation question rather than a privilege one: the agent asks logind, logind asks polkit, and the installer leaves behind a rule that permits exactly two actions for exactly this account. Hosts installed before that rule existed need the installer run once more, because updating the agent from the panel replaces the binary and nothing in /etc. Until then the panel refuses and says why.
- Console and diagnostics
- Nothing to install. Ping and traceroute use an ICMP socket that needs no privileges as long as the host allows it for the service account's group, which it did on every host we measured; where it does not, the agent measures with TCP instead and tells you which method it used. The console is relayed over the connection that is already open.
Installing with the one-liner
On the agent's settings tab the panel builds a single command with an install link in it. Run it as root on the host. The link works once: it stops working as soon as the agent has enrolled, and after 72 hours at the latest. You can make a new one at any time.
curl -fsSL https://app.provibr.com/api/install/<token> | shWhat the command does, in order:
- Checks that it is running as root on a 64-bit x86 host and picks curl or wget, whichever is present.
- Downloads the binary and its checksum and verifies them. If the host has no tool to compute a checksum at all, it stops rather than skipping the check.
- Creates the service account and the two directories, installs the binary, and puts the update script and its public key in place, both owned by root, so the account the agent runs as cannot replace them.
- Asks for the activation code on the terminal, fetches the licence file with that code as a request header, and installs it.
- Enrols as the service account, then installs the systemd unit and starts the service.
Check the result on the host:
systemctl status provibr-agent
journalctl -u provibr-agent -fInstalling by hand
Everything the one-liner does is also a handful of commands, and the panel prints them for you with the current values filled in. This is the shape of it.
Prepare the host: the service account, the directories and the binary.
shell# System user without a shell and without a home directory id -u provibr-agent >/dev/null 2>&1 || \ sudo useradd --system --no-create-home --shell /usr/sbin/nologin provibr-agent # Directories for configuration (license, key) and state (identity, vault) sudo install -d -o provibr-agent -g provibr-agent -m 0750 /etc/provibr-agent sudo install -d -o provibr-agent -g provibr-agent -m 0700 /var/lib/provibr-agent # The binary from the download sudo install -o root -g root -m 0755 ./provibr-agent-linux-x86_64 /usr/local/bin/provibr-agent /usr/local/bin/provibr-agent versionPut the licence file in place. You download it from the agent page; it is encrypted with the activation code, and it can be downloaded once per token.
shellsudo install -o root -g provibr-agent -m 0640 \ ./provibr-<agent>.plic /etc/provibr-agent/license.plicEnrol as the service account, with the activation code.
shellsudo -u provibr-agent /usr/local/bin/provibr-agent enroll \ --license /etc/provibr-agent/license.plic --code XXXXX-XXXXXInstall the unit and start the service.
shellsudo install -o root -g root -m 0644 provibr-agent.service /etc/systemd/system/provibr-agent.service sudo systemctl daemon-reload sudo systemctl enable --now provibr-agent
Docker and Kubernetes
The agent also runs as a container. The image holds the same signed release binary as the Linux download and nothing else: no shell and no package manager, and it runs as a user without root rights. It connects outbound only, so it needs no published ports and no service.
Enrollment happens on the first start. Give the container the installation link from the panel and the activation code; it fetches its license file, enrolls, and keeps its identity on the volume. On every later start that identity is used and the two values are no longer read.
Docker
The panel shows this command with the link and code of your agent filled in. The named volume takes its owner from the image, so the agent can write to it without a chown.
docker run -d --name provibr-agent --restart unless-stopped \
--hostname provibr-agent \
--read-only --cap-drop ALL --security-opt no-new-privileges \
-v provibr-agent-data:/var/lib/provibr-agent \
-e PROVIBR_INSTALL_URL='https://app.provibr.com/api/install/<token>' \
-e PROVIBR_ACTIVATION_CODE='XXXXX-XXXXX' \
ghcr.io/provibr/provibr-agent:latestOr the same as a Docker Compose file:
# Provibr agent — Docker Compose.
#
# The agent connects outbound to the platform; it needs no published ports.
# On the first start it enrolls itself with the installation link and the
# activation code below, and stores its identity on the volume. After that the
# two values are no longer read; the link expires 72 hours after it was made.
#
# Update: change the image tag, then `docker compose up -d`. The volume keeps
# identity and credentials.
services:
provibr-agent:
image: ghcr.io/provibr/provibr-agent:latest
container_name: provibr-agent
hostname: provibr-agent
restart: unless-stopped
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
environment:
PROVIBR_INSTALL_URL: "https://app.provibr.com/api/install/<token>"
PROVIBR_ACTIVATION_CODE: "XXXXX-XXXXX"
volumes:
- provibr-agent-data:/var/lib/provibr-agent
volumes:
provibr-agent-data:
Kubernetes
Plain manifests, no Helm: a namespace, a secret with the link and the code, a volume claim and a deployment. The panel fills in the secret for your agent.
# Provibr agent — Kubernetes (plain manifests, no Helm).
#
# kubectl apply -f provibr-agent.yaml
#
# The agent connects outbound to the platform; there is no Service and no port.
# On the first start it enrolls itself with the installation link and the
# activation code from the Secret, and stores its identity on the volume. After
# that the Secret is no longer read; the link expires 72 hours after it was made.
#
# Exactly one replica, and Recreate: one agent is one identity (one
# certificate). Two pods would be two agents fighting over it.
apiVersion: v1
kind: Namespace
metadata:
name: provibr-agent
---
apiVersion: v1
kind: Secret
metadata:
name: provibr-agent-enrollment
namespace: provibr-agent
type: Opaque
stringData:
PROVIBR_INSTALL_URL: "https://app.provibr.com/api/install/<token>"
PROVIBR_ACTIVATION_CODE: "XXXXX-XXXXX"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: provibr-agent-data
namespace: provibr-agent
spec:
# One pod, and only one, may mount this volume. On clusters older than
# Kubernetes 1.29 (or storage without it), use ReadWriteOnce instead; the
# agent also refuses to start a second time on the same data directory.
accessModes:
- ReadWriteOncePod
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: provibr-agent
namespace: provibr-agent
labels:
app.kubernetes.io/name: provibr-agent
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: provibr-agent
template:
metadata:
labels:
app.kubernetes.io/name: provibr-agent
spec:
hostname: provibr-agent
automountServiceAccountToken: false
enableServiceLinks: false
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
# Only when the volume root does not match yet; otherwise the kubelet
# widens the modes on the private keys at every mount.
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
# The ping check uses an unprivileged ICMP socket. Some container
# runtimes keep those closed, and then the check reports a source
# error. This safe sysctl opens them for the agent's group only.
sysctls:
- name: net.ipv4.ping_group_range
value: "65532 65532"
containers:
- name: agent
image: ghcr.io/provibr/provibr-agent:latest
args: ["run"]
envFrom:
# optional: the agent reads it on its first start only, so you can
# delete the Secret once it is enrolled.
- secretRef:
name: provibr-agent-enrollment
optional: true
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
memory: 256Mi
volumeMounts:
- name: data
mountPath: /var/lib/provibr-agent
volumes:
- name: data
persistentVolumeClaim:
claimName: provibr-agent-data
For Kubernetes the license file is the better route: put the license file and the code in the secret instead of the installation link, mount the file in the pod and set PROVIBR_LICENSE_FILE to its path. The token inside the file works exactly once, so the secret holds nothing that can enroll the agent a second time.
Updating a container
By default a container does not update itself: the image is the release. The panel shows which image is current; pull it and recreate the container, or set the new tag on the deployment. The volume keeps identity and credentials. Automatic updates skip these agents; the option below changes that.
docker pull ghcr.io/provibr/provibr-agent:latest
docker stop provibr-agent && docker rm provibr-agent
# then run the same docker run command again with the new imagekubectl -n provibr-agent set image deployment/provibr-agent agent=ghcr.io/provibr/provibr-agent:latestOption: update itself from the volume
Set PROVIBR_SELF_UPDATE=volume, or tick the option in the panel. The container then takes part in the update button and in the nightly updates: a new version is downloaded, its signature is checked against the platform key built into the image, and it is stored on the volume. The container stops with exit code 0, Docker or the kubelet starts it again, and the binary in the image starts the new version from the volume after checking its signature once more.
docker run -d --name provibr-agent --restart unless-stopped \
--hostname provibr-agent \
--read-only --cap-drop ALL --security-opt no-new-privileges \
-v provibr-agent-data:/var/lib/provibr-agent \
-e PROVIBR_INSTALL_URL='https://app.provibr.com/api/install/<token>' \
-e PROVIBR_ACTIVATION_CODE='XXXXX-XXXXX' \
-e PROVIBR_SELF_UPDATE=volume \
ghcr.io/provibr/provibr-agent:latestA new version has 60 seconds to reach the platform. If it does not, it is marked as bad on the volume and never started again, and the container comes back with the previous version. When you roll out a newer image, the image wins: what was stored on the volume for the old image is no longer used.
Option: the tools image for automations
The standard image has no automation runtime. The tools image, with the tag suffix -tools, holds the same signed agent on Debian with the pinned automation runtime added, so automations can run in the container. The Docker examples give it a small /tmp and a pids limit (--pids-limit); on Kubernetes /tmp is an in-memory emptyDir, and a pids limit is set on the kubelet (podPidsLimit), not in the manifest. The root filesystem stays read-only.
docker run -d --name provibr-agent --restart unless-stopped \
--hostname provibr-agent \
--read-only --cap-drop ALL --security-opt no-new-privileges \
--tmpfs /tmp:rw,noexec,nosuid,size=16m --pids-limit 256 \
-v provibr-agent-data:/var/lib/provibr-agent \
-e PROVIBR_INSTALL_URL='https://app.provibr.com/api/install/<token>' \
-e PROVIBR_ACTIVATION_CODE='XXXXX-XXXXX' \
ghcr.io/provibr/provibr-agent:latest-toolsWhat does not work in a container
Some features need the host itself, and a locked-down container does not have it:
- Network installs (PXE): they need the host network and ports below 1024.
- Rebooting the host from the panel: the container has no access to the host's init system.
- Automations in the standard image: it has no automation runtime, and the sandbox they get on a host needs user namespaces, which a locked-down container does not grant. The tools image runs them inside the container instead.
- Host metrics: CPU, memory and uptime describe the machine the container runs on, not the container's own limits.
The activation code and the licence file
When you create an agent the panel shows an activation code once. It is never written down, not even as a hash. The code is what encrypts the licence file, so the file on your host is encrypted with something that is not on that host.
The licence file itself carries no secret about your infrastructure. It tells the agent which platform it belongs to, which certificate authority to trust, and the one-time token it enrols with.
Managing agents in the panel
The agent list shows everything at once: status, version, whether it is connected right now, when it was last seen and when its certificate expires.
- Name and description are yours to change and are only labels. A name already inside a delivered licence file stays as it was, which is cosmetic and no reason to re-enrol.
- Connected or not is read from a live signal with a short expiry, not from the last time a row was written. If we cannot tell, the panel says so instead of claiming your agent is down.
- Version and operating system are what the agent reported at its last connection. An agent that never connected shows a dash rather than a guess.
- Select several agents in the list to update them in one go. The button counts only the agents that can actually be updated right now, so you see before clicking that twelve selected means three updated.
- Progress is read from the command that is running, not from something your browser remembered, so a reload picks it back up.
The three states
- Pending
- Created in the panel, not yet enrolled. It has a token and an activation code waiting for it, and no certificate yet.
- Enrolled
- It has its own certificate and may connect. This is the only state in which it can be given work, and the only state in which it cannot be deleted.
- Revoked
- Withdrawn by you. Its session is closed, outstanding tokens are void, and it may not come back.
Revoking an agent
Revoking is the switch you reach for when a host is compromised, decommissioned or simply no longer yours. It takes effect without you touching the host.
- A running session is closed within the minute. The agent is told why rather than simply dropped.
- The agent then erases its own identity, its certificate and its credential vault. The credentials for your hypervisor are gone from that host.
- Outstanding enrolment tokens are void, so a licence file that was already downloaded cannot be used to come back.
Deleting an agent
Deleting removes the agent from the panel. It is a separate step from revoking, and it is deliberately fussy about when it is allowed.
- An enrolled agent cannot be deleted. Revoke it first, because otherwise you would be removing the record of something that is still connected.
- An agent with integrations or servers attached cannot be deleted either, and the refusal names both counts. Those rows hang off the agent, so one click would take your inventory with it.
- If the agent never ran a command and never had a server, the record really disappears. Otherwise it is hidden everywhere but its command history is kept, because an audit trail you can erase by deleting its subject is not an audit trail.
Removing it from the host
Revoking clears the agent's secrets but leaves the files. To clean the host, the panel offers a second one-liner, and the same four steps by hand.
Stop the service, disable it and remove the unit.
shell# Stop the service and forget the unit sudo systemctl disable --now provibr-agent sudo rm -f /etc/systemd/system/provibr-agent.service sudo systemctl daemon-reload sudo systemctl reset-failed provibr-agent 2>/dev/null || trueRemove the binary, the update script and the update key.
shell# Binary, update script and the trust anchor of the update step sudo rm -f /usr/local/bin/provibr-agent sudo rm -f /usr/local/lib/provibr-agent/apply-update.sh /usr/local/lib/provibr-agent/license-signing.pub # The polkit rule that allowed host.reboot (and its 0.105 fallback) sudo rm -f /etc/polkit-1/rules.d/49-provibr-agent-reboot.rules /etc/polkit-1/localauthority/50-local.d/49-provibr-agent-reboot.pkla # Only if nothing else lives in there sudo rmdir /usr/local/lib/provibr-agent 2>/dev/null || trueRemove the data. This is the step that matters: the licence, the vault key and the encrypted credentials live here.
shell# License and vault key sudo rm -rf /etc/provibr-agent # Identity, encrypted credential vault, outbox and staged updates sudo rm -rf /var/lib/provibr-agentRemove the service account.
shellsudo userdel provibr-agent sudo groupdel provibr-agent 2>/dev/null || true
Afterwards, both of these should print nothing at all:
ls -d /etc/provibr-agent /var/lib/provibr-agent /usr/local/lib/provibr-agent /usr/local/bin/provibr-agent /etc/systemd/system/provibr-agent.service /etc/polkit-1/rules.d/49-provibr-agent-reboot.rules /etc/polkit-1/localauthority/50-local.d/49-provibr-agent-reboot.pkla 2>/dev/null
id provibr-agent 2>/dev/null
# Both commands should print nothing at all