| name | server-inventory |
| description | Add and scan remote Linux servers via SSH, collect system facts, and view fleet health in a React dashboard. |
| triggers | ["server inventory","ssh fleet scan","collect server facts","server-inventory"] |
When to use this skill
Use this skill when working on the server-inventory application: adding SSH targets, triggering scans, interpreting collected data, configuring groups, or troubleshooting connection errors.
Prerequisites
- Docker and Docker Compose installed
- SSH private key accessible on the host machine
- Target servers running Linux with SSH enabled
- User account on each target with read access to
/proc, /etc/os-release, and df
Quick Start
Development
pnpm install
cd backend && pnpm dev
cd frontend && pnpm dev
Docker
cp .env.example .env
docker compose up
Dashboard: http://localhost:8080
API: http://localhost:3000
Environment Variables
| Variable | Default | Description |
|---|
PORT | 3000 | Express server port |
DB_PATH | ./data/inventory.db | SQLite database path |
SCAN_INTERVAL_MINUTES | 60 | Scheduled fleet scan interval |
SCAN_CONCURRENCY | 5 | Max parallel SSH connections |
SSH_TIMEOUT_MS | 10000 | SSH connection timeout in ms |
CORS_ORIGIN | http://localhost:5173 | Allowed CORS origin |
VITE_API_URL | http://localhost:3000 | API base URL for frontend |
Adding Servers
Via the dashboard
Click "Add Server" in the top bar. Fill in label, host, port, username, auth method, and key path.
Via the API
curl -X POST http://localhost:3000/api/servers \
-H "Content-Type: application/json" \
-d '{
"label": "web-01",
"host": "192.168.1.10",
"port": 22,
"username": "ubuntu",
"authMethod": "key",
"keyPath": "/root/.ssh/id_rsa",
"groupId": null
}'
The keyPath must be the path inside the server-inventory container (e.g., /root/.ssh/id_rsa when ~/.ssh is mounted at /root/.ssh).
Triggering Scans
curl -X POST http://localhost:3000/api/scan-all
curl -X POST http://localhost:3000/api/servers/1/scan
Scan results appear immediately in the dashboard. Each scan inserts a snapshot row and a scan_log row.
API Reference
| Method | Path | Description |
|---|
GET | /api/health | Health check |
GET | /api/servers | List all servers with latest snapshot |
POST | /api/servers | Add a server |
PUT | /api/servers/:id | Update server config |
DELETE | /api/servers/:id | Remove server |
POST | /api/servers/:id/scan | On-demand scan |
GET | /api/servers/:id | Server detail with latest snapshot |
GET | /api/servers/:id/snapshots | Snapshot history (last 90) |
GET | /api/servers/:id/scan-log | Recent scan log entries for server |
POST | /api/scan-all | Scan all servers |
GET | /api/groups | List groups |
POST | /api/groups | Create group |
PUT | /api/groups/:id | Update group |
DELETE | /api/groups/:id | Delete group |
GET | /api/scan-log | Recent scan log (all servers) |
GET | /api/settings | Current settings |
POST | /api/settings | Update settings |
Collected Facts
Each scan runs these read-only SSH commands and stores the results:
| Fact | Command |
|---|
| Hostname | hostname -f |
| OS name | cat /etc/os-release |
| Kernel | uname -r |
| Architecture | uname -m |
| Uptime | cat /proc/uptime |
| CPU model | cat /proc/cpuinfo (first model name) |
| CPU cores | nproc |
| Memory | free -m |
| Disk mounts | df -m --output=source,target,size,used,pcent |
| Load average | cat /proc/loadavg |
Groups
Groups organize servers in the sidebar and allow fleet-filtered views.
curl -X POST http://localhost:3000/api/groups \
-H "Content-Type: application/json" \
-d '{ "name": "web", "color": "#0891b2" }'
curl -X PUT http://localhost:3000/api/servers/1 \
-H "Content-Type: application/json" \
-d '{ "groupId": 1 }'
Deleting a group sets group_id to null for all member servers; servers are not deleted.
Snapshot Retention
Snapshots older than RETENTION_DAYS (default 90) are pruned at the start of each scheduled scan cycle. On-demand scans do not trigger pruning. To change retention, update RETENTION_DAYS in Settings or via POST /api/settings.
Troubleshooting
connect ETIMEDOUT
The server is not reachable on the configured port. Check:
- The server is powered on and on the network
- No firewall is blocking TCP port 22 from the server-inventory container
- Try increasing
SSH_TIMEOUT_MS in Settings for slow connections
All configured authentication methods failed
SSH key authentication is failing. Check:
- The public key is in
~username/.ssh/authorized_keys on the target server
- The private key at
keyPath is correctly mounted in the container
- Key file permissions are
600 (run chmod 600 ~/.ssh/id_rsa on the host)
- The username is correct
No snapshots after scan
The scan succeeded (scan_log shows success) but snapshot data is missing. Check:
- The SSH user has read access to
/proc/meminfo, /proc/loadavg, and df
/etc/os-release exists on the target (required for OS detection)
- Review
raw_output in the snapshots table for parse errors
SSH key not found in container
The key path is wrong or the mount is missing. Verify the Docker Compose volume mount:
volumes:
- ~/.ssh:/root/.ssh:ro
The keyPath field should use the in-container path: /root/.ssh/id_rsa.