Installing inping agents and runners
This guide takes you from an empty server to data in inping, one chapter per system. It is written so that a tester needs nothing else: every chapter has the prerequisites, the exact commands, where the files land, how to check that it worked, how to update, how to uninstall, and what to look at when nothing arrives. The last chapter is a checklist for a full round trip.
Two words first:
- An agent runs on a server of yours (or your client's) and reports to inping: CPU, memory, disks and platform of the host, the packages and runtimes of the projects on it, the packages the host's package manager installed, the host's scheduled jobs and, in Docker, the services of a Compose project. It only reads. It writes to one directory of its own (token, buffer) and never to anything else.
- A runner checks websites for inping: uptime of URLs, changes of pages, new pages in sitemaps. inping runs its own runners; you add one of your own to check from inside your network or from your own location.
Throughout, <platform> is the inping you installed: https://inping.test on a local development setup,
https://www.inping.io in production. The commands below are shown with a placeholder; the install dialog in the app
shows them with the address, the token and your project ids filled in.
1. Before you start
The token. Every install needs a registration token, shown exactly once and valid for 24 hours:
- Agent: Agents → Add agent (or, inside a project, Servers → Add agent – then the command already names that project). An existing agent gets a new token with New registration token at the bottom of its page.
- Runner: Runners → Add runner (your team's runners) or Project → Runners → Add runner (a runner for one project), both as a team admin. A new token for an existing runner is in its row's menu.
The dialog that shows the token also shows the install command for every system, with the token in it. Copy it from there – a click on the command copies it. The token never has to be typed.
Project ids. An agent's command carries --project <id>=<path> for every project it serves: the project's id
(from Project → Settings) and the directory of its code on that server. You can add or change projects later in
the agent's configuration file; assign them to the agent under Agents → (agent) → Settings → Projects.
Network. The installer and the agent or runner only make outbound HTTPS connections, to <platform> on port 443
(plain http:// is accepted for localhost only). The Docker images come from ghcr.io; the one-line scan talks
to api.osv.dev. Nothing listens on the server; no inbound port has to be opened.
Clock. Reports more than 60 seconds ahead of the platform's clock are rejected one by one. Run NTP (chronyd,
systemd-timesyncd, ntpd, the Windows Time service) before you wonder why metrics are missing.
Rights. Two ways, the dialog shows both for Linux and macOS:
- With root (sudo, or an administrator – the usual way for a server): the installer creates a service user, writes the configuration (owned by root, readable by the service user), installs the binary to a system path and sets up the system service and the update job. The service itself runs as that unprivileged user. To read a project's packages, that user needs read access to the project directory (see each chapter).
- Without root (
--user, Linux and macOS): the installer runs as your own account and touches nothing outside its home – binary, configuration, state and the service files live there, the account itself runs the agent and its update job. For a shared host, a customer's server you have no root on, a developer's machine. The agent then reads exactly what your account may read; nothing has to be granted. Details in the Debian and macOS chapters.
Windows and FreeBSD know the first way only.
Architectures. amd64 and arm64 on every system; the installer picks the right build and verifies its SHA-256
(and its minisign signature when minisign is installed – optional, the checksum comes from the signed release on
the platform).
What you will see in inping. Within one interval after the service starts (60 seconds by default):
- Agents: the agent's card shows the hostname and system instead of Not registered yet, the status turns to Online, Last seen says just now, the CPU, memory and disk meters fill.
- The agent's page, in tabs: Overview starts drawing CPU, memory and disks, and shows version, architecture and cores; Accounts lists every account of a Linux host with the CPU share and memory of its processes (from the second sample on); Packages shows the installed packages (a few seconds after start, the host inventory is read first); Jobs lists cron, systemd timers, launchd jobs or scheduled tasks.
- Project → Packages: the runtimes and packages of each project the agent serves, read at the first check after the start (then every 15 minutes, reported when something changed).
- Within a day, vulnerabilities in those packages appear on the project and on the agent's page, and as mails or
webhooks if a notification rule asks for
vulnerability.found.
2. Agent on Debian and Ubuntu (systemd)
Also for Raspberry Pi OS, Linux Mint and every other Debian-based system with systemd.
Prerequisites. curl (or wget), sudo or a root shell, outbound HTTPS, a Linux kernel of 5.13 or later for
the agent's own Landlock sandbox (older kernels run the agent too, with the systemd hardening alone).
Install. On the server, with the command from the dialog:
curl -fsSL https://<platform>/install.sh | INPING_TOKEN=<token> sudo --preserve-env=INPING_TOKEN sh -s -- --project <project-id>=/srv/shop
Repeat --project for every project on this server. The script prints what it does (inping: downloading …,
inping: checksum verified, inping: wrote /etc/inping/agent.toml, inping: running (systemctl status inping-agent, …)) and ends with inping: inping-agent <version> installed.
Read access to the projects. The service user inping-agent has to read the project directory and its lock
files (composer.lock, vendor/composer/installed.json, package-lock.json, …). Add it to the group that owns the
project – never run the agent as root:
sudo usermod -aG www-data inping-agent
sudo systemctl restart inping-agent
Network per account (optional). /proc knows no network per account, systemd's IP accounting does. Add
--ip-accounting to the install command, or run the script again with it and without a token:
curl -fsSL https://<platform>/install.sh | sudo sh -s -- --ip-accounting
The script sets DefaultIPAccounting=yes in /etc/systemd/system.conf.d/inping-ip-accounting.conf and switches it on for
the services, scopes and logins running now, without a restart. The agent itself stays read-only: it only reads the
counters with systemctl show. Accounts then shows received and sent per account: a login's slice for its account, a
service for its User=, a service that drops its rights by itself and a scope for the account with most processes in it,
so system services count too (under www-data, mysql, root).
- It counts by the unit that opened the socket. Visitors of a shared web server count for the web server's account, not the customer's whose site they request; a PHP pool in its own unit counts only its own connections (database, APIs). Several pools in one unit count for one account.
- Traffic no process of a unit owns, such as forwarded packets, is not in it. What a unit sent since the last sample before it stopped is lost.
- It needs cgroup BPF: kernel 4.15 or newer with cgroup v2, often unavailable inside containers. The script warns then.
- systemd runs a small BPF program per packet for every level of units with accounting. Look at it on a busy host before
a fleet-wide rollout.
--no-ip-accountingremoves it again, as does--uninstall.
Without root. The same script with --user, run as the account that owns (or may read) the project, no sudo:
curl -fsSL https://<platform>/install.sh | INPING_TOKEN=<token> sh -s -- --user --project <project-id>=$HOME/www/shop
Everything stays in the account's home: the binary in ~/.local/bin/inping-agent, the configuration in
~/.config/inping/agent.toml (0600, the account's own), the state in ~/.local/share/inping-agent, the units in
~/.config/systemd/user/ (inping-agent.service, inping-agent-update.service, .timer). The agent runs as your
account under the user manager of systemd and reads what your account may read – no group to join. Check with
systemctl --user status inping-agent and journalctl --user -u inping-agent; update by hand with
INPING_SERVICE_SCOPE=user ~/.local/bin/inping-agent update --config ~/.config/inping/agent.toml (the timer sets
that variable itself); remove with the install command plus --user --uninstall or --user --purge.
Two limits of the user manager. It starts at your first login and stops at your last logout, and the agent with it –
unless lingering is on for your account: sudo loginctl enable-linger <account> once (the installer tries it
itself and tells you when it could not). And the sandbox is thinner: the user manager applies NoNewPrivileges,
PrivateTmp, ProtectHome=read-only and the address families, not the kernel protections of the system unit; the
agent's own Landlock sandbox still applies on kernels 5.13 and later. Metrics are the same; cron jobs of other
accounts are not read.
Where things are.
| What | Where |
|---|---|
| Binary | /usr/local/bin/inping-agent |
| Configuration | /etc/inping/agent.toml (root:inping-agent, 0640) |
| State (token, buffer) | /var/lib/inping-agent (the only directory the agent writes to) |
| Service | /etc/systemd/system/inping-agent.service, user inping-agent |
| Update job | inping-agent-update.timer and .service (every 6 hours, as root) |
| Log | journald: journalctl -u inping-agent |
Check.
systemctl status inping-agent # active (running)
journalctl -u inping-agent -n 20 # the lines below
sudo inping-agent check # the configuration the platform handed out (interval, metrics, projects)
The log of a healthy start reads:
level=INFO msg=sandbox enforced=true detail=…
level=INFO msg=starting version=1.2.0 platform=https://www.inping.io
level=INFO msg=configuration interval_seconds=60 buffer_hours=24 metrics="[cpu memory disk containers users platform]"
level=INFO msg="host packages reported" packages=612
level=INFO msg="host jobs reported" jobs=9
level=INFO msg="inventory reported" project=01m5qxbbej7tdq8m4hhqvr9vhn components=84
In inping, see What you will see above: the agent online within a minute, host packages a few seconds after the start, the project's packages at the first check.
Update. Automatic: the timer runs inping-agent update every 6 hours (spread over an hour), which installs a
newer signed release, restarts the service and rolls back if it does not come up. By hand:
sudo inping-agent update --check # only tell
sudo inping-agent update # install now
Running the install command again (without a token) also updates and keeps configuration and registration.
Uninstall.
curl -fsSL https://<platform>/install.sh | sudo sh -s -- --uninstall # service and binary
curl -fsSL https://<platform>/install.sh | sudo sh -s -- --purge # also configuration, state and user
Then delete the agent in inping (Agents → (agent) → Settings → Delete agent); its metrics and alerts go with it.
When nothing arrives.
journalctl -u inping-agentsayscannot startorregistration failed: the token was used already, expired (24 hours) or belongs to another platform. Request a new token in inping and run the install command again with it.configuration unusable: a typo in/etc/inping/agent.toml– unknown keys are errors on purpose.- The agent runs but stays Not registered yet or Offline: outbound HTTPS to
<platform>is blocked (trycurl -sI https://<platform>/api/v1/releases/agent/latestas the agent's user), orapi_urlpoints to the wrong host, or the clock is off by more than a minute. - Metrics arrive, packages do not:
inping-agent inventoryrun as the service user (sudo -u inping-agent inping-agent inventory --config /etc/inping/agent.toml) shows what it can read; a directory it cannot enter is reported as nothing (an empty list would delete the inventory). - Host packages missing: the host has none of dpkg, rpm, apk, Homebrew or pacman, or
[host] packages = falseis set.
3. Agent on RHEL-like systems (Rocky Linux, AlmaLinux, CentOS Stream, Fedora)
Everything from the Debian chapter applies; the differences:
Prerequisites. curl is in the base system; sudo or root. SELinux may stay enforcing: the binary lives in
/usr/local/bin (type bin_t), the state in /var/lib/inping-agent (var_lib_t), both fine for a systemd
service. The agent neither listens nor writes anywhere else, so no policy module is needed. If ausearch -m avc -ts recent shows denials for inping-agent after all (a project directory with an unusual context, say), label the
directory for reading rather than switching SELinux off.
Install. The same command; firewalld allows outbound connections by default.
curl -fsSL https://<platform>/install.sh | INPING_TOKEN=<token> sudo --preserve-env=INPING_TOKEN sh -s -- --project <project-id>=/var/www/shop
Read access. The web server's group is usually apache or nginx:
sudo usermod -aG nginx inping-agent && sudo systemctl restart inping-agent
Check, update, uninstall. As on Debian (systemctl status inping-agent, journalctl -u inping-agent, the
update timer, --uninstall/--purge).
What is reported. Host packages come from rpm -qa with the distribution as OSV names it (Rocky Linux:9,
AlmaLinux:9); Fedora sends no ecosystem, so its packages are listed but not checked for advisories. Scheduled jobs:
cron and systemd timers.
4. Agent on Alpine Linux (OpenRC, no systemd)
Alpine has no systemd, BusyBox instead of GNU tools and no useradd. The installer still downloads, verifies,
installs, writes the configuration and registers the agent; it then tells you to start the service yourself. These
steps add the OpenRC service and the update job.
Prerequisites. apk add curl (BusyBox's wget does not know the --https-only the script uses), root (su -
or doas), kernel 5.13 or later for Landlock (every current Alpine).
Install. Create the service user first – the script keeps a user that exists – then run the installer:
adduser -S -D -H -h /var/lib/inping-agent -s /sbin/nologin inping-agent
curl -fsSL https://<platform>/install.sh | INPING_TOKEN=<token> sh -s -- --project <project-id>=/srv/shop
The last lines say inping: no systemd here: start it yourself as inping-agent with: /usr/local/bin/inping-agent run --config /etc/inping/agent.toml; …. Do that with OpenRC – supervise-daemon restarts the agent after a crash:
cat >/etc/init.d/inping-agent <<'EOF'
#!/sbin/openrc-run
name="inping-agent"
description="inping agent: host metrics and packages for inping"
supervisor=supervise-daemon
command="/usr/local/bin/inping-agent"
command_args="run --config /etc/inping/agent.toml"
command_user="inping-agent:inping-agent"
respawn_delay=10
output_log="/var/log/inping-agent.log"
error_log="/var/log/inping-agent.log"
depend() {
need net
after firewall
}
EOF
chmod 0755 /etc/init.d/inping-agent
touch /var/log/inping-agent.log && chown inping-agent /var/log/inping-agent.log
rc-update add inping-agent default
rc-service inping-agent start
Read access. addgroup inping-agent www-data (or whatever group owns the project), then
rc-service inping-agent restart.
Where things are. As on Debian (/usr/local/bin/inping-agent, /etc/inping/agent.toml,
/var/lib/inping-agent); the service is /etc/init.d/inping-agent, the log /var/log/inping-agent.log.
Check.
rc-service inping-agent status # started
tail -n 20 /var/log/inping-agent.log
The same msg=starting, msg=configuration, msg="host packages reported" lines as on Debian; host packages come
from /lib/apk/db/installed with the ecosystem Alpine:v3.20 (the release of /etc/os-release), scheduled jobs
from /etc/crontabs and /etc/periodic.
Update. The built-in updater restarts the service through systemctl, which Alpine does not have, so it cannot
be used here: it would install the new binary, fail to restart and put the old one back. Instead, run the install
command again (without a token – it keeps configuration and registration) and restart the service:
curl -fsSL https://<platform>/install.sh | sh && rc-service inping-agent restart
A line in root's crontab does this every night, if you want it automatic (rc-update add crond default once):
echo '17 4 * * * curl -fsSL https://<platform>/install.sh | sh >/dev/null && rc-service inping-agent restart' >>/etc/crontabs/root
Uninstall.
rc-service inping-agent stop && rc-update del inping-agent default && rm /etc/init.d/inping-agent
curl -fsSL https://<platform>/install.sh | sh -s -- --purge
(--purge removes configuration and state; the user it tries to remove with userdel stays – deluser inping-agent takes care of it.)
When nothing arrives. The Debian list, plus: curl: not found (install it), useradd: not found (create the
user as above before the installer), the service file lacks the execute bit, or crond is not running for the
update line.
5. Agent on macOS (launchd)
For Macs that serve something – a Mac mini with a web stack, a build host. Intel and Apple silicon.
Prerequisites. An administrator account; curl is part of macOS. The command runs in Terminal and asks for
the administrator password (sudo).
Install.
curl -fsSL https://<platform>/install.sh | INPING_TOKEN=<token> sudo --preserve-env=INPING_TOKEN sh -s -- --project <project-id>=/srv/shop
The script creates the hidden system user _inping-agent, writes the configuration, registers the agent and loads
two launchd daemons: the agent and its update job.
Read access. Give _inping-agent read access to the project directory – add it to the directory's group
(sudo dseditgroup -o edit -a _inping-agent -t user staff for a directory owned by staff) or grant read rights
with chmod -R o+rX/an ACL (sudo chmod -R +a "_inping-agent allow read,execute,readattr,readextattr,list,search" /srv/shop).
Without root. The same script with --user, as your own account in Terminal, no password asked:
curl -fsSL https://<platform>/install.sh | INPING_TOKEN=<token> sh -s -- --user --project <project-id>=$HOME/www/shop
The binary goes to ~/.local/bin/inping-agent, configuration and state to ~/Library/Application Support/inping,
the log to ~/Library/Logs/inping/agent.log, and two launch agents to ~/Library/LaunchAgents (the agent and its
update job, in your login session's launchd domain). The agent runs as your account and reads your own folders –
nothing to grant – but only while you are signed in: a Mac that serves something around the clock is better served by
the root install above. Check with launchctl print gui/$(id -u)/de.medienakzent.inping-agent | grep -E 'state|pid';
remove with the install command plus --user --uninstall or --user --purge.
Where things are.
| What | Where |
|---|---|
| Binary | /usr/local/bin/inping-agent |
| Configuration | /Library/Application Support/inping/agent.toml |
| State | /Library/Application Support/inping/state |
| Daemons | /Library/LaunchDaemons/de.medienakzent.inping-agent.plist and …inping-agent-update.plist |
| Log | /Library/Logs/inping/agent.log |
Check.
sudo launchctl print system/de.medienakzent.inping-agent | grep -E 'state|pid' # state = running
tail -n 20 /Library/Logs/inping/agent.log
The same msg=starting and msg=configuration lines as on Linux. Disks: only / and /System/Volumes/Data are
reported, each device once. Host packages: Homebrew's brew list --versions when Homebrew is installed – listed as
inventory, not checked for advisories (OSV has no Homebrew ecosystem). Scheduled jobs: the launchd plists in
/Library/LaunchDaemons, /Library/LaunchAgents and ~/Library/LaunchAgents.
Update. The daemon de.medienakzent.inping-agent-update runs inping-agent update every 6 hours; by hand
sudo inping-agent update --check / sudo inping-agent update. Running the install command again updates too.
Uninstall.
curl -fsSL https://<platform>/install.sh | sudo sh -s -- --uninstall # or --purge: also configuration, state, user, log
When nothing arrives. The Debian list, plus: launchctl print says the job exited – read the log; a project
under /Users/<name> is unreadable to _inping-agent until you grant access (macOS protects home folders); the
Full Disk Access privacy setting is not needed for ordinary project directories.
6. Agent on FreeBSD (rc.d)
FreeBSD 13 and 14, amd64 and arm64. Jails work the same way as long as the jail may reach the platform.
Prerequisites. Root (su -). fetch is part of the base system; no package is needed. (curl from ports works
too.)
Install.
fetch -qo - https://<platform>/install.sh | INPING_TOKEN=<token> sh -s -- --project <project-id>=/usr/local/www/shop
The script creates the user inping-agent, writes the configuration, installs the rc.d script, enables it
(sysrc inping_agent_enable=YES), starts it and installs the update cron file.
Read access. pw groupmod www -m inping-agent (the group that owns the project), then
service inping_agent restart.
Where things are.
| What | Where |
|---|---|
| Binary | /usr/local/bin/inping-agent |
| Configuration | /usr/local/etc/inping/agent.toml |
| State | /var/db/inping-agent |
| Service | /usr/local/etc/rc.d/inping_agent (runs under daemon(8)) |
| Update job | /usr/local/etc/cron.d/inping-agent-update (every 6 hours) |
| Log | syslog, /var/log/messages, tag inping-agent |
Check.
service inping_agent status # inping_agent is running as pid …
grep inping-agent /var/log/messages | tail -n 20
The same msg=starting and msg=configuration lines. There is no Landlock on FreeBSD; the agent can only write
what its user owns. Host packages: FreeBSD's pkg is not read yet – the agent reports no host packages on FreeBSD
(project packages and runtimes, yes). Scheduled jobs: /etc/crontab, /etc/cron.d and the user crontabs the agent's
user may read.
Update. The cron entry runs inping-agent update every 6 hours (a random minute per host); by hand
inping-agent update --check / inping-agent update as root. The install command run again updates too.
Uninstall.
fetch -qo - https://<platform>/install.sh | sh -s -- --uninstall # or --purge
When nothing arrives. The Debian list, plus: service inping_agent status says it is not running while the log
shows restarts every 10 seconds – read the first error line in /var/log/messages; a jail without a route to the
platform; fetch refusing the download: the platform's certificate is not trusted (pkg install ca_root_nss).
7. Agent on Windows (PowerShell)
Windows Server 2019 and later, Windows 10 and 11; x64 and ARM64.
Prerequisites. PowerShell 5.1 or 7, started as administrator. Outbound HTTPS. The script and the download use TLS 1.2 and follow no redirects.
Install. Paste the command from the dialog:
$env:INPING_TOKEN = '<token>'; & ([scriptblock]::Create((irm https://<platform>/install.ps1))) -Project '<project-id>=D:\sites\shop'
Several projects: -Project '<id1>=D:\sites\shop', '<id2>=D:\sites\blog'. The script verifies the SHA-256 of the
download (minisign is not verified on Windows), installs the binary, writes the configuration, creates the service
inping-agent running as the virtual account NT SERVICE\inping-agent, registers the agent, starts the service and
registers the scheduled task inping-agent-update. It ends with inping: inping-agent <version> installed and running (Event Viewer → Application, source inping-agent); updates every 6 h (task inping-agent-update).
Read access. The service account needs read access to the project directory:
icacls 'D:\sites\shop' /grant 'NT SERVICE\inping-agent:(OI)(CI)RX' /T
Restart-Service inping-agent
Where things are.
| What | Where |
|---|---|
| Binary | %ProgramFiles%\inping\inping-agent.exe |
| Configuration | %ProgramData%\inping\agent.toml |
| State | %ProgramData%\inping\state (the only directory the service may change) |
| Service | inping-agent, account NT SERVICE\inping-agent, automatic start |
| Update job | Task Scheduler, task inping-agent-update (SYSTEM, every 6 hours) |
| Log | Event Viewer → Windows Logs → Application, source inping-agent |
Check.
Get-Service inping-agent # Status Running
Get-WinEvent -ProviderName inping-agent -MaxEvents 20 | Format-List TimeCreated, Message
Get-ScheduledTask inping-agent-update # State Ready
The event log carries the same msg=starting and msg=configuration lines. Reported: CPU (no load average on
Windows), memory, disks, platform, the scheduled tasks (schtasks /query) and the projects' packages. Host
packages are not reported on Windows – there is no package manager the agent reads there.
CPU, memory and processes per account (optional). Add -AccountMetrics to the install command, or run the script
again with it (without a token on a registered host); -NoAccountMetrics or -Uninstall remove it:
& ([scriptblock]::Create((irm https://<platform>/install.ps1))) -AccountMetrics
Get-ScheduledTask inping-agent-accounts # State Ready
The service account may not see who owns the processes of others, so the scheduled task inping-agent-accounts reads
them as SYSTEM every minute and leaves the result in %ProgramData%\inping\account-usage.json for the service; the service
keeps its rights. IIS application pools with an identity of their own appear as accounts (IIS APPPOOL\shop), which is how a
shared host tells its customers apart; services under SYSTEM or NETWORK SERVICE add up there, and applications running
as one account cannot be told apart. The CPU share is the average of the last minute. Disk and network per account are not
measured on Windows. The metric users has to be selected for the agent in the platform.
Update. The task runs inping-agent update every 6 hours as SYSTEM. By hand, in an elevated PowerShell:
& "$env:ProgramFiles\inping\inping-agent.exe" update --config "$env:ProgramData\inping\agent.toml"
Running the install command again (without $env:INPING_TOKEN) updates too and keeps the configuration.
Uninstall.
& ([scriptblock]::Create((irm https://<platform>/install.ps1))) -Uninstall # service, task and binary
& ([scriptblock]::Create((irm https://<platform>/install.ps1))) -Purge # also configuration and state
When nothing arrives. run in an elevated PowerShell: start PowerShell as administrator. the service could not be set up: an older service of the same name is stuck – sc.exe delete inping-agent, then run again. The
service stops right away: open the Application log, the first error names the cause (usually cannot start – a
missing or unreadable token in %ProgramData%\inping\state – or configuration unusable). A proxy: the service
account has no proxy settings; set HTTPS_PROXY as a system environment variable or allow direct HTTPS to the
platform. A project without the icacls grant above is reported as nothing.
8. Agent in Docker Compose
The agent as a service next to the project's own services, on the same host as them. It reports the host (through
read-only mounts of /proc, /sys and /), the inventory of the mounted project directory and every service of the
Compose project (CPU, memory, network, I/O, running) through a socket proxy. Not reported in a container: host
packages and scheduled jobs (they would be the container's), runtime versions (php -v cannot be asked across
containers – the lock files alone feed the inventory).
Prerequisites. Docker Engine with Compose v2 on the host; the project's docker-compose.yml; outbound HTTPS to
the platform and to ghcr.io. The agent image is ghcr.io/medienakzent/inping-agent:<version>@sha256:<digest> –
version and digest are in the release notes on the install page (<platform>/agents/install); never a moving tag.
Install. The install dialog's Docker tab shows three things with your values filled in; here they are with placeholders.
-
Three lines in the
.envnext to the Compose file:INPING_AGENT_IMAGE=ghcr.io/medienakzent/inping-agent:<version>@sha256:<digest> INPING_API_URL=https://<platform> INPING_PROJECT_ID=<project-id> -
Two services, one config, one volume and one network added to the project's Compose file (or the whole
docker-compose.example.ymlcopied next to it). The essential parts:services: inping-agent: image: ${INPING_AGENT_IMAGE:?set INPING_AGENT_IMAGE to a release} restart: unless-stopped read_only: true cap_drop: [ALL] security_opt: - no-new-privileges:true configs: - source: inping-agent target: /etc/inping/agent.toml volumes: - inping-agent-state:/var/lib/inping-agent - /proc:/host/proc:ro - /sys:/host/sys:ro - /:/host/root:ro - ./:/srv/app:ro networks: [default, inping] depends_on: [inping-docker-proxy] inping-docker-proxy: image: tecnativa/docker-socket-proxy:0.3.0 restart: unless-stopped environment: CONTAINERS: 1 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro networks: [inping] configs: inping-agent: content: | api_url = "${INPING_API_URL:?set INPING_API_URL}" state_dir = "/var/lib/inping-agent" [docker] endpoint = "tcp://inping-docker-proxy:2375" [[project]] id = "${INPING_PROJECT_ID:?set INPING_PROJECT_ID}" path = "/srv/app" volumes: inping-agent-state: networks: inping: internal: trueThe proxy is not optional: a read-only mount of
docker.sockis no protection (the daemon accepts every call that reaches it), so the agent never sees the socket. The proxy refuses every write and everything outside/containers. It does allow every read below that path, including inspect output, logs and files of other containers, so it protects the host from the agent, not the containers' contents. Pin the proxy image by digest too once you have resolved it (docker buildx imagetools inspect tecnativa/docker-socket-proxy:0.3.0). -
Register once – the token goes in through stdin and never into a file – and start:
echo "<token>" | docker compose run --rm -T inping-agent register --token - docker compose up -d
One project per Compose file: the example mounts one project directory; a second project on the same host gets its own agent in its own Compose file.
Without the socket proxy. The agent also reads the socket directly: mount it and name it as the endpoint. It
still sends nothing but the two GETs. What changes is the blast radius: the Engine API has no read-only mode, so
:ro on the mount protects nothing, and a container that reaches the socket can start, stop and create containers –
whoever broke into the agent's container would hold the host. The agent runs as user 65532, so it needs the socket's
group (stat -c %g /var/run/docker.sock, often docker or, on Synology, root). Take this route only on a host
where nothing untrusted runs next to the agent, such as a NAS of your own:
services:
inping-agent:
# … as above, without depends_on and without the inping network …
group_add:
- "<gid of /var/run/docker.sock>"
volumes:
- inping-agent-state:/var/lib/inping-agent
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/host/root:ro
- /var/run/docker.sock:/var/run/docker.sock:ro # :ro limits nothing, see above
configs:
inping-agent:
content: |
api_url = "${INPING_API_URL:?set INPING_API_URL}"
state_dir = "/var/lib/inping-agent"
[docker]
endpoint = "unix:///var/run/docker.sock"
all = true # every container on the host; or project = "<compose project>" for one of them
Drop the inping-docker-proxy service, the inping network and the [[project]] block when there is no project
directory to read. The agent then reports the host and every container (or those of one Compose project) – not the
packages of the host.
Synology DSM (Container Manager). The same Compose file should work in Container Manager → Project on models that
have it (not tested on real hardware); the installer is not built for DSM, so the Docker way is the one for a NAS. Mount /var/run/docker.sock with group_add
as above (or run the proxy variant, which Container Manager supports as well). What you get: CPU, memory, disks,
load and uptime of the NAS, and the state and consumption of every container on it. What you do not get: the DSM
packages (the Package Center is not a package manager the agent reads, and no advisory database covers it) and the
packages inside the other containers – the agent reads lock files of a mounted project directory, not the images of
its neighbours. Vulnerabilities on a NAS are therefore those of the projects whose directories you mount.
Where things are. Everything is in the Compose project: the configuration is the configs entry (rendered into
/etc/inping/agent.toml in the container), the state (token, buffer) the volume inping-agent-state, the log
docker compose logs inping-agent.
Check.
docker compose ps # inping-agent and inping-docker-proxy Up
docker compose logs --tail 20 inping-agent
The log shows msg=starting, msg=configuration and msg="docker: reporting the agent's own Compose project" project=<name>. In inping, besides the usual: the agent's page gets a Services section with one row per service
of the Compose project – state, CPU, memory, network, I/O – within two intervals (rates need two samples). Switch
the metric Services (Docker) on under Settings → Collected metrics if it is off.
Update. The agent never replaces itself in a container (inping-agent update refuses). A new release is a new
image: set the new version and digest in .env, then
docker compose pull inping-agent && docker compose up -d inping-agent
Uninstall. Remove the two services, the config, the volume and the network from the Compose file, then
docker compose up -d --remove-orphans (or docker compose down -v for a copy of the example file – -v deletes
the state volume). Delete the agent in inping.
When nothing arrives.
registersaysregistration failed: expired or used token, orINPING_API_URLwrong (the error names the response).set INPING_AGENT_IMAGE to a release …: the.envis not next to the Compose file you run, or a variable is missing.- The agent restarts in a loop:
docker compose logs inping-agent–cannot startmeans it was never registered (run step 3),docker source unusablemeans the proxy is not reachable (is it on theinpingnetwork, is the endpointtcp://inping-docker-proxy:2375?). - Host metrics arrive, services do not: the proxy answers 403 –
CONTAINERS: 1missing – or the agent's container is not part of a Compose project (then every container on the host is reported, see[docker]in the agent's README). - The project's packages are missing:
./:/srv/app:romounts the wrong directory – the lock files have to be at the top of/srv/app.
9. Runner on Linux (systemd)
Linux with systemd, amd64 or arm64; the runner is not built for other systems (use the Docker image there). A runner
needs outbound HTTPS to the platform and to the sites it checks. A runner of your own may check private networks when
both the platform (the runner's scope) and runner.toml allow it.
Prerequisites. curl (or wget), sudo or root, kernel 5.13 or later for Landlock (optional), a sensible
nofile limit for many parallel checks (the unit sets it).
Install. With the command from Runners → Add runner:
curl -fsSL https://<platform>/install-runner.sh | INPING_TOKEN=<token> sudo --preserve-env=INPING_TOKEN sh
The script creates the user inping-runner, writes /etc/inping/runner.toml, registers the runner (it exchanges
the token for its own key) and starts the service. It ends with inping: running (systemctl status inping-runner, journalctl -u inping-runner); updates every 6 h (inping-runner-update.timer) and inping: inping-runner <version> installed.
Where things are.
| What | Where |
|---|---|
| Binary | /usr/local/bin/inping-runner |
| Configuration | /etc/inping/runner.toml (api_url, concurrency, private_networks) |
| State | /var/lib/inping-runner (token, last plan, buffer, seed, discoveries) |
| Service | /etc/systemd/system/inping-runner.service, user inping-runner |
| Update job | inping-runner-update.timer and .service (every 6 hours, as root) |
| Log | journald: journalctl -u inping-runner |
An agent on the same host shares /etc/inping; each keeps its own file.
Check.
systemctl status inping-runner
journalctl -u inping-runner -n 20
sudo -u inping-runner inping-runner check --config /etc/inping/runner.toml # fetch the plan and show it
A healthy start logs msg="runner started" version=1.2.0 platform=https://www.inping.io concurrency=50 …, then
msg=plan from=… origins=12 pages=3 once the plan arrived (the planner asks every minute). In inping, Runners
shows the runner Online with its version and Last seen within a minute (it sends a heartbeat); the project's
Overview shows results from it with the next check of each URL (every 60 seconds by default), pages at their
interval, and new pages of projects with Find new pages on within 1 to 10 minutes.
Private networks. Allow it in inping for the runner (its scope) and on the host:
# /etc/inping/runner.toml
private_networks = true
then sudo systemctl restart inping-runner. Loopback, link-local and the cloud metadata addresses stay refused.
Update. The timer runs inping-runner update every 6 hours; by hand sudo inping-runner update --check /
sudo inping-runner update. The install command run again updates too.
Uninstall.
curl -fsSL https://<platform>/install-runner.sh | sudo sh -s -- --uninstall # or --purge
Then delete (or pause) the runner in inping.
When nothing arrives.
registration failed: token used, expired, or the runner was created for another platform.runner startedbut noplanline: the platform answers 401 or 403 – the runner was paused or deleted in inping (it asks again every hour;inping-runner checksays so at once) – or no project is in its scope yet.- Checks run but every site is down: the host cannot reach the sites (DNS, egress firewall) – the runner's own errors show up under Administration → Error log for super admins.
- A private site is never checked:
private_networksis off on one of the two sides.
10. Runner in Docker
Prerequisites. Docker with Compose v2, outbound HTTPS to the platform and the sites.
The image is ghcr.io/medienakzent/inping-runner:<version>@sha256:<digest> (release notes on the platform).
Install. Copy tools/runner/docker-compose.example.yml next to a
.env:
INPING_RUNNER_IMAGE=ghcr.io/medienakzent/inping-runner:<version>@sha256:<digest>
INPING_API_URL=https://<platform>
# INPING_CONCURRENCY=50
# INPING_PRIVATE_NETWORKS=false
then
echo "<token>" | docker compose run --rm -T runner register --token -
docker compose up -d
Where things are. The configuration is the configs entry (/etc/inping/runner.toml in the container), the
state the volume state, the log docker compose logs runner.
Check. docker compose ps (Up), docker compose logs --tail 20 runner (runner started, then plan); in
inping the runner turns Online within a minute.
Update. New version and digest in .env, then docker compose pull && docker compose up -d. The runner never
replaces itself in a container.
Uninstall. docker compose down -v (deletes the state volume with the runner's key), then delete the runner in
inping.
When nothing arrives. As on Linux; in addition set INPING_RUNNER_IMAGE to a release … means the .env is not
where you run Compose.
11. The one-line scan (no install, no account)
Checks the packages of a project tree against OSV.dev and prints the vulnerable ones. It downloads the agent binary
for the system into a temporary directory, verifies it, runs inping-agent scan and removes the binary again.
Nothing but package names and versions leaves the machine, and nothing is registered.
Prerequisites. Linux, macOS or FreeBSD (amd64, arm64); curl (or wget/fetch); outbound HTTPS to the
platform and to api.osv.dev. No root: it runs as you. (/tmp mounted noexec: the binary runs from the current
directory for the moment and is removed afterwards.)
Run.
curl -fsSL https://<platform>/scan.sh | sh -s -- /path/to/project
curl -fsSL https://<platform>/scan.sh | sh -s -- . --json > findings.json
curl -fsSL https://<platform>/scan.sh | sh -s -- . --fail-on high # exit 1 only for high and critical
Every directory under the path that holds a lock file is a project (a monorepo yields several); node_modules,
vendor, .git, target, .venv, dist and build are not entered.
What you see. One block per language with each project directory and its package count, one line per advisory
(name version → fixed SEVERITY id / aliases summary) and a last line such as 84 packages in 2 projects, 1 vulnerable, worst: high. Exit codes: 0 clean (at or above --fail-on, default low), 1 findings, 2 an error or
OSV not reachable (the message on stderr). That makes it a CI step:
curl -fsSL https://<platform>/scan.sh | sh -s -- . --fail-on high || exit 1
When nothing comes out. the platform has no published agent release yet – the platform has no release; no build of <version> for <os>/<arch> – an unsupported system (Windows: install the agent and run inping-agent.exe scan <dir>); checksum mismatch – a proxy altered the download, try again; exit 2 with osv in the message –
api.osv.dev is not reachable.
12. Checklist for a full round trip
Use a fresh VM or container and tick the steps. Expected times assume the default interval of 60 seconds.
- Register. Create the agent in inping, run the install command of the system. The script ends with
inping-agent <version> installed; the agent's card leaves Not registered yet and shows the hostname. - Metrics. Within a minute the card shows Online with CPU, memory and disk; the agent's page draws Last 24 hours after the second sample.
- Project inventory. Assign a project with code on the host (
--projectat install, or Settings → Projects), give the service user read access. Project → Packages lists runtimes and packages after the next inventory check (at the latest 15 minutes; restart the service to force it). Manual packages stay next to them. - Host packages. Packages on the host on the agent's page shows the count, the package manager and the
distribution (
Debian:12,Ubuntu:24.04, …). Search foropenssl. On Windows and FreeBSD this section stays empty by design; on macOS it lists Homebrew without advisories. - Jobs. Scheduled jobs lists the host's cron entries, timers, launchd jobs or tasks – on Linux with systemd,
macOS and Windows the agent's own update job is among them (
inping-agent-update; FreeBSD's cron file under/usr/local/etc/cron.dis not read). - An alert rule firing. On the agent's page under Settings → Alert rules, add a rule CPU usage above 1 % for 0 minutes (every host exceeds it) – the alert opens with the next report under Alerts, the project's Servers tab gets a count, and a notification rule with the event Server threshold exceeded sends a mail. Delete the rule; the alert clears with the next report.
- Offline. Stop the service (
systemctl stop inping-agent); after three missed reports (3 minutes by default) the agent is Offline and an offline alert opens; start it again, the alert clears. - A scan.
curl -fsSL https://<platform>/scan.sh | sh -s -- /path/to/projectprints the same packages as step 3, with their findings, and exits 1 if there are any. - Update.
sudo inping-agent update --checksays whether a newer release exists;sudo inping-agent updateinstalls it and the version on the agent's page changes within a minute. - Uninstall. The
--uninstallcommand stops and removes the service; the agent goes Offline; delete it in inping.
For a runner: create it, install, see it Online within a minute; add a URL to a project and see the first check within its interval; stop the runner and see the platform's own runners keep checking.
Paddle: paid plans
Paddle sells the plans as merchant of record: it takes the payment, writes the invoices and pays the tax. The .env holds
only PADDLE_API_KEY and PADDLE_WEBHOOK_SECRET; everything else comes from the code or through the API. Without them
nothing can be bought: teams stay on the free plan and a super admin gives plans by hand under Administration → Teams.
- Try it first in the sandbox (
sandbox-vendors.paddle.com): a sandbox key (pdl_sdbx_…) makes inping use the sandbox, a live key the real one. Keys from before May 2025 have no such mark and count as live. - API key: Developer tools → Authentication, with the permissions: products, prices, customers, subscriptions and client tokens read and write, transactions read. Keys created earlier do not have the client token permission yet.
- Prices:
pricesof a plan inconfig/plans.php, e.g.['month' => 3900, 'year' => 39000]for 39.00 per month or 390.00 per year without tax, the currency inconfig/cashier.php(EUR). Without prices the plan is not sold. The custom plan never is: teams ask for it on their plan page, the inquiry goes by mail to the super admins, who give the plan and the team's quotas under Administration. - Sync:
php artisan inping:sync-planscreates at Paddle what is missing: the product, the prices and the client-side token of the checkout; what exists is left alone. It runs at every deployment throughdocker/deploy-hook.shand hourly, so a changed price arrives by itself. Running subscriptions stay on their price; new ones get the new one. - Webhook: Events → Notifications: destination
https://<your host>/paddle/webhookwith the eventscustomer.updated,transaction.completed,transaction.updated,subscription.created,subscription.updated,subscription.paused,subscription.canceled. Its secret key goes toPADDLE_WEBHOOK_SECRET. Without a secret the route does not exist: nobody can change a plan with a forged request. - Domain: approve your host in Paddle (Checkout → Website approval) for the live checkout, and set the default
payment link under Checkout → Checkout settings to
https://<your host>/billing. Without it Paddle opens no checkout at all (transaction_default_checkout_url_not_set); sandbox and live each need their own. A super admin sees on the plan page what is still missing here in inping. - Payment failures: set the retries under Checkout → Recovery: the plan stays while Paddle retries and falls back to free once it cancels the subscription.
Paddle knows one customer per address, so each paying team needs its own billing address. When a plan gets smaller nothing is deleted: what exceeds the quotas keeps running, only nothing new can be added.