Files
portainer-core/docs/npm-logging-guide.md
T

8.6 KiB

NPM Logging & Audit Guide

Centralized logging for all external access Created: 2025-11-11

Why NPM is the Logging Hub

All public service access routes through NPM, which means:

  • Every external request is logged
  • Failed authentication attempts tracked
  • Rate limiting violations recorded
  • SSL certificate renewals logged
  • Configuration changes audited

Accessing NPM Logs

Via NPM UI

Access NPM admin panel:

View logs:

  1. Navigate to each Proxy Host
  2. Click "View Logs" button
  3. See real-time access logs
  4. Filter by status code, IP, user agent

Via Docker Logs

Real-time monitoring:

# All NPM logs
docker logs -f nginx-proxy-manager

# Filter for access logs only
docker logs -f nginx-proxy-manager 2>&1 | grep -i "access"

# Filter for errors
docker logs -f nginx-proxy-manager 2>&1 | grep -i "error"

# Filter for specific service (e.g., Jellyfin)
docker logs -f nginx-proxy-manager 2>&1 | grep "media.schweitz.net"

Via Log Files

Log location:

# Access logs stored in container volume
ls -lh ~/docker-data/nginx-proxy-manager/data/logs/

# View access logs
tail -f ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log

# View error logs
tail -f ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*_error.log

Log Format

Standard NPM access log:

192.0.2.1 - - [11/Nov/2025:21:30:15 +0100] "GET /api/users HTTP/2.0" 200 1234 "https://media.schweitz.net" "Mozilla/5.0..."

Fields:

  • IP address: Client IP (or Cloudflare IP if proxied)
  • Timestamp: When request occurred
  • HTTP method: GET, POST, etc.
  • Request path: /api/users
  • Protocol: HTTP/2.0
  • Status code: 200 (success), 404 (not found), 403 (forbidden), etc.
  • Bytes sent: Response size
  • Referer: Previous page
  • User agent: Browser/client info

Useful Log Queries

Find Failed Login Attempts

# Status codes 401 (unauthorized) or 403 (forbidden)
grep -E " (401|403) " ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log

# With IP addresses
grep -E " (401|403) " ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | awk '{print $1}' | sort | uniq -c | sort -nr

Monitor Specific Service Access

# Jellyfin access
grep "media.schweitz.net" ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | tail -20

# Nextcloud uploads (POST requests)
grep "cloud.schweitz.net" ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | grep "POST"

Identify High-Traffic IPs

# Top 10 IP addresses by request count
awk '{print $1}' ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | sort | uniq -c | sort -nr | head -10

Monitor SSL Certificate Activity

# Certificate renewal attempts
docker logs nginx-proxy-manager 2>&1 | grep -i "letsencrypt"

# Certificate errors
docker logs nginx-proxy-manager 2>&1 | grep -i "certificate" | grep -i "error"

Track API Usage

# API endpoint access
grep "/api/" ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log

# Specific API endpoint
grep "/api/login" ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log

Security Monitoring

Suspicious Activity Patterns

Brute force attempts:

# Multiple 401s from same IP (potential brute force)
grep " 401 " ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | \
  awk '{print $1}' | sort | uniq -c | sort -nr | \
  awk '$1 > 10 {print "Potential brute force from " $2 " (" $1 " attempts)"}'

Directory scanning:

# Looking for 404s (scanning for vulnerabilities)
grep " 404 " ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | \
  grep -E "(wp-admin|phpmyadmin|admin|login\.php)"

Unusual user agents:

# Non-browser requests (potential bots/scrapers)
grep -v "Mozilla" ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | \
  grep -v "curl" | tail -20

Log Rotation

Automatic rotation configuration:

NPM handles basic rotation, but for long-term storage:

# Create logrotate config
sudo tee /etc/logrotate.d/nginx-proxy-manager <<EOF
/home/jpmschweitzer/docker-data/nginx-proxy-manager/data/logs/*.log {
    daily
    rotate 30
    compress
    delaycompress
    notifempty
    missingok
    create 0644 root root
    postrotate
        docker exec nginx-proxy-manager nginx -s reload > /dev/null 2>&1 || true
    endscript
}
EOF

# Test logrotate config
sudo logrotate -d /etc/logrotate.d/nginx-proxy-manager

Manual log cleanup:

# Archive old logs
cd ~/docker-data/nginx-proxy-manager/data/logs/
tar -czf logs-archive-$(date +%Y%m%d).tar.gz *.log
mv logs-archive-*.tar.gz ~/backups/

# Clean logs older than 30 days
find ~/docker-data/nginx-proxy-manager/data/logs/ -name "*.log" -mtime +30 -delete

Centralized Logging (Future Enhancement)

Option 1: Ship logs to external service

Use a log aggregator like:

  • Loki + Grafana (self-hosted)
  • Elasticsearch + Kibana
  • Splunk
  • Cloud services (Datadog, Loggly)

Option 2: Syslog forwarding

Configure NPM to forward to syslog:

# Add to NPM custom nginx config
access_log syslog:server=10.99.0.1:514,tag=nginx combined;

Option 3: Promtail + Loki (Recommended)

Deploy Promtail container to tail NPM logs and send to Loki:

# Future: stacks/promtail.yml
services:
  promtail:
    image: grafana/promtail:latest
    volumes:
      - /home/jpmschweitzer/docker-data/nginx-proxy-manager/data/logs:/var/log/nginx
      - ./promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml

Compliance Logging

For audit trails, log these events:

Access Events

  • All successful logins (200 responses to /login endpoints)
  • Failed login attempts (401/403 responses)
  • File downloads (GET requests with large response sizes)
  • File uploads (POST/PUT requests)
  • API calls (requests to /api/ paths)

Security Events

  • SSL certificate renewals
  • Configuration changes in NPM
  • Rate limit violations
  • Blocked IPs (403 responses)

Monitoring Events

  • Service downtime (502/503 responses)
  • Slow responses (response time tracking)
  • High traffic patterns

Alerting Setup

Create alerts for critical events:

Using Uptime Kuma

Configure HTTP(s) monitors in Uptime Kuma:

  • Monitor each public service
  • Alert on downtime
  • Track response times

Using Custom Scripts

Example: Alert on failed logins:

#!/bin/bash
# /home/jpmschweitzer/scripts/monitor-failed-logins.sh

THRESHOLD=10
LOG_FILE="/home/jpmschweitzer/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log"

# Count 401s in last 5 minutes
RECENT_FAILURES=$(find ~/docker-data/nginx-proxy-manager/data/logs/ -name "proxy-host-*.log" -mmin -5 -exec grep -c " 401 " {} + | awk '{s+=$1} END {print s}')

if [ "$RECENT_FAILURES" -gt "$THRESHOLD" ]; then
    echo "ALERT: $RECENT_FAILURES failed login attempts in last 5 minutes" | \
        mail -s "Security Alert: High Failed Login Rate" admin@schweitz.net
fi

Run via cron:

*/5 * * * * /home/jpmschweitzer/scripts/monitor-failed-logins.sh

Performance Monitoring

Track service performance via logs:

Response Time Analysis

# Extract response times (if configured in NPM)
grep "upstream_response_time" ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | \
  awk '{print $NF}' | sort -n | tail -20

Bandwidth Usage

# Sum bytes sent per service
awk '{sum+=$10} END {print "Total bytes: " sum " (" sum/1024/1024 " MB)"}' \
  ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-media*.log

Most Accessed Endpoints

# Top 10 requested paths
awk '{print $7}' ~/docker-data/nginx-proxy-manager/data/logs/proxy-host-*.log | \
  sort | uniq -c | sort -nr | head -10

Best Practices

Regular Log Review

  • Check NPM logs weekly for suspicious activity
  • Review SSL certificate status monthly
  • Archive logs older than 30 days
  • Monitor for unusual traffic patterns

Retention Policy

  • Active logs: 30 days (on SSD)
  • Compressed archives: 1 year (on HDD /mnt/media/backups/logs/)
  • Long-term storage: Ship to external service if needed

Privacy Considerations

  • ⚠️ Logs contain IP addresses (PII in EU)
  • ⚠️ Don't log full request bodies (may contain passwords)
  • ⚠️ Rotate/delete old logs per privacy policy
  • ⚠️ Secure log access (only admins via VPN)

Summary:

  • All external access logged in NPM
  • Logs accessible via UI, Docker, or files
  • Use for security monitoring and audit trails
  • Set up alerts for critical events
  • Regular log review and rotation