A lightweight Go agent that collects VPS and host metrics and reports them to a Node Monitor dashboard over HTTP.
- Single static binary with a small runtime footprint
- No runtime dependencies beyond the Go module and the OS interfaces it uses
- Runs on Linux, macOS, and Windows
- Collects Docker container metrics when the Docker socket is available
- Shuts down gracefully on SIGINT/SIGTERM
- Go 1.21 or newer to build from source
- The module is declared as
node-monitor-agentand depends ongithub.com/shirou/gopsutil/v3 v3.24.5 - A host that can run the compiled binary on Linux, macOS, or Windows
- Optional: access to the Docker socket if you want Docker container metrics
go mod download
go build -ldflags="-s -w" -o node-agent .CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o node-agent-linux-amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o node-agent-linux-arm64 .
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o node-agent-windows-amd64.exe .docker build -t node-monitor-agent .
docker run --rm -e DASHBOARD_URL=https://monitor.example.com/api/report -e TOKEN=replace-me node-monitor-agentThe agent uses the following configuration precedence:
- CLI flag
- Environment variable
- Built-in default
| Flag | Environment variable | Default | Meaning |
|---|---|---|---|
-server |
DASHBOARD_URL |
http://localhost:3000 |
Dashboard URL |
-token |
TOKEN |
changeme |
Agent authentication token |
-hostname |
HOSTNAME |
empty string | Reported hostname; if empty, the OS hostname is used |
-interval |
INTERVAL_SEC |
10 |
Seconds between reports |
-timeout |
TIMEOUT_SEC |
5 |
HTTP timeout in seconds |
Environment integer parsing is strict: getEnvInt only accepts positive integers. Any other value falls back to the default.
Important: the agent sends the report to the value of
-serverorDASHBOARD_URLexactly as provided. It does not append/api/reportautomatically. You must provide the full endpoint URL, for examplehttps://monitor.example.com/api/report. The built-in defaults in the code and Docker image are not sufficient by themselves because they omit the path.
Using flags:
./node-agent \
-server https://monitor.example.com/api/report \
-token replace-me \
-hostname web-01 \
-interval 15 \
-timeout 8Using environment variables:
export DASHBOARD_URL=https://monitor.example.com/api/report
export TOKEN=replace-me
export HOSTNAME=web-01
export INTERVAL_SEC=15
export TIMEOUT_SEC=8
./node-agentThe agent starts by printing a banner, launches the reporting loop in a background goroutine, and waits for SIGINT/SIGTERM for graceful shutdown.
=== Node Monitor Agent (Go) ===
Server: https://monitor.example.com/api/report
Interval: 15s
Timeout: 8s
Hostname: web-01
-------------------------------
[OK] Reported to https://monitor.example.com/api/report | CPU: 12.3% | RAM: 47.1% | Disk: 31.4%
[ERROR] Report failed: dashboard returned status 404
The agent collects the following fields and sends them as JSON.
| Metric | Unit | Notes |
|---|---|---|
cpu.name |
string | CPU model name from the first CPU info entry |
cpu.usage |
percent | Sampled over a blocking 1-second window |
memory.used |
MB | Virtual memory used |
memory.total |
MB | Virtual memory total |
memory.percent |
percent | Memory usage percentage |
disk.used |
GB | Based on the first partition returned by disk.Partitions(false) |
disk.total |
GB | Based on the same partition |
disk.percent |
percent | Usage percentage for that partition |
network.rx |
bytes | Cumulative bytes received since boot; the dashboard must derive rates if needed |
network.tx |
bytes | Cumulative bytes sent since boot; the dashboard must derive rates if needed |
docker.containers |
count | Best-effort Docker container count |
docker.running |
count | Best-effort running container count |
uptime |
seconds | Host uptime from the OS API |
os |
string | Linux uses PRETTY_NAME from /etc/os-release; otherwise the Go architecture string |
platform |
string | runtime.GOOS value |
The values are intentionally best-effort: any metric that fails to collect is ignored and the agent continues running. Numeric values are truncated to two decimal places rather than rounded to the nearest value.
The agent posts a JSON object with the following top-level fields:
{
"token": "replace-me",
"hostname": "web-01",
"os": "Linux Ubuntu 24.04 LTS",
"platform": "linux",
"uptime": 482391,
"cpu": {
"name": "Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz",
"usage": 12.3
},
"memory": {
"used": 2048.5,
"total": 8192,
"percent": 25.0
},
"disk": {
"used": 18.7,
"total": 100,
"percent": 18.7
},
"network": {
"rx": 1234567890,
"tx": 987654321
},
"docker": {
"containers": 4,
"running": 2
}
}Create a unit file at /etc/systemd/system/node-agent.service:
[Unit]
Description=Node Monitor Agent
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/node-agent
Environment=DASHBOARD_URL=https://monitor.example.com/api/report
Environment=TOKEN=replace-me
Environment=INTERVAL_SEC=15
Environment=TIMEOUT_SEC=8
ExecStart=/opt/node-agent/node-agent
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetThen reload and enable it:
sudo systemctl daemon-reload
sudo systemctl enable --now node-agent
sudo journalctl -u node-agent -fservices:
node-agent:
image: node-monitor-agent:latest
environment:
DASHBOARD_URL: https://monitor.example.com/api/report
TOKEN: replace-me
INTERVAL_SEC: 15
TIMEOUT_SEC: 8
restart: always- Keep the agent token secret and do not commit it to source control.
- The token is included in the JSON payload, so HTTPS is strongly recommended in production.
- Docker metrics require access to the Docker socket and should be considered a privileged operation.
| Symptom | Likely cause | Fix |
|---|---|---|
| HTTP 404 responses | The dashboard URL is missing /api/report |
Use a full endpoint such as https://monitor.example.com/api/report |
| HTTP 400 responses | The token sent by the agent does not match the dashboard expectation | Check -token or TOKEN and make sure the dashboard accepts the same value |
| Connection timeouts | Firewall rules, network reachability, or a timeout that is too low | Verify reachability and increase -timeout / TIMEOUT_SEC |
| Docker metrics stay at zero | The process does not have access to the Docker socket | Ensure the container or host has Docker socket access and that Docker is available |
| CPU usage appears delayed | CPU usage is sampled over a blocking 1-second window | Expect a short one-second delay before the metric reflects recent load |
agent/
├── main.go # Entry point and graceful shutdown handling
├── config.go # CLI flags, environment variables, and defaults
├── metrics.go # Metric collection for CPU, memory, disk, network, Docker, and OS metadata
├── reporter.go # HTTP POST reporting loop and logging
├── go.mod # Go module declaration and dependency list
├── Dockerfile # Multi-stage container build
├── .gitignore # Build artifact ignore rules
└── .dockerignore # Docker build context exclusions
This project is licensed under the MIT License. See LICENSE.md.