Server Monitoring Checklist for SMB Infrastructure

Published 9 min read
server monitoringsmb infrastructureobservabilityalertsincident response

Small and medium business teams do not need enterprise-grade observability platforms to achieve strong reliability. They need the right metrics, actionable alerts, clear dashboards, and a repeatable incident response loop. This checklist covers host metrics, application metrics, uptime monitoring, log aggregation, alert routing, on-call setup, and a practical tool comparison so your team can build a monitoring program that catches problems before customers do.

Why Monitoring Matters for SMBs

Without monitoring, your customers are your alerting system. They find the outage, they experience the slowness, and they leave. A basic monitoring setup for an SMB costs $0-50/month and catches 90% of issues before they become customer-visible.

The three goals of any monitoring program:

  • Reduce MTTD (Mean Time to Detect) -- find problems in minutes, not hours.
  • Reduce MTTR (Mean Time to Resolve) -- give responders the context to fix fast.
  • Prevent recurrence -- use data from incidents to improve infrastructure.
  • Host Metrics Checklist

    Every server in your infrastructure needs these five categories of host-level metrics:

    CPU

    <h1>Key metrics to track</h1>
    

    cpu_usage_percent # Overall CPU utilization

    cpu_iowait_percent # Time waiting for disk I/O (high = disk bottleneck)

    cpu_steal_percent # Time stolen by hypervisor (high = noisy neighbor on VPS)

    load_average_1m # 1-minute load average vs core count

    • Alert threshold: CPU above 85% for 5+ minutes.
    • Investigate: CPU steal above 10% means your VPS host is overcommitted.

    RAM

    memory_used_percent      # Actual memory usage
    

    memory_available_bytes # Memory available to applications

    swap_used_percent # Swap usage (any sustained swap = problem)

    oom_kill_count # OOM killer invocations

    • Alert threshold: Available memory below 15% for 5+ minutes. Any OOM kill is a SEV2.
    • Action: If swap is consistently used, you need more RAM or a memory leak investigation.

    Disk

    disk_used_percent        # Per-mount usage
    

    disk_iops_read # Read operations per second

    disk_iops_write # Write operations per second

    disk_latency_ms # Average I/O latency

    inode_used_percent # Inode usage (can run out before disk space)

    • Alert threshold: Disk above 80% or inodes above 80%. Disk latency above 20ms sustained.
    • Critical: Disk at 95% is an emergency -- services will crash.

    Network

    network_bytes_in         # Incoming bandwidth
    

    network_bytes_out # Outgoing bandwidth

    network_errors # Interface errors

    network_drops # Dropped packets

    tcp_connections_active # Active TCP connections

    • Alert threshold: Network errors or drops above 0 sustained. Connection count exceeding expected baseline by 3x.

    Saturation

    cpu_runqueue_length      # Processes waiting for CPU
    

    disk_queue_length # I/O requests waiting

    network_socket_overflow # TCP socket overflows

    • Why track saturation: A resource can be at 50% utilization but fully saturated if requests are queuing. Saturation metrics catch bottlenecks that utilization metrics miss.

    Application Metrics Checklist

    Host metrics tell you if the server is healthy. Application metrics tell you if the business is healthy.

    The Four Golden Signals

    SignalWhat it measuresExample metricAlert condition

    Request rateTraffic volume`http_requests_total`Drop below 50% of baseline
    Error rateFailure percentage`http_5xx_total / http_requests_total`Above 1% for 5 min
    LatencyResponse time`http_request_duration_p95`P95 above 500ms
    SaturationResource pressure`thread_pool_active / thread_pool_max`Above 80%

    Instrumenting Your Application

    For a Node.js/Express application:

    // Prometheus metrics middleware
    

    const promClient = require("prom-client");

    const httpRequestDuration = new promClient.Histogram({

    name: "http_request_duration_seconds",

    help: "Duration of HTTP requests in seconds",

    labelNames: ["method", "route", "status_code"],

    buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5]

    });

    const httpRequestTotal = new promClient.Counter({

    name: "http_requests_total",

    help: "Total HTTP requests",

    labelNames: ["method", "route", "status_code"]

    });

    app.use((req, res, next) => {

    const end = httpRequestDuration.startTimer();

    res.on("finish", () => {

    end({ method: req.method, route: req.route?.path || req.path, status_code: res.statusCode });

    httpRequestTotal.inc({ method: req.method, route: req.route?.path || req.path, status_code: res.statusCode });

    });

    next();

    });

    // Expose /metrics endpoint

    app.get("/metrics", async (req, res) => {

    res.set("Content-Type", promClient.register.contentType);

    res.end(await promClient.register.metrics());

    });

    For Python/FastAPI:

    from prometheus_client import Counter, Histogram, generate_latest
    

    from starlette.middleware.base import BaseHTTPMiddleware

    import time

    REQUEST_COUNT = Counter("http_requests_total", "Total requests", ["method", "endpoint", "status"])

    REQUEST_LATENCY = Histogram("http_request_duration_seconds", "Request latency", ["method", "endpoint"])

    class MetricsMiddleware(BaseHTTPMiddleware):

    async def dispatch(self, request, call_next):

    start = time.time()

    response = await call_next(request)

    duration = time.time() - start

    REQUEST_COUNT.labels(request.method, request.url.path, response.status_code).inc()

    REQUEST_LATENCY.labels(request.method, request.url.path).observe(duration)

    return response

    app.add_middleware(MetricsMiddleware)

    @app.get("/metrics")

    async def metrics():

    return Response(generate_latest(), media_type="text/plain")

    Uptime Monitoring

    External uptime monitoring checks your services from outside your infrastructure. If your server is down, internal monitoring cannot alert you.

    What to Monitor Externally

    • Homepage and critical pages: HTTP 200 check every 60 seconds.
    • API health endpoint: JSON response validation every 30 seconds.
    • SSL certificate expiry: Alert 30 days before expiration.
    • DNS resolution: Verify A/CNAME records resolve correctly.
    • Key user flows: Synthetic transactions (login, checkout) every 5 minutes.

    <h1>Simple uptime check script</h1>
    

    #!/bin/bash

    ENDPOINTS=(

    "https://example.com|200"

    "https://example.com/api/health|200"

    "https://api.example.com/v1/status|200"

    )

    for entry in "${ENDPOINTS[@]}"; do

    url=$(echo "$entry"

    cut -d"
    " -f1)

    expected=$(echo "$entry"

    cut -d"
    " -f2)

    status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url")

    if [[ "$status" != "$expected" ]]; then

    echo "ALERT: $url returned $status (expected $expected)"

    # Send alert via webhook, email, or PagerDuty

    fi

    done

    Log Aggregation

    Logs are the narrative of what happened. Metrics tell you something is wrong; logs tell you why.

    Log Aggregation Stack Options

    StackComponentsBest forMonthly cost (self-hosted)

    Loki + GrafanaPromtail, Loki, GrafanaTeams already using Grafana$0 (OSS) + compute
    ELKFilebeat, Elasticsearch, KibanaFull-text search heavy use$0 (OSS) + 4GB+ RAM
    Vector + ClickHouseVector, ClickHouse, GrafanaHigh volume, cost-sensitive$0 (OSS) + compute
    Managed (Datadog/New Relic)SaaSTeams that cannot self-host$50-200/month

    For most SMBs, Loki plus Grafana is the best starting point. It integrates with Prometheus dashboards and uses minimal resources.

    Structured Logging Best Practices

    {"timestamp":"2026-04-01T12:00:00Z","level":"error","service":"api","message":"payment processing failed","error_code":"STRIPE_TIMEOUT","user_id":12345,"duration_ms":5000,"trace_id":"abc123"}
    

    Rules for effective logging:

    • Always use structured format (JSON), never free-text.
    • Include a trace ID for request correlation across services.
    • Log at the right level: ERROR for failures, WARN for degradation, INFO for business events.
    • Never log passwords, tokens, PII, or credit card numbers.
    • Set retention policies: 7 days hot, 30 days warm, 90 days cold.

    Alert Routing and On-Call Setup

    Alerts are useless if they go to the wrong person or drown in noise.

    Alert Severity Framework

    SeverityDefinitionResponse timeNotificationExample

    SEV1Customer-facing outage5 minutesPhone + SMS + SlackSite down, data loss
    SEV2Degraded but functional30 minutesSlack + emailHigh error rate, slow API
    SEV3Non-urgent issueNext business dayEmail or ticketDisk at 75%, cert expiring

    Alert Routing Rules

    <h1>Example PagerDuty / Alertmanager routing</h1>
    

    routes:

    - match:

    severity: sev1

    receiver: oncall-phone

    repeat_interval: 5m

    - match:

    severity: sev2

    receiver: oncall-slack

    repeat_interval: 30m

    - match:

    severity: sev3

    receiver: team-email

    repeat_interval: 24h

    On-Call for Small Teams

    Teams of 2-5 engineers can sustain on-call with these rules:

    • Rotate weekly. Each person is on-call 1 week out of N (where N is team size).
    • Primary plus secondary on-call. Secondary is backup if primary does not acknowledge in 10 minutes.
    • Compensate on-call time. Either paid stipend or time off in lieu.
    • Review every alert in a weekly meeting. If an alert did not require action, tune or remove it.

    Monitoring Tool Comparison

    ToolTypeFree tierBest forComplexity

    Prometheus + GrafanaMetrics + dashboardsUnlimited (OSS)Teams with KubernetesMedium
    Uptime KumaUptime monitoringUnlimited (OSS)Simple uptime checksLow
    NetdataHost monitoringUnlimited (OSS)Single-server visibilityLow
    DatadogFull observability5 hosts freeTeams wanting SaaSLow
    New RelicFull observability100 GB/monthAPM-focused teamsLow
    Grafana CloudMetrics + logs10K metrics, 50 GB logsGrafana-native teamsMedium
    UptimeRobotExternal uptime50 monitorsBasic uptime alertingVery low

    Review how these tools integrate with your infrastructure at DevOps Tools and Services Overview.

    Dashboard Design

    Build three dashboards and resist the urge to add more until these are battle-tested:

    1. Executive Health Dashboard

    Shows overall system status. Green/yellow/red for each service. Uptime percentage for the month. Error budget remaining.

    2. On-Call Triage Dashboard

    Shows the four golden signals for each service. Active alerts with severity and duration. Recent deployments timeline. Quick links to logs and runbooks.

    3. Capacity Planning Dashboard

    Shows resource usage trends over 30/60/90 days. Projected exhaustion dates for disk, memory, and CPU. Cost trends and forecasts.

    Weekly and Monthly Cadence

    Weekly (30 minutes)

    • Review all alerts that fired: were they actionable? Tune noisy alerts.
    • Check for any unresolved SEV3 alerts.
    • Verify backup and restore jobs completed successfully.

    Monthly (1 hour)

    • Review SLO performance (did we meet our uptime and latency targets?).
    • Capacity planning: any resources projected to exhaust within 60 days?
    • Run one disaster recovery drill or restore test.
    • Update runbooks based on recent incidents.

    Frequently Asked Questions

    How many alerts should a small team have?

    Start with 5-10 alerts total. Each alert should map to a specific runbook and a specific action. If an engineer gets paged and the answer is "do nothing" or "I do not know what to do," the alert needs to be fixed or removed. Alert fatigue is the number one cause of monitoring failure in small teams.

    Do I need APM (Application Performance Monitoring)?

    Not initially. Start with the four golden signals (request rate, error rate, latency, saturation) using Prometheus or a similar tool. Add APM (distributed tracing, code-level profiling) when you have performance problems that the golden signals detect but cannot diagnose.

    What should I monitor first if I have nothing today?

    Set up three things in this order: (1) external uptime monitoring with UptimeRobot or similar, (2) host metrics with Netdata or node_exporter, (3) application error rate from your logs. These three give you 80% of the value for 20% of the effort.

    How do I avoid alert fatigue?

    Three rules: alert on symptoms (user impact) not causes (CPU usage), require every alert to have a runbook, and review alert noise weekly. Delete any alert that fires more than twice without requiring action.

    Should I use SaaS monitoring or self-hosted?

    If your team is under 5 engineers and nobody enjoys infrastructure work, use SaaS (Datadog, New Relic, Grafana Cloud). The free tiers are often sufficient for SMB needs. Self-host only if you have the skills and want to avoid vendor lock-in or data residency requirements.

    Final Takeaway

    A lightweight but disciplined monitoring program improves reliability faster than adding more tools. Start with host metrics, the four golden signals, external uptime checks, and 5-10 actionable alerts. Review weekly, plan capacity monthly, and iterate based on real incidents.

    For implementation support, contact us.