tutorial

Uptime monitoring for Nuclei vulnerability scanner infrastructure

Nuclei is a fast, template-driven vulnerability scanner used by security teams to assess their attack surface continuously. In production security workflows,...

Nuclei is a fast, template-driven vulnerability scanner used by security teams to assess their attack surface continuously. In production security workflows, Nuclei runs as a scheduled job or a persistent service — scanning targets on a cadence, feeding results into a SIEM, or powering an automated vulnerability management pipeline. When the infrastructure running Nuclei goes down, scheduled scans silently stop. Vulnerabilities accumulate undetected. Your security posture degrades without any signal.

This tutorial covers production-grade uptime monitoring for Nuclei infrastructure using Vigilmon. We will walk through:

  • Monitoring the services Nuclei depends on (API wrappers, result storage, scan schedulers)
  • Monitoring Nuclei scan health via custom health endpoints
  • Alerting on scan pipeline failures
  • Webhook alerts for DOWN/UP events
  • Docker and Kubernetes deployment monitoring

Prerequisites

  • Nuclei integrated into a scanning pipeline (scheduled, API-driven, or containerized)
  • A free account at vigilmon.online

Part 1: What to monitor in a Nuclei deployment

Nuclei itself is a CLI tool — it does not expose an HTTP server by default. What you monitor depends on how Nuclei is deployed:

| Deployment pattern | What to monitor | |--------------------|-----------------| | Nuclei API wrapper | The HTTP API exposing scan submission and results | | Scan scheduler (cron + Nuclei) | The scheduler's health endpoint or last-run timestamp | | Nuclei in Docker/K8s | The container/pod status and any sidecar health endpoint | | Nuclei → database pipeline | The database and result ingestion service | | Nuclei + nuclei-templates server | The template update server |

The most common production pattern is a REST API wrapper that accepts scan requests, runs Nuclei, and stores results. That wrapper is what you expose to Vigilmon.


Part 2: Add a health endpoint to your Nuclei API wrapper

If you manage a Nuclei API service, add a /health or /ping endpoint:

// nuclei-api/src/health.ts
import express from 'express';
import { execSync } from 'child_process';
import { checkDatabaseConnection } from './db';

const router = express.Router();

router.get('/health', async (req, res) => {
  const checks: Record<string, string> = {};

  // Check that the nuclei binary is reachable
  try {
    const version = execSync('nuclei -version 2>&1', { timeout: 5000 })
      .toString()
      .trim();
    checks.nuclei = version.includes('Nuclei Engine') ? 'ok' : 'degraded';
  } catch {
    checks.nuclei = 'unavailable';
  }

  // Check database connectivity
  try {
    await checkDatabaseConnection();
    checks.database = 'ok';
  } catch {
    checks.database = 'unavailable';
  }

  // Check template directory exists and is non-empty
  try {
    const count = execSync('ls ~/.local/nuclei-templates | wc -l', { timeout: 3000 })
      .toString()
      .trim();
    checks.templates = parseInt(count) > 100 ? 'ok' : 'stale';
  } catch {
    checks.templates = 'unavailable';
  }

  const allOk = Object.values(checks).every(v => v === 'ok');
  const status = allOk ? 'ok' : 'degraded';

  res.status(allOk ? 200 : 503).json({ status, checks });
});

export default router;

This endpoint returns:

{
  "status": "ok",
  "checks": {
    "nuclei": "ok",
    "database": "ok",
    "templates": "ok"
  }
}

Or on failure:

{
  "status": "degraded",
  "checks": {
    "nuclei": "ok",
    "database": "unavailable",
    "templates": "stale"
  }
}

Part 3: Monitor scan pipeline liveness with a heartbeat endpoint

For Nuclei deployments that run scheduled scans, add a "last scan" heartbeat endpoint that fails if no scan has completed within the expected window:

# nuclei_api/health.py (Flask)
from flask import Flask, jsonify
from datetime import datetime, timedelta
from models import db, ScanResult

app = Flask(__name__)

@app.route('/health/scan-liveness')
def scan_liveness():
    # Fail if no scan has completed in the last 6 hours
    threshold = datetime.utcnow() - timedelta(hours=6)
    last_scan = ScanResult.query.filter(
        ScanResult.completed_at >= threshold
    ).order_by(ScanResult.completed_at.desc()).first()

    if last_scan is None:
        return jsonify({
            'status': 'stale',
            'message': 'No scans completed in the last 6 hours'
        }), 503

    return jsonify({
        'status': 'ok',
        'last_scan_at': last_scan.completed_at.isoformat(),
        'targets_scanned': last_scan.target_count
    }), 200

This is the most valuable monitor for scheduled scan workflows — it detects silent cron failures, scan timeouts, and scheduler crashes that leave the API alive but scans stopped.


Part 4: Set up HTTP monitoring in Vigilmon

Monitor the Nuclei API health

  1. Log in to vigilmon.online and click Add Monitor.
  2. Choose HTTP(S) monitor.
  3. Enter: https://nuclei-api.example.com/health
  4. Set interval to 1 minute.
  5. Add a keyword check: must contain "status":"ok".
  6. Add your alert channel.
  7. Click Save.

Monitor scan pipeline liveness

  1. Click Add MonitorHTTP(S) monitor.
  2. Enter: https://nuclei-api.example.com/health/scan-liveness
  3. Set interval to 15 minutes (scans are not instant; polling every minute would be noisy).
  4. Add a keyword check: must contain "status":"ok".
  5. Click Save.

Monitor the result database

If your Nuclei results are stored in PostgreSQL, add a database-specific monitor:

  1. Click Add MonitorHTTP(S) monitor.
  2. Enter a database health proxy endpoint (e.g., via PgBouncer or a sidecar health check).
  3. Set interval to 2 minutes.
  4. Click Save.

| Monitor | URL | Interval | Check | |---------|-----|----------|-------| | Nuclei API | /health | 1 min | keyword: "status":"ok" | | Scan liveness | /health/scan-liveness | 15 min | keyword: "status":"ok" | | Result store | /health/db | 2 min | HTTP 200 |


Part 5: SSL certificate monitoring

If your Nuclei API is exposed over HTTPS, add an SSL monitor:

  1. In Vigilmon, click Add Monitor.
  2. Choose SSL monitor.
  3. Enter: nuclei-api.example.com
  4. Set alert threshold to 14 days before expiry.
  5. Add your alert channel.

Part 6: Webhook alerts

Configure Vigilmon webhooks to notify your security team when the scan pipeline fails:

// webhook-receiver.ts
import express from 'express';

const app = express();
app.use(express.json());

app.post('/webhook/vigilmon', (req, res) => {
  const { monitor_name, status, url, response_code, checked_at } = req.body;

  if (status === 'down') {
    console.error('[VIGILMON] Nuclei pipeline DOWN', {
      monitor: monitor_name,
      url,
      code: response_code,
      at: checked_at,
    });

    // Alert security team — unscanned assets accumulate risk
    notifySecurityTeam({
      severity: 'high',
      title: `Nuclei scan pipeline offline: ${monitor_name}`,
      body: `No vulnerability scans running since ${checked_at}. Assets are unmonitored.`,
      runbook: 'https://wiki.example.com/runbooks/nuclei-pipeline',
    });
  } else if (status === 'up') {
    console.info('[VIGILMON] Nuclei pipeline recovered', {
      monitor: monitor_name,
    });
  }

  res.sendStatus(204);
});

app.listen(3000);

Vigilmon payload:

{
  "monitor_id": "mon_abc123",
  "monitor_name": "Nuclei Scan Liveness",
  "status": "down",
  "url": "https://nuclei-api.example.com/health/scan-liveness",
  "checked_at": "2026-06-30T08:01:00Z",
  "response_code": 503,
  "response_time_ms": 312
}

Part 7: Docker deployments

# docker-compose.yml
version: "3.8"

services:
  nuclei-api:
    build: ./nuclei-api
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgres://nuclei:password@postgres:5432/nuclei_results
      - NUCLEI_TEMPLATES_PATH=/root/nuclei-templates
    volumes:
      - nuclei-templates:/root/nuclei-templates
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s

  template-updater:
    image: projectdiscovery/nuclei:latest
    entrypoint: ["/bin/sh", "-c"]
    command:
      - "while true; do nuclei -update-templates; sleep 3600; done"
    volumes:
      - nuclei-templates:/root/nuclei-templates

  postgres:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=nuclei_results
      - POSTGRES_USER=nuclei
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  nuclei-templates:
  postgres-data:

Check service health:

docker inspect nuclei-api --format='{{.State.Health.Status}}'

Part 8: Kubernetes deployments

For Nuclei running as a Kubernetes CronJob, add a liveness probe to the scanner pod and a separate Vigilmon monitor for the scan scheduler:

# nuclei-scanner-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: nuclei-scanner
spec:
  schedule: "0 */6 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: nuclei
              image: projectdiscovery/nuclei:latest
              args:
                - "-l"
                - "/targets/targets.txt"
                - "-t"
                - "cves/,exposures/"
                - "-o"
                - "/results/output.json"
                - "-json"
              volumeMounts:
                - name: targets
                  mountPath: /targets
                - name: results
                  mountPath: /results
          restartPolicy: OnFailure
          volumes:
            - name: targets
              configMap:
                name: nuclei-targets
            - name: results
              persistentVolumeClaim:
                claimName: nuclei-results-pvc

Monitor CronJob health via a sidecar or result-ingestor service:

# nuclei-health-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nuclei-health
spec:
  selector:
    app: nuclei-ingestor
  ports:
    - name: health
      port: 8080
      targetPort: 8080
  type: LoadBalancer

Point Vigilmon at the external IP of nuclei-health to monitor scan liveness from outside the cluster.


Summary

Your Nuclei scanning infrastructure now has layered monitoring:

  1. API health monitor — confirms the Nuclei API wrapper, binary availability, and database connectivity, polled every 60 seconds by Vigilmon.
  2. Scan liveness monitor — detects silent scan failures where the API is alive but no scans have completed within the expected window.
  3. SSL monitor — alerts before the API certificate expires.
  4. Webhook alerts — DOWN events trigger immediate escalation to your security team, since an offline scanner means unmonitored attack surface.

Security scanning infrastructure that runs silently and unmonitored defeats its own purpose. Vigilmon gives you independent external verification that Nuclei is scanning — so you know when it isn't.


Monitor your Nuclei scanning infrastructure free at vigilmon.online

#nuclei #vulnerability-scanning #security #devops #monitoring #appsec

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →