This is a troubleshooting log template. Facts first, evidence second, conclusions last.
TL;DR
- Symptoms: The DNS service becomes unresponsive; the whole machine stutters/freezes; sometimes it looks like storage I/O issues or a “disk drop”.
- Pattern: Random, usually after 1+ day of continuous running.
- Recovery: Power off and let it cool down for a while, then it works again.
- Current conclusion (probabilistic): ____ (e.g., thermals / power / storage-controller reset).
Impact
- Direct impact: ____ (e.g., LAN-wide DNS timeouts).
- Indirect impact: ____ (services that depend on name resolution).
- Time window: ____ (start time / duration / auto-recovery?).
Environment (reproducible)
Hardware
- CPU: Intel® N100 (4C/4T, 6MB cache, up to 3.4GHz)
- GPU: Intel® UHD Graphics
- RAM: 8GB LPDDR5 (single channel, onboard, 4800MHz)
- Storage: UFS 2.1 256G
- Wireless: Intel AX200/201 (Wi‑Fi 6 / Bluetooth 5.2)
- Ethernet: 2.5G RJ45 ×1 (PoE IEEE 802.3at)
- Ports: USB 3.2 Gen2 Type‑A ×2; USB 3.2 Gen2 Type‑C ×1 (Alt DP/PD); HDMI ×1
- Power: 65W USB‑C Power Delivery adapter
Software
- Actual OS: Linux (current kernel:
6.8.12-5-pve; factory OS: Windows 11 Home) - Kernel (if Linux):
6.8.12-5-pve - Bootloader (GRUB):
grub2 2.06-13+pmx2 - DNS stack: ____ (dnsmasq / unbound / AdGuard Home / Pi‑hole / other)
- Deployment: ____ (systemd / Docker / other)
- Other services: ____
- Logging policy: ____ (write frequency / rotation / same disk?).
Problem statement (facts only)
- First seen: ____
- Frequency: about every ____ hours/days (random).
- Observable symptoms during failure:
- DNS: ____ (timeouts / refusal / high latency)
- System: ____ (SSH/RDP reachable? CPU/memory spike?)
- Storage: ____ (mount disappears / read-only remount / I/O error / device reset)
- Network: ____ (packet loss? NIC reset?)
- Recovery: ____ (power off for ____ minutes; does reboot also work: ____)
- Load correlation: ____ (QPS / logging / other tasks).
Immediate mitigation (timeline)
- T+0: ____ (e.g., switch upstream DNS)
- T+5m: ____ (stop DNS or high-IO workloads)
- T+10m: ____ (collect logs and metrics snapshots)
- Result: ____
Evidence model (align everything by timestamp)
- One timeline: failure start time, temperature peak, I/O errors, service timeouts.
- One core question: is DNS dead, or is the storage/system dead and DNS is just the first visible symptom?
Hypotheses (prioritized)
- H1: Thermals / overheating → throttling or protection → instability
- Evidence: ____
- H2: Power instability (PD/PoE/adapter) → controller reset
- Evidence: ____
- H3: Storage media/controller issue → I/O stall or device drop
- Evidence: ____
- H4: Software resource exhaustion (FD/memory/log amplification/IO saturation)
- Evidence: ____
- H5: Kernel/driver bug (power management, device PM policy)
- Evidence: ____
Data to capture (during failure)
- Temps/fans: ____ (
sensors,/sys/class/thermal/, Windows tools) - System logs: ____ (
journalctl, Windows Event Viewer) - Storage logs: ____ (I/O error, timeout, reset, read-only remount)
- Resource metrics: ____ (CPU/load/iowait/memory/disk throughput)
- DNS metrics: ____ (QPS, cache hit ratio, upstream latency, error rate)
- Client checks: ____ (
dig/nslookupoutputs)
Key evidence (3–5 snippets)
text[time] ____
text[time] ____
text[time] ____
Investigation (Attempt → Observation → Conclusion)
Attempt A: ____
- Change: ____
- Observation: ____
- Conclusion: ____
Attempt B: ____
- Change: ____
- Observation: ____
- Conclusion: ____
Attempt C: ____
- Change: ____
- Observation: ____
- Conclusion: ____
Final conclusion (probability, not absolute claims)
- Most likely root cause: ____
- Secondary factors: ____
- Ruled out: ____ (with evidence)
Fix plan (short-term vs long-term)
Short-term mitigation
- ____ (e.g., improve cooling)
- ____ (e.g., cap power / disable aggressive power saving)
- ____ (e.g., reduce disk writes / configure log rotation)
Long-term fix
- ____ (e.g., replace power adapter / replace storage / BIOS update)
- ____ (e.g., airflow redesign / thermal pads)
- ____ (e.g., kernel/OS change)
Validation
- Uptime: ____ days without recurrence
- Temperature: CPU ≤ ____°C; storage ≤ ____°C
- Logs: no I/O errors / resets / read-only remount
Retrospective
- Biggest learning: ____
- What I will do earlier next time: ____ (monitoring, alerting, auto log capture)
- Open questions: ____
Attempt plan: Monitor 192.168.22.18 (Minisforum) via a monitoring LXC (192.168.22.19) on PVE 192.168.22.12
Goal: turn “it feels like overheating / disk drop / DNS died” into an evidence-aligned timeline: temps + kernel/storage errors + real DNS query success/latency + host reachability.
Note: never put passwords into configs or the repo. Any credentials should live in root-only local files on the monitoring host.
Extra pitfall: port 22 is reachable, but ssh still hangs / drops (more common across subnets)
This one is misleading: nc says 22/tcp open, so people assume “network is fine”. But ssh can still stall during handshake and eventually time out (or die with Broken pipe).
Topology (what happened in this incident)
- Client subnet:
192.168.11.0/24 - Target host:
192.168.22.12(PVE) - Same-subnet control point:
192.168.22.18(SSH from here to.12is stable) - Correlated signal: DNS server (e.g.
192.168.22.53:53) experienced timeouts — reverse lookups can amplify SSH handshake latency.
Symptoms (typical)
nc -vz -G 2 192.168.22.12 22reports succeeded (TCP handshake is OK)ssh root@192.168.22.12hangs and later shows:ssh_dispatch_run_fatal: Connection to 192.168.22.12 port 22: Operation timed out- or
Read from remote host 192.168.22.12: Operation timed out/client_loop: send disconnect: Broken pipe
Quick triage (split “port is open” into smaller truths)
- Check whether the SSH banner is returned immediately (protocol only, no auth):
bashnc -v 192.168.22.12 22
You should quickly see SSH-2.0-OpenSSH_.... If the TCP connect succeeds but the banner is slow/missing, the issue is likely in sshd/system path (DNS reverse lookup stalls, high load, policy/ACL causing unstable data path), not “port blocked”.
- Capture where SSH stalls (useful evidence):
bashssh -vvv -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 root@192.168.22.12
Minimal fix (eliminate DNS/GSSAPI special cases first)
If you can SSH from a same-subnet host (e.g. .22.18) into .22.12, prefer the server-side fix:
- On
.22.12, edit/etc/ssh/sshd_configand make sure you have:
UseDNS noGSSAPIAuthentication no
- Validate and restart:
bashsshd -t && systemctl restart sshsystemctl status ssh --no-pager
Practical rationale: when DNS/reverse lookup is flaky,
UseDNS yescan block parts of the handshake/audit path. Disabling it removes an entire class of “it depends on DNS today” failures.
If it is still flaky: continue with the “cross-subnet triad”
- Return path routing (the target must know how to route back to
192.168.11.0/24):
baship route get 192.168.11.12
- Firewall/ACL/state tracking: allow not only
22/tcpinbound but also the correct return direction and conntrack behavior (especially with gateways/NAT/policy routing). - MTU/fragmentation: tunnels/PPPoE/VLAN paths can yield “SYN works but data is unstable”; validate with DF-sized pings on Linux and temporarily lower MSS/MTU to confirm.
Architecture (minimal, but sufficient)
- 192.168.22.19 (monitoring LXC on PVE 192.168.22.12): Prometheus + Grafana + blackbox_exporter (optional Pushgateway)
- 192.168.22.18 (target host): nodeexporter (host metrics) + (optional) smartctlexporter + custom textfile metrics (kernel/I/O error counter)
Data model:
- Time series (Prometheus): scrape every 15s, keep at least 48h around failures.
- Active probing (blackbox_exporter): probe
.18/.54from.19to separate “host down” from “service down”. - Alerting: pin the incident timestamp immediately.
Attempt D: Bring up the monitoring stack (Prometheus + Grafana + blackbox_exporter)
D0. Pre-checks (network & ports)
From 192.168.22.19, verify reachability to 192.168.22.18:
bashping 192.168.22.18Test-NetConnection 192.168.22.18 -Port 22
Ports used (defaults):
.18:9100node_exporter.18:9633smartctl_exporter (optional).19:9090Prometheus.19:3000Grafana.19:9115blackbox_exporter.19:9091Pushgateway (optional)
D1. Deploy node_exporter on 192.168.22.18 (systemd, stable)
Run on .18:
bashexport VER="1.7.0"curl -fsSL -o /tmp/node_exporter.tar.gz "https://github.com/prometheus/node_exporter/releases/download/v${VER}/node_exporter-${VER}.linux-amd64.tar.gz"tar -C /tmp -xzf /tmp/node_exporter.tar.gzinstall -m 0755 /tmp/node_exporter-${VER}.linux-amd64/node_exporter /usr/local/bin/node_exporteruseradd --system --no-create-home --shell /usr/sbin/nologin nodeexp || truemkdir -p /var/lib/node_exporter/textfile_collectorchown -R nodeexp:nodeexp /var/lib/node_exportercat >/etc/systemd/system/node_exporter.service <<'EOF'[Unit]Description=Prometheus Node ExporterAfter=network-online.targetWants=network-online.target[Service]User=nodeexpGroup=nodeexpExecStart=/usr/local/bin/node_exporter \--web.listen-address=:9100 \--collector.textfile.directory=/var/lib/node_exporter/textfile_collectorRestart=on-failureRestartSec=2[Install]WantedBy=multi-user.targetEOFsystemctl daemon-reloadsystemctl enable --now node_exportersystemctl status node_exporter --no-pager
Verify (from .12 or any LAN host):
bashcurl -fsS http://192.168.22.18:9100/metrics | head
D2. (Recommended) Add a kernel/storage error counter as a custom metric
The keywords that matter:
I/O error|timeout|reset|remount read-only|EXT4-fs error|BTRFS error|blk_update_request
Use node_exporter textfile collector and a timer:
bashcat >/usr/local/bin/collect_kernel_error_metrics.sh <<'EOF'#!/usr/bin/env bashset -euo pipefailOUT="/var/lib/node_exporter/textfile_collector/kernel_errors.prom"TMP="$(mktemp)"COUNT="$(journalctl -k --since "10 min ago" --no-pager 2>/dev/null | \grep -Eic 'I/O error|timeout|reset|remount read-only|EXT4-fs error|BTRFS error|blk_update_request|nvme|ufs' || true)"NOW="$(date +%s)"{echo "# HELP kernel_error_events_10m Number of kernel error-like events in last 10 minutes"echo "# TYPE kernel_error_events_10m gauge"echo "kernel_error_events_10m ${COUNT}"echo "# HELP kernel_error_scrape_time_seconds Last collect time"echo "# TYPE kernel_error_scrape_time_seconds gauge"echo "kernel_error_scrape_time_seconds ${NOW}"} >"${TMP}"mv "${TMP}" "${OUT}"EOFchmod +x /usr/local/bin/collect_kernel_error_metrics.shcat >/etc/systemd/system/kernel-error-metrics.service <<'EOF'[Unit]Description=Collect kernel error metrics for node_exporter textfile collector[Service]Type=oneshotExecStart=/usr/local/bin/collect_kernel_error_metrics.shEOFcat >/etc/systemd/system/kernel-error-metrics.timer <<'EOF'[Unit]Description=Run kernel error metrics collector every 60s[Timer]OnBootSec=30sOnUnitActiveSec=60sAccuracySec=1s[Install]WantedBy=timers.targetEOFsystemctl daemon-reloadsystemctl enable --now kernel-error-metrics.timer
Verify:
bashcurl -fsS http://192.168.22.18:9100/metrics | grep -E '^kernel_error_events_10m|^kernel_error_scrape_time_seconds' || true
D3. (Optional) smartctl_exporter for disk health
If the storage device does not expose SMART/health, this may not work. Still worth trying.
bashapt-get updateapt-get install -y smartmontoolsexport VER="0.12.0"curl -fsSL -o /tmp/smartctl_exporter.tar.gz "https://github.com/prometheus-community/smartctl_exporter/releases/download/v${VER}/smartctl_exporter-${VER}.linux-amd64.tar.gz"tar -C /tmp -xzf /tmp/smartctl_exporter.tar.gzinstall -m 0755 /tmp/smartctl_exporter-${VER}.linux-amd64/smartctl_exporter /usr/local/bin/smartctl_exporteruseradd --system --no-create-home --shell /usr/sbin/nologin smartctl-exp || truecat >/etc/systemd/system/smartctl_exporter.service <<'EOF'[Unit]Description=Prometheus Smartctl ExporterAfter=network-online.targetWants=network-online.target[Service]User=smartctl-expGroup=smartctl-expExecStart=/usr/local/bin/smartctl_exporter --web.listen-address=:9633Restart=on-failureRestartSec=2[Install]WantedBy=multi-user.targetEOFsystemctl daemon-reloadsystemctl enable --now smartctl_exporter
D4. Deploy Prometheus + Grafana + blackbox_exporter on 192.168.22.12 (PVE) via LXC (recommended)
Since 192.168.22.12 is also a PVE host, avoid piling Docker onto the host OS. The simplest operationally is: one Debian LXC container running the monitoring stack with systemd.
D4.1 Create a Debian LXC container on PVE (.12)
You can do it in the PVE Web UI, or via CLI like this (replace <CTID>, <GW>; LXC IP is pinned to 192.168.22.19):
bash# Run on the PVE host 192.168.22.12pct create <CTID> local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \--hostname monitoring \--cores 2 --memory 2048 --swap 512 \--rootfs local-lvm:8 \--net0 name=eth0,bridge=vmbr0,ip=192.168.22.19/24,gw=<GW> \--unprivileged 1pct set <CTID> -features keyctl=1,nesting=1pct start <CTID>
D4.2 Install Prometheus in the container (systemd)
Enter the container:
bashpct exec <CTID> -- bash
Inside the container:
bashapt-get updateapt-get install -y ca-certificates curl taruseradd --system --no-create-home --shell /usr/sbin/nologin prometheus || truemkdir -p /etc/prometheus /var/lib/prometheuschown -R prometheus:prometheus /etc/prometheus /var/lib/prometheusexport VER="2.54.1"curl -fsSL -o /tmp/prometheus.tar.gz "https://github.com/prometheus/prometheus/releases/download/v${VER}/prometheus-${VER}.linux-amd64.tar.gz"tar -C /tmp -xzf /tmp/prometheus.tar.gzinstall -m 0755 /tmp/prometheus-${VER}.linux-amd64/prometheus /usr/local/bin/prometheusinstall -m 0755 /tmp/prometheus-${VER}.linux-amd64/promtool /usr/local/bin/promtoolcp -r /tmp/prometheus-${VER}.linux-amd64/consoles /etc/prometheus/cp -r /tmp/prometheus-${VER}.linux-amd64/console_libraries /etc/prometheus/chown -R prometheus:prometheus /etc/prometheus
Create /etc/prometheus/prometheus.yml (scrape .18 + active probes; DNS is the AdGuardDNS LXC at .54):
yamlglobal:scrape_interval: 15sevaluation_interval: 15sscrape_configs:- job_name: "minisforum-node"static_configs:- targets: ["192.168.22.18:9100"]- job_name: "minisforum-smartctl"static_configs:- targets: ["192.168.22.18:9633"]- job_name: "blackbox-icmp"metrics_path: /probeparams:module: [icmp]static_configs:- targets:- 192.168.22.18- 192.168.22.54relabel_configs:- source_labels: [__address__]target_label: __param_target- source_labels: [__param_target]target_label: instance- target_label: __address__replacement: 127.0.0.1:9115- job_name: "blackbox-tcp"metrics_path: /probeparams:module: [tcp_connect]static_configs:- targets:- 192.168.22.18:22- 192.168.22.18:9100- 192.168.22.54:53- 192.168.22.54:3000relabel_configs:- source_labels: [__address__]target_label: __param_target- source_labels: [__param_target]target_label: instance- target_label: __address__replacement: 127.0.0.1:9115
Create the systemd service:
bashcat >/etc/systemd/system/prometheus.service <<'EOF'[Unit]Description=PrometheusAfter=network-online.targetWants=network-online.target[Service]User=prometheusGroup=prometheusExecStart=/usr/local/bin/prometheus \--config.file=/etc/prometheus/prometheus.yml \--storage.tsdb.path=/var/lib/prometheus \--storage.tsdb.retention.time=30dRestart=on-failureRestartSec=2[Install]WantedBy=multi-user.targetEOFsystemctl daemon-reloadsystemctl enable --now prometheus
D4.3 Install blackbox_exporter in the container (systemd)
bashuseradd --system --no-create-home --shell /usr/sbin/nologin blackbox || truemkdir -p /etc/blackbox_exporterchown -R blackbox:blackbox /etc/blackbox_exporterexport VER="0.25.0"curl -fsSL -o /tmp/blackbox.tar.gz "https://github.com/prometheus/blackbox_exporter/releases/download/v${VER}/blackbox_exporter-${VER}.linux-amd64.tar.gz"tar -C /tmp -xzf /tmp/blackbox.tar.gzinstall -m 0755 /tmp/blackbox_exporter-${VER}.linux-amd64/blackbox_exporter /usr/local/bin/blackbox_exportercat >/etc/blackbox_exporter/blackbox.yml <<'EOF'modules:icmp:prober: icmptimeout: 5stcp_connect:prober: tcptimeout: 5sEOFchown -R blackbox:blackbox /etc/blackbox_exportercat >/etc/systemd/system/blackbox_exporter.service <<'EOF'[Unit]Description=Prometheus Blackbox ExporterAfter=network-online.targetWants=network-online.target[Service]User=blackboxGroup=blackboxExecStart=/usr/local/bin/blackbox_exporter \--config.file=/etc/blackbox_exporter/blackbox.yml \--web.listen-address=:9115Restart=on-failureRestartSec=2[Install]WantedBy=multi-user.targetEOFsystemctl daemon-reloadsystemctl enable --now blackbox_exporter
D4.4 Install Grafana in the container (systemd)
Do not commit any Grafana admin password. Install it, then set the password manually.
bashapt-get install -y apt-transport-https software-properties-common wget gpgmkdir -p /etc/apt/keyringswget -qO- https://apt.grafana.com/gpg.key | gpg --dearmor >/etc/apt/keyrings/grafana.gpgecho "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" >/etc/apt/sources.list.d/grafana.listapt-get updateapt-get install -y grafanasystemctl enable --now grafana-server
Reset admin password (run in the container):
bashgrafana-cli admin reset-admin-password
Attempt E: Deploy AdGuardDNS on 192.168.22.18 via LXC (fixed IP: 192.168.22.54) + real DNS query probing
Goal: isolate DNS service from the host and pin the DNS IP to 192.168.22.54 so monitoring targets never drift.
E0. Create the AdGuardDNS LXC container on PVE (.18) with IP 192.168.22.54
Example (replace <CTID>, <GW>):
bash# Run on the PVE host 192.168.22.18pct create <CTID> local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \--hostname adguarddns \--cores 1 --memory 1024 --swap 256 \--rootfs local-lvm:4 \--net0 name=eth0,bridge=vmbr0,ip=192.168.22.54/24,gw=<GW> \--unprivileged 1pct set <CTID> -features keyctl=1,nesting=1pct start <CTID>
E1. Install AdGuard Home inside the container (as AdGuardDNS)
Enter the container:
bashpct exec <CTID> -- bash
Inside the container:
bashapt-get updateapt-get install -y ca-certificates curl tarexport VER="0.107.57"curl -fsSL -o /tmp/adguardhome.tar.gz "https://github.com/AdguardTeam/AdGuardHome/releases/download/v${VER}/AdGuardHome_linux_amd64.tar.gz"tar -C /opt -xzf /tmp/adguardhome.tar.gz/opt/AdGuardHome/AdGuardHome -s installsystemctl status AdGuardHome --no-pager
First-time setup:
- Admin UI:
http://192.168.22.54:3000 - DNS:
192.168.22.54:53(UDP/TCP)
E1. Real DNS query probe using dig + Pushgateway (credential-free)
Use .12 to run dig periodically and push two metrics: success and latency.
- Optionally install Pushgateway on the monitoring side (the
.12monitoring LXC) via systemd:
bashapt-get updateapt-get install -y ca-certificates curl taruseradd --system --no-create-home --shell /usr/sbin/nologin pushgateway || truemkdir -p /etc/pushgateway /var/lib/pushgatewaychown -R pushgateway:pushgateway /etc/pushgateway /var/lib/pushgatewayexport VER="1.10.0"curl -fsSL -o /tmp/pushgateway.tar.gz "https://github.com/prometheus/pushgateway/releases/download/v${VER}/pushgateway-${VER}.linux-amd64.tar.gz"tar -C /tmp -xzf /tmp/pushgateway.tar.gzinstall -m 0755 /tmp/pushgateway-${VER}.linux-amd64/pushgateway /usr/local/bin/pushgatewaycat >/etc/systemd/system/pushgateway.service <<'EOF'[Unit]Description=Prometheus PushgatewayAfter=network-online.targetWants=network-online.target[Service]User=pushgatewayGroup=pushgatewayExecStart=/usr/local/bin/pushgateway --web.listen-address=:9091Restart=on-failureRestartSec=2[Install]WantedBy=multi-user.targetEOFsystemctl daemon-reloadsystemctl enable --now pushgatewaysystemctl status pushgateway --no-pager
- Add a Prometheus scrape job (use
127.0.0.1since Pushgateway runs on the same container):
yaml- job_name: "pushgateway"honor_labels: truestatic_configs:- targets: ["127.0.0.1:9091"]
- Create a probe script (DNS IP is pinned to
192.168.22.54):
bashcat >/opt/monitoring/dns_probe_push.sh <<'EOF'#!/usr/bin/env bashset -euo pipefailDNS_IP="192.168.22.54"NAME="www.baidu.com"PUSH_URL="http://127.0.0.1:9091/metrics/job/dns_probe/instance/${DNS_IP}"OUT="$(dig +tries=1 +time=2 +stats @"${DNS_IP}" "${NAME}" A 2>/dev/null || true)"if echo "${OUT}" | grep -qE '^;; ANSWER SECTION:'; thenSUCCESS=1elseSUCCESS=0fiLAT_MS="$(echo "${OUT}" | awk -F': ' '/^;; Query time:/{print $2}' | awk '{print $1}' || true)"LAT_MS="${LAT_MS:-0}"cat <<METRICS | curl -fsS --data-binary @- "${PUSH_URL}" >/dev/null# TYPE dns_probe_success gaugedns_probe_success ${SUCCESS}# TYPE dns_probe_latency_ms gaugedns_probe_latency_ms ${LAT_MS}METRICSEOFchmod +x /opt/monitoring/dns_probe_push.sh
- Schedule it (during investigation, 15s is useful):
bashcrontab -e*/1 * * * * /opt/monitoring/dns_probe_push.sh*/1 * * * * sleep 15; /opt/monitoring/dns_probe_push.sh*/1 * * * * sleep 30; /opt/monitoring/dns_probe_push.sh*/1 * * * * sleep 45; /opt/monitoring/dns_probe_push.sh
E2. (Optional) AdGuard Home internal stats (requires admin API creds; never commit them)
If you need AdGuard-specific stats (blocked/allowed/qps/upstream latency), run an exporter on .12 that talks to AdGuard’s admin API.
Store credentials in a root-only local file (e.g. /opt/monitoring/secrets/adguard.env, chmod 600) and keep it out of Git.
Comments