← inping

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:

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:

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:

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

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

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.

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.

  1. Three lines in the .env next 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>
    
  2. Two services, one config, one volume and one network added to the project's Compose file (or the whole docker-compose.example.yml copied 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: true
    

    The proxy is not optional: a read-only mount of docker.sock is 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).

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

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.

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.

  1. 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.
  2. 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.
  3. Project inventory. Assign a project with code on the host (--project at 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.
  4. 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 for openssl. On Windows and FreeBSD this section stays empty by design; on macOS it lists Homebrew without advisories.
  5. 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.d is not read).
  6. 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.
  7. 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.
  8. A scan. curl -fsSL https://<platform>/scan.sh | sh -s -- /path/to/project prints the same packages as step 3, with their findings, and exits 1 if there are any.
  9. Update. sudo inping-agent update --check says whether a newer release exists; sudo inping-agent update installs it and the version on the agent's page changes within a minute.
  10. Uninstall. The --uninstall command 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.

  1. 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.
  2. 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.
  3. Prices: prices of a plan in config/plans.php, e.g. ['month' => 3900, 'year' => 39000] for 39.00 per month or 390.00 per year without tax, the currency in config/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.
  4. Sync: php artisan inping:sync-plans creates 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 through docker/deploy-hook.sh and hourly, so a changed price arrives by itself. Running subscriptions stay on their price; new ones get the new one.
  5. Webhook: Events → Notifications: destination https://<your host>/paddle/webhook with the events customer.updated, transaction.completed, transaction.updated, subscription.created, subscription.updated, subscription.paused, subscription.canceled. Its secret key goes to PADDLE_WEBHOOK_SECRET. Without a secret the route does not exist: nobody can change a plan with a forged request.
  6. 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.
  7. 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.