Skip to content

Configuration ​

Probestyx is configured using a YAML file (config.yaml). This guide covers all configuration options with examples.

Configuration File Location ​

By default, Probestyx looks for config.yaml in the current working directory:

  • Linux/macOS: /opt/probestyx/config.yaml
  • Windows: C:\probestyx\config.yaml

Basic Configuration Structure ​

yaml
server:
  port: 9100
  secret: ""
  # consumers: []   # optional, only needed for per-consumer log tailing - see Scrapers

system:
  enabled: true
  cache_ttl: 60     # optional, caches system metrics only (see below)
  metrics:
    - cpu_usage_percent
    - ram_usage_percent

scrapers:
  - name: scraper_name
    source:
      type: url
      url: "http://example.com/metrics"
      format: json
    metrics:
      - path: "metric.path"
        name: "output_name"

Server Configuration ​

The server section configures the HTTP server that exposes metrics.

yaml
server:
  port: 9100              # HTTP server port (default: 9100)
  secret: "your-secret"   # Optional HMAC authentication secret
  consumers: []           # Optional allowlist for ?callerId= (see Scrapers → Per-Consumer Tailing)

Options ​

OptionTypeDefaultDescription
portinteger9100Port to listen on
secretstring""HMAC secret for authentication (empty = no auth)
consumerslist of strings(none)Allowlist of ?callerId= values for per-consumer log tailing

TIP

Looking for cache_ttl? It lives under system:, not server: — see System Metrics Configuration below. It only caches system metrics; scrapers are never cached and re-run on every request.

Authentication ​

If secret is provided, clients must sign requests:

yaml
server:
  secret: "my-secure-secret-key"

If secret is empty or omitted, no authentication is required:

yaml
server:
  port: 9100
  # No secret = no authentication

See Authentication for client implementation.

System Metrics Configuration ​

The system section enables collection of built-in system metrics.

yaml
system:
  enabled: true    # Enable/disable system metrics
  cache_ttl: 60    # Optional: cache duration in seconds (default: 60)
  metrics:         # List of metrics to collect
    - cpu_usage_percent
    - ram_usage_percent
    - disk_usage_percent

Caching ​

cache_ttl (under system:) applies only to system metrics (CPU/RAM/disk/network/etc.) — it avoids re-querying the OS on every rapid /metrics request. It has no effect on the scrapers: section: every scraper (URL, file, or command) runs fresh on every request, with no caching layer of its own.

The one exception is format: log scrapers, which have their own separate mechanism — they track what's already been read (a file offset, or a timestamp) rather than caching a result. See Scrapers → Log Parser.

Available Metrics ​

See System Metrics for a complete list of available system metrics.

Examples ​

Minimal system metrics:

yaml
system:
  enabled: true
  metrics:
    - cpu_usage_percent
    - ram_usage_percent
    - disk_usage_percent

Comprehensive monitoring:

yaml
system:
  enabled: true
  metrics:
    # CPU
    - cpu_usage_percent
    - cpu_usage_per_core
    - cpu_count
    - cpu_load_1min
    - cpu_load_5min
    
    # Memory
    - ram_usage_percent
    - available_ram_mb
    - total_ram_mb
    - swap_usage_percent
    
    # Disk
    - disk_usage_percent
    - disk_read_bytes_per_sec
    - disk_write_bytes_per_sec
    
    # Network
    - network_bytes_sent_per_sec
    - network_bytes_recv_per_sec
    - active_connections
    
    # System
    - system_uptime_seconds
    - process_count

Disable system metrics:

yaml
system:
  enabled: false

Scrapers Configuration ​

The scrapers section defines external metric sources to collect from.

Basic Structure ​

yaml
scrapers:
  - name: scraper_name        # Unique identifier
    source:
      type: url|file|command  # Source type
      url: "..."              # For type: url
      path: "..."             # For type: file
      command: "..."          # For type: command
      format: json|prometheus|raw|log
      pattern: "..."          # For format: raw/log (optional)
      max_lines: 100          # For format: log (optional, default 100)
    metrics:
      - path: "..."           # For JSON format
        match: "..."          # For Prometheus/raw/log format
        name: "output_name"
        calculate: "..."      # Optional transformation
    filter:                   # Optional filtering
      include:
        - "pattern.*"
      exclude:
        - ".*exclude.*"
    push: false               # Optional; format: log only - see Push Mode

Source Types ​

URL Source ​

Fetch metrics from an HTTP endpoint.

yaml
scrapers:
  - name: api_metrics
    source:
      type: url
      url: "https://api.example.com/metrics"
      format: json

Command Source ​

Run a shell command and capture its combined stdout+stderr as the source text — useful for things with no file to point at, like docker logs.

yaml
scrapers:
  - name: docker_errors
    source:
      type: command
      command: "docker logs --since {{since}} --tail 1000 my-container 2>&1"
      format: log

Runs via sh -c on Linux/macOS or cmd /C on Windows. Only put trusted, operator-authored commands here — this isn't meant to run untrusted or request-derived input. See Log Parser below for what does.

File Source ​

Read metrics from a local file.

yaml
scrapers:
  - name: local_metrics
    source:
      type: file
      path: "/var/app/metrics.json"
      format: json

Format Types ​

JSON Format ​

Extract values from JSON using dot notation paths.

yaml
scrapers:
  - name: json_example
    source:
      type: url
      url: "http://localhost:8080/stats"
      format: json
    metrics:
      - path: "database.connections"
        name: "db_connections"
      - path: "cache.hit_rate"
        name: "cache_hit_percent"
        calculate: "value * 100"

Source JSON:

json
{
  "database": {
    "connections": 42
  },
  "cache": {
    "hit_rate": 0.95
  }
}

Output:

json
{
  "db_connections": 42,
  "cache_hit_percent": 95
}

Prometheus Format ​

Parse Prometheus exposition format.

yaml
scrapers:
  - name: node_exporter
    source:
      type: url
      url: "http://localhost:9100/metrics"
      format: prometheus
    metrics:
      - match: "node_cpu_seconds_total"
        name: "cpu_seconds"
      - match: "node_memory_MemAvailable_bytes"
        name: "memory_mb"
        calculate: "value / 1024 / 1024"

Source Prometheus:

# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 12345.67

# TYPE node_memory_MemAvailable_bytes gauge
node_memory_MemAvailable_bytes 4294967296

Output:

json
{
  "cpu_seconds": 12345.67,
  "memory_mb": 4096
}

Raw Format ​

Use regex patterns to extract key-value pairs from text.

yaml
scrapers:
  - name: sensors
    source:
      type: file
      path: "/data/sensors.txt"
      format: raw
      pattern: 'sensor_(\w+)=([0-9.]+)'  # Optional, default: (\w+)=(\S+)
    metrics:
      - match: "temperature"
        name: "temp_celsius"
      - match: "humidity"
        name: "humidity_percent"

Source text:

sensor_temperature=23.5
sensor_humidity=65.2
sensor_pressure=1013.25

Output:

json
{
  "temp_celsius": 23.5,
  "humidity_percent": 65.2
}

Log Format ​

Scans a text source line-by-line and returns the lines that look like errors, plus a count — built for grepping error lines out of log files or command output (an nginx error.log, docker logs, etc). Unlike raw, which collapses matches into single key/value pairs, log is built to return a list of matching lines.

yaml
scrapers:
  - name: nginx_errors
    source:
      type: file
      path: "/var/log/nginx/error.log"
      format: log
      max_lines: 50
      # pattern: '\[error\]'  # optional regex; default matches lines containing "error" (case-insensitive)
    metrics:
      - match: "count"
        name: "nginx_error_count"
      - match: "lines"
        name: "nginx_error_lines"

Output:

json
{
  "nginx_error_count": 1,
  "nginx_error_lines": [
    "2026/07/18 10:00:05 [error] 123#0: *2 open() \"/var/www/missing.html\" failed (2: No such file or directory)"
  ]
}

Only new lines are reported each scrape — not everything currently sitting in the log:

  • type: file sources are tailed automatically: probestyx remembers how far it has already read. The first scrape after startup seeds that position at the current end of the file (so it doesn't dump the whole historical log on boot) and reports nothing; every scrape after that only sees lines appended since the previous one. If the file shrinks (log rotation/truncation), it detects that and starts over from the beginning of the new file.
  • type: command sources (e.g. docker logs) have no seekable position, so this is opt-in via a placeholder in the command — it's substituted with the timestamp of that scraper's last successful run: docker logs --since --tail 1000 my-container 2>&1. Without in the command, it re-runs and re-scans the full output every time.
  • This tailing state lives in memory only, per scraper: a probestyx restart resets it to "start from now."

Per-Consumer Tailing ​

If more than one independent monitoring backend polls the same probestyx instance, they'd race over a single shared tailing position by default — whichever polls first after a new error line appears "consumes" it, and the other never sees it.

Declare the expected consumers in server.consumers, then have each pass its identity as ?callerId=:

yaml
server:
  consumers:
    - logstyx-prod
    - staging-poller
bash
curl "http://localhost:9100/metrics?callerId=logstyx-prod"
curl "http://localhost:9100/metrics?callerId=staging-poller"

Each declared callerId gets its own independent tailing position per scraper. A request with no callerId (or when server.consumers isn't set) falls back to a single shared "default" bucket — today's behavior if you never touch this. A callerId that isn't in server.consumers is rejected with 400 Bad Request rather than silently tracked, since /metrics is often unauthenticated and an open-ended ID namespace would let anyone grow the in-memory state without bound.

Most setups don't need this at all — see Integration, where one Logstyx probe polls one probestyx instance, which is exactly the single-consumer default case.

Under pull mode, a new error only reaches whoever's polling /metrics on their next scrape — up to a full poll interval of latency. If that's too slow, see Push Mode below: probestyx can send new lines to Logstyx itself, as they appear.

Metric Transformations ​

Use the calculate field to transform values:

yaml
metrics:
  # Convert bytes to megabytes
  - path: "memory.bytes"
    name: "memory_mb"
    calculate: "value / 1024 / 1024"
  
  # Convert ratio to percentage
  - path: "cache.hit_rate"
    name: "cache_hit_percent"
    calculate: "value * 100"
  
  # Convert seconds to milliseconds
  - path: "response.time"
    name: "response_ms"
    calculate: "value * 1000"
  
  # Add offset
  - path: "temperature.celsius"
    name: "temperature_kelvin"
    calculate: "value + 273.15"

Supported operators:

  • + (addition)
  • - (subtraction)
  • * (multiplication)
  • / (division)

Note: Only simple expressions with value and one operator are supported.

Filtering ​

Filter which metrics to include or exclude using regex patterns.

yaml
scrapers:
  - name: filtered_metrics
    source:
      type: url
      url: "http://localhost:9100/metrics"
      format: prometheus
    filter:
      include:
        - "^node_.*"          # Only metrics starting with "node_"
        - ".*memory.*"        # Metrics containing "memory"
      exclude:
        - ".*_bucket$"        # Exclude histogram buckets
        - ".*internal.*"      # Exclude internal metrics

How filtering works:

  1. If include patterns are specified, only metrics matching at least one pattern are kept
  2. Then, metrics matching any exclude pattern are removed
  3. If no include patterns are specified, all metrics pass (before exclusion)

Examples:

Include only network metrics:

yaml
filter:
  include:
    - "^network_.*"

Exclude debug and internal metrics:

yaml
filter:
  exclude:
    - ".*_debug$"
    - ".*internal.*"

Include node metrics but exclude histograms:

yaml
filter:
  include:
    - "^node_.*"
  exclude:
    - ".*_bucket$"
    - ".*_sum$"
    - ".*_count$"

Push Mode ​

Probestyx is normally pull-only: it exposes /metrics and waits to be polled. Push mode is an opt-in add-on for format: log scrapers specifically — instead of waiting for the next poll, probestyx detects new error lines itself and POSTs them straight to Logstyx's ingestion API, cutting latency from "up to one poll interval" down to about the length of a short debounce window.

How it decides when to push ​

  • type: file scrapers are event-driven: probestyx watches the log file's directory (not the file itself — this is what lets it survive log rotation, including the rename-then-recreate scheme, since a directory watch isn't tied to the old file's inode) and pushes shortly after new lines are written, debounced by ~200ms so a burst of many lines in one write collapses into a single push instead of one per line.
  • type: command scrapers (e.g. docker logs) have no file to watch, so they're re-run on a ticker instead — push.command_poll_interval_seconds (default 15).
  • Either way, one push is one scraper's new lines since the last trigger (matching the {count, lines} shape format: log already produces) — never one push per line, since there's no batch endpoint on the ingestion side and this keeps well under its rate limit during a burst.
  • Push maintains its own tailing cursor per scraper, completely independent of whatever polls /metrics — enabling push for a scraper doesn't change what /metrics reports for it, and vice versa. If a scraper is both pulled and pushed, the same lines appear through both channels independently.

Configuration ​

yaml
server:
  port: 9100

push:
  enabled: true
  endpoint: "https://api.logstyx.com/api/v1/logs"
  project_id: "your-project-id"
  secret: "your-project-secret"       # from the Logstyx dashboard - NOT the same as server.secret
  level: "error"                      # optional, default "error"
  context:                            # optional static tags attached to every push
    environment: "production"
    region: "us-east-1"

scrapers:
  - name: nginx_errors
    source:
      type: file
      path: "/var/log/nginx/error.log"
      format: log
      max_lines: 50
    push: true                        # opt this scraper into push mode

push.secret is deliberately a separate field from server.secret: server.secret authenticates callers of probestyx's own /metrics endpoint, while push.secret is the credential probestyx uses to authenticate itself to Logstyx. They're different keys for different directions and must never be the same value. push: true on a scraper only takes effect when its format is log and the global push.enabled is true — anything else is ignored with a startup warning, not a hard failure.

Reliability characteristics, plainly ​

  • In-memory retry queue only — failed pushes are retried with exponential backoff (1s up to 30s), but nothing is persisted to disk. A probestyx restart during a Logstyx outage loses whatever was queued, and (since tailing state is also in-memory) resets to "start from now" — it will not go back and re-send what it missed while it was down.
  • A 401 (bad secret/project_id) is treated as fatal, not retried: push logs one prominent warning, drops everything queued, and stops trying for the rest of that process's life, rather than silently retrying into a bounded queue forever. Restart probestyx after fixing the config.
  • A 200 response does not guarantee the log was actually stored — an unknown or mistyped project_id can be silently accepted as a fake success server-side and the log discarded, with no way to detect this from the response. Double-check project_id against the dashboard rather than trusting a lack of errors.
  • The queue is bounded (push.max_queue_size, default 256); if it fills up (e.g. a prolonged outage), the oldest pending item is dropped to make room, and this is logged.

Complete Examples ​

Minimal Configuration ​

Only collect basic system metrics:

yaml
server:
  port: 9100

system:
  enabled: true
  metrics:
    - cpu_usage_percent
    - ram_usage_percent
    - disk_usage_percent

With Authentication ​

Secure Probestyx with HMAC authentication:

yaml
server:
  port: 9100
  secret: "change-this-to-a-secure-secret"

system:
  enabled: true
  metrics:
    - cpu_usage_percent
    - ram_usage_percent

Multiple Scrapers ​

Collect from multiple sources:

yaml
server:
  port: 9100

system:
  enabled: true
  cache_ttl: 30
  metrics:
    - cpu_usage_percent
    - ram_usage_percent
    - disk_usage_percent

scrapers:
  # Application metrics from JSON API
  - name: app_metrics
    source:
      type: url
      url: "http://localhost:8080/metrics"
      format: json
    metrics:
      - path: "requests.total"
        name: "total_requests"
      - path: "requests.errors"
        name: "error_count"
      - path: "database.connections"
        name: "db_connections"
  
  # Node exporter metrics
  - name: node_exporter
    source:
      type: url
      url: "http://localhost:9100/metrics"
      format: prometheus
    metrics:
      - match: "node_memory_MemAvailable_bytes"
        name: "memory_available_mb"
        calculate: "value / 1024 / 1024"
    filter:
      include:
        - "^node_memory_.*"
  
  # Custom sensor data
  - name: sensors
    source:
      type: file
      path: "/var/sensors/data.txt"
      format: raw
      pattern: 'sensor_(\w+)=([0-9.]+)'
    metrics:
      - match: "temperature"
        name: "temperature_celsius"

High-Frequency Monitoring ​

Fast metrics collection with short cache:

yaml
server:
  port: 9100

system:
  enabled: true
  cache_ttl: 5  # Cache for only 5 seconds
  metrics:
    - cpu_usage_percent
    - cpu_usage_per_core
    - ram_usage_percent
    - disk_read_bytes_per_sec
    - disk_write_bytes_per_sec
    - network_bytes_sent_per_sec
    - network_bytes_recv_per_sec

Production Configuration ​

A complete production setup:

yaml
server:
  port: 9100
  secret: "use-a-strong-secret-here"

system:
  enabled: true
  cache_ttl: 60
  metrics:
    # CPU Metrics
    - cpu_usage_percent
    - cpu_count
    - cpu_load_1min
    - cpu_load_5min
    - cpu_load_15min
    
    # Memory Metrics
    - ram_usage_percent
    - available_ram_mb
    - total_ram_mb
    - swap_usage_percent
    
    # Disk Metrics
    - disk_usage_percent
    - available_disk_gb
    - total_disk_gb
    - disk_read_bytes_per_sec
    - disk_write_bytes_per_sec
    
    # Network Metrics
    - network_bytes_sent_per_sec
    - network_bytes_recv_per_sec
    - active_connections
    
    # System Info
    - system_uptime_seconds
    - hostname
    - process_count

scrapers:
  # Application metrics
  - name: application
    source:
      type: url
      url: "http://localhost:8080/metrics"
      format: json
    metrics:
      - path: "http.requests.total"
        name: "http_requests_total"
      - path: "http.requests.errors"
        name: "http_errors_total"
      - path: "http.response_time.avg"
        name: "http_response_time_ms"
        calculate: "value * 1000"
      - path: "database.pool.active"
        name: "db_connections_active"
      - path: "database.pool.idle"
        name: "db_connections_idle"
      - path: "cache.hit_rate"
        name: "cache_hit_percent"
        calculate: "value * 100"
  
  # Redis metrics
  - name: redis
    source:
      type: url
      url: "http://localhost:9121/metrics"
      format: prometheus
    metrics:
      - match: "redis_connected_clients"
        name: "redis_clients"
      - match: "redis_memory_used_bytes"
        name: "redis_memory_mb"
        calculate: "value / 1024 / 1024"
    filter:
      include:
        - "^redis_.*"
      exclude:
        - ".*_total$"

Error Log Monitoring ​

Grep nginx and Docker container logs for error lines, with each declared consumer tailing independently:

yaml
server:
  port: 9100
  consumers:
    - logstyx-prod

system:
  enabled: true
  metrics:
    - cpu_usage_percent
    - ram_usage_percent

scrapers:
  - name: nginx_errors
    source:
      type: file
      path: "/var/log/nginx/error.log"
      format: log
      max_lines: 50
    metrics:
      - match: "count"
        name: "nginx_error_count"
      - match: "lines"
        name: "nginx_error_lines"

  - name: docker_errors
    source:
      type: command
      command: "docker logs --since {{since}} --tail 1000 my-container 2>&1"
      format: log
      max_lines: 50
    metrics:
      - match: "count"
        name: "docker_error_count"
      - match: "lines"
        name: "docker_error_lines"
bash
curl "http://localhost:9100/metrics?callerId=logstyx-prod"

Authentication ​

Server Side ​

Enable authentication by setting a secret:

yaml
server:
  secret: "your-secure-secret-key"

Client Side ​

Clients must include authentication headers:

Bash example:

bash
#!/bin/bash

SECRET="your-secure-secret-key"
TIMESTAMP=$(date +%s)
SIGNATURE=$(echo -n "$TIMESTAMP" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)

curl -H "X-Timestamp: $TIMESTAMP" \
     -H "X-Signature: $SIGNATURE" \
     http://localhost:9100/metrics

Python example:

python
import time
import hmac
import hashlib
import requests

SECRET = "your-secure-secret-key"
timestamp = str(int(time.time()))
signature = hmac.new(
    SECRET.encode(),
    timestamp.encode(),
    hashlib.sha256
).hexdigest()

response = requests.get(
    "http://localhost:9100/metrics",
    headers={
        "X-Timestamp": timestamp,
        "X-Signature": signature
    }
)

Logstyx Integration:

When creating a probe in Logstyx, select Probestyx as the authentication type and enter your secret.

Validation ​

After creating or modifying config.yaml, validate the syntax:

Check YAML syntax:

bash
python3 -c "import yaml; yaml.safe_load(open('config.yaml'))"

Test Probestyx:

bash
# Start Probestyx manually to check for errors
/opt/probestyx/probestyx

# Expected: Server starts without errors
# Server listening on :9100

Troubleshooting ​

Invalid YAML Syntax ​

Error: yaml: unmarshal errors

  • Check indentation (use spaces, not tabs)
  • Ensure colons have spaces after them: key: value
  • Validate with a YAML linter

Metrics Not Appearing ​

  • Verify metric names in metrics list are correct
  • Check scraper source URL is accessible
  • Review logs for parsing errors
  • Test source URL manually: curl <url>

Authentication Failing ​

  • Ensure secret matches on both server and client
  • Check for extra spaces in secret value
  • Verify timestamp is current (within 5 minutes)

Next Steps ​

Released under the MIT License.