Prometheus & Grafana
Node64 exposes its metrics at /api/metrics in the Prometheus text format – from the core through health checks, DynDNS, DNS64, PROXY64 and CDN64 to certificates. Plug your node into your existing monitoring, build Grafana dashboards and write your own alerting rules.
Overview
| What | Value |
|---|---|
| Endpoint | GET /api/metrics |
| Format | Prometheus text format 0.0.4 (text/plain; version=0.0.4), never cached |
| Authentication | API key as bearer token (or a browser session) |
| Contents | the core and every running module; disabled modules only report node64_module_running{module="…"} 0 |
| Duration | a few milliseconds per scrape |
| Interval | 15 to 60 seconds works well |
Setup
- Under the API menu item (
/api/docs, section API keys), create a dedicated key, for example namedprometheus. More in Local API. - Save the key on your Prometheus server in a file such as
/etc/prometheus/node64.key. The file contains nothing but the key (node64_…). - Add a scrape job to your
prometheus.ymland reload Prometheus.
scrape_configs:
- job_name: node64
scheme: https # or http, depending on how you open the web interface
metrics_path: /api/metrics
authorization:
type: Bearer
credentials_file: /etc/prometheus/node64.key # contains: node64_...
tls_config:
insecure_skip_verify: true # only with the web interface's self-signed certificate
static_configs:
- targets: ["node64.lan:8080"]Node64 speaks HTTP and HTTPS on the same port. If the web interface has a proper certificate (see Certificates), drop insecure_skip_verify. Every node has its own API keys, so for several nodes create one scrape job per node, each with its own key file.
Quick manual test:
curl -s -H "Authorization: Bearer $(cat node64.key)" http://node64.lan:8080/api/metrics | headAuthentication and protection
/api/metricsis protected like the rest of the API: without a key you get401.- Every scrape updates the key's "last used" – so the key list tells you whether Prometheus is still scraping.
- Node64 slows down invalid keys just like for the rest of the API (after 5 failed attempts, only one attempt every 30 seconds).
- Use a key just for Prometheus. If you no longer need it, delete it without breaking any other scripts.
All metrics
Points in time are Unix time in seconds (suffix _seconds), durations are in seconds, data volumes in bytes. Metrics that are "always 1" carry their information in a label – count them with count or sum.
Core
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_info | Gauge | version, node_id, node_name, hostname | always 1 |
node64_start_time_seconds | Gauge | process start time | |
node64_uptime_seconds | Gauge | time since start | |
node64_database_bytes | Gauge | database size including WAL | |
node64_module_enabled | Gauge | module | 1 = enabled in the settings |
node64_module_running | Gauge | module | 1 = module is running |
node64_module_error | Gauge | module | 1 = module reports an error |
node64_module_info | Gauge | module, version | always 1, module version |
node64_link_linked | Gauge | 1 = linked to an ipv64.net account (health checks sync) | |
node64_link_connected | Gauge | 1 = connection to ipv64.net is up | |
node64_link_outbox | Gauge | entries not yet transferred | |
node64_link_conflicts | Gauge | open conflicts | |
node64_link_last_sync_seconds | Gauge | last successful sync | |
node64_link_online_checks_today | Gauge | online measurements used today (linked only) | |
node64_link_online_checks_limit | Gauge | daily limit of online measurements, 0 = none (linked only) | |
node64_group_members | Gauge | members of the cluster (excluding this node) | |
node64_group_members_online | Gauge | of which reachable | |
node64_group_member_up | Gauge | node_id, node_name | 1 = member reachable |
node64_mcp_enabled | Gauge | 1 = AI interface (MCP) switched on | |
node64_mcp_calls_total | Counter | tool, result | tool calls via MCP since start, result = ok or error |
go_goroutines | Gauge | number of goroutines | |
go_memstats_heap_alloc_bytes | Gauge | allocated heap | |
go_memstats_sys_bytes | Gauge | memory obtained from the operating system | |
go_gc_cycles_total | Counter | completed garbage collection cycles | |
node64_scrape_duration_seconds | Gauge | duration of this scrape |
Health checks
Labels per check: token (stays the same even if the name changes), check (name), type and group.
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_hc_check_state | Gauge | check, state | always 1, state in the label: up, down, degraded, flapping, dependent, stabilizing, paused, pending, unchecked |
node64_hc_check_up | Gauge | check | 1 = reachable (up, degraded), 0 = down; missing when paused, pending or unchecked |
node64_hc_check_maintenance | Gauge | check | 1 = currently in maintenance |
node64_hc_check_last_seconds | Gauge | check | time of the last measurement |
node64_hc_check_latency_seconds | Gauge | check | response time of the last measurement |
node64_hc_check_consecutive_failures | Gauge | check | consecutive failures |
node64_hc_check_interval_seconds | Gauge | check | check interval |
node64_hc_check_cert_expiry_seconds | Gauge | check | TLS certificate expiry (HTTPS checks only) |
node64_hc_checks | Gauge | state | number of checks per state; maintenance is counted additionally |
node64_hc_incidents_open | Gauge | open incidents |
Proxmox VE
Per system (system), node and guest: node64_pve_up, node64_pve_error, node64_pve_poll_failures_total, node64_pve_quorate, node64_pve_guests_running, node64_pve_guests_bad, node64_pve_node_up, node64_pve_guest_state, node64_pve_guest_up plus CPU, RAM and disk as a ratio 0..1 (node64_pve_node_*_ratio, node64_pve_guest_*_ratio), and per guest network and disk I/O in bytes/s (node64_pve_guest_net_*_bytes, node64_pve_guest_disk_*_bytes), RAM in bytes and CPUs (node64_pve_guest_memory_bytes, node64_pve_guest_cpus). The full list is under Proxmox VE.
Docker
For Docker monitoring. Per host: node64_docker_up, node64_docker_enabled, node64_docker_poll_failures_total, node64_docker_last_poll_seconds, node64_docker_containers_running, node64_docker_containers_bad. Per container: node64_docker_container_state{state,detail}, …_container_up, …_container_restarts, …_container_cpu_ratio, …_container_cpus, …_container_memory_ratio, …_container_memory_bytes, …_container_memory_limit_bytes, …_container_uptime_seconds, …_container_net_in_bytes, …_container_net_out_bytes, …_container_disk_read_bytes and …_container_disk_write_bytes.
DynDNS
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_dyndns_target_status | Gauge | target, status | always 1, state in the label: ok, cdn, pending, dns, error, paused, off, new |
node64_dyndns_target_ok | Gauge | target | 1 = last update succeeded, 0 = error; missing when off, paused or new |
node64_dyndns_target_last_ok_seconds | Gauge | target | last successful update |
node64_dyndns_target_last_error_seconds | Gauge | target | last error |
node64_dyndns_targets | Gauge | status | number of targets per state |
node64_dyndns_updates_24h | Gauge | calls to ipv64.net in the last 24 hours |
DNS64
The counters run since the DNS server last started. Changing the settings restarts it – rate() and increase() handle that just fine.
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_dns64_up | Gauge | 1 = DNS server running | |
node64_dns64_start_time_seconds | Gauge | start of the DNS server | |
node64_dns64_blocking_paused | Gauge | 1 = blocking paused | |
node64_dns64_queries_total | Counter | status, proto | queries; status: allowed, cached, blocked, local, error, custom; proto: udp, tcp, doh, dot, doq |
node64_dns64_query_duration_seconds_sum, …_count | Counter | sum and count of response times (average via rate(sum)/rate(count)) | |
node64_dns64_querylog_dropped_total | Counter | entries not written to the query log | |
node64_dns64_refused_total | Counter | refused because they came from outside the allowed networks | |
node64_dns64_blocklist_domains | Gauge | domains in the blocklists | |
node64_dns64_cache_entries, node64_dns64_cache_max_entries | Gauge | entries in the cache and its size | |
node64_dns64_cache_hits_total, …_misses_total, …_stale_served_total | Counter | cache hits, misses and answers served stale | |
node64_dns64_upstream_up | Gauge | upstream, proto | 1 = upstream healthy |
node64_dns64_upstream_requests_total, node64_dns64_upstream_errors_total | Counter | upstream, proto | requests to the upstream and errors |
node64_dns64_upstream_latency_seconds | Gauge | upstream, proto | average upstream response time |
node64_dns64_group_queries_total | Counter | group, status | queries per device group and result |
node64_dns64_group_internet_off | Gauge | group | 1 = internet off for the group (button or schedule) |
node64_dns64_group_free | Gauge | group | 1 = "Free" button active (schedules and categories suspended) |
node64_dns64_group_members | Gauge | group | entries (MAC, IP, network) of the group |
node64_dns64_forward_up | Gauge | domain, server | forward server healthy (1/0) |
node64_dns64_forward_requests_total | Counter | domain, server | requests to the forward server |
node64_dns64_forward_errors_total | Counter | domain, server | errors of the forward server |
PROXY64 and CDN64
Labels per host: host_id (stays the same), host (first name) and kind (proxy, redirect, dead, passthrough).
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_cdn64_hosts | Gauge | number of hosts | |
node64_cdn64_host_enabled | Gauge | host | 1 = enabled |
node64_cdn64_host_requests_total | Counter | host | requests since the node started |
node64_cdn64_host_errors_total | Counter | host | errors (5xx, target unreachable) since start |
node64_cdn64_host_blocked_total | Counter | host | rejected requests since start |
node64_cdn64_host_requests_24h | Gauge | host, status (2xx … 5xx) | requests in the last 24 hours per status class |
node64_cdn64_host_bytes_24h | Gauge | host | bytes sent in the last 24 hours |
node64_cdn64_host_blocked_24h | Gauge | host | rejected requests in the last 24 hours |
node64_cdn64_host_target_up | Gauge | host | 1 = target reachable at the last check (proxy and TLS passthrough only) |
node64_cdn64_host_target_latency_seconds | Gauge | host | connect time to the target |
node64_cdn64_honeypot_blocks, …_blocks_total, …_rejected_total, …_decoy_total, …_reported_total | Gauge, Counter | working with Honeypot64: active instant blocks, blocks, rejections, requests passed to the bait and reported (only when a switch is on) |
CDN64 – these values come from the node's last sync with ipv64 (every minute):
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_cdn64_online_name | Gauge | host_id, name, mode | always 1: name is enabled online (mode 1 = passthrough, 2 = terminate) |
node64_cdn64_online_edge_connected | Gauge | edge | 1 = tunnel to this edge server connected |
node64_cdn64_online_edge_rtt_seconds | Gauge | edge, transport (quic, tcp) | round-trip time of the tunnel |
node64_cdn64_online_edge_streams | Gauge | edge | open visitor streams |
node64_cdn64_online_edge_streams_total, …_reconnects_total | Counter | edge | streams and reconnects since the node started |
node64_cdn64_online_host_status | Gauge | name, mode, status | always 1, state at ipv64: active, pending, blocked |
node64_cdn64_online_host_traffic_month_bytes | Gauge | name | traffic of this name in the current month |
node64_cdn64_online_domains_used, …_domains_limit | Gauge | CDN domains of the account (the classic ipv64 CDN counts too) | |
node64_cdn64_online_traffic_used_bytes, …_traffic_limit_bytes | Gauge | CDN traffic of the account this month and its quota | |
node64_cdn64_online_last_sync_seconds | Gauge | last sync with ipv64 | |
node64_cdn64_online_sync_ok | Gauge | 1 = the last sync succeeded |
Certificates
The values of the central certificate management belong to the core and are always included. Labels per certificate: cert_id, name (first name) and source (ipv64, alias, http, dns, ca, self, upload).
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_certs | Gauge | number of certificates | |
node64_cert_expiry_seconds | Gauge | certificate | expiry time |
node64_cert_status | Gauge | certificate, status | always 1, state in the label: pending, valid, error, expired |
node64_cert_error | Gauge | certificate | 1 = last issuance or renewal failed |
node64_cert_auto_renew | Gauge | certificate | 1 = renewed automatically |
Honeypot64
Only with the module switched on; details under Honeypot64.
| Metric | Type | Labels | Meaning |
|---|---|---|---|
node64_honeypot_connections_total | Counter | service, port | connections to the trap services |
node64_honeypot_logins_total | Counter | service | login attempts |
node64_honeypot_events_total | Counter | kind, source | events per kind |
node64_honeypot_unique_sources | Gauge | distinct source IPs of the last 24 hours | |
node64_honeypot_listen_errors | Gauge | port | port could not be opened |
node64_honeypot_buffer_events, node64_honeypot_dropped_total | Gauge, Counter | events in the buffer, dropped due to overload | |
node64_honeypot_report_last_success_timestamp | Gauge | last successful report to ipv64 (Unix time, 0 = none yet) | |
node64_honeypot_report_errors_total, node64_honeypot_report_sources_total | Counter | failed reports, reported source IPs | |
node64_honeypot_log_lines_total, node64_honeypot_log_file_ok | Counter, Gauge | path | log lines read; file is being read (1) or error (0) |
node64_honeypot_syslog_messages_total, node64_honeypot_syslog_denied_total | Counter | accepted and rejected syslog messages | |
node64_firewall_active, node64_firewall_entries, node64_firewall_list_entries | Gauge | family or list, edition | firewall helper in the mode without a web interface: active, entries per address family and list |
node64_firewall_last_update_timestamp, node64_firewall_errors_total | Gauge, Counter | last update, errors | |
node64_honeypot_proxy_decoy_total, …_proxy_access_total, …_proxy_access_dropped_total, …_proxy_blocks_total | Counter | working with PROXY64: bait requests, analysed and dropped accesses, instant blocks | |
node64_honeypot_events_by_country_total | Counter | country | events per country of origin |
Example alerting rules
You can drop these rules straight into a Prometheus rule file and route them through Alertmanager. Adjust thresholds and wait times (for) to your setup.
groups:
- name: node64
rules:
- alert: Node64Down
expr: up{job="node64"} == 0
for: 5m
annotations:
summary: "Node64 {{ $labels.instance }} is not responding"
- alert: Node64ModuleError
expr: node64_module_error == 1
for: 10m
annotations:
summary: "Module {{ $labels.module }} reports an error"
- alert: Node64CheckDown
expr: node64_hc_check_up == 0 and on(token) node64_hc_check_maintenance == 0
for: 2m
annotations:
summary: "{{ $labels.check }} is down"
- alert: Node64CertExpiresSoon
expr: node64_hc_check_cert_expiry_seconds - time() < 14 * 86400
annotations:
summary: "Certificate of {{ $labels.check }} expires in less than 14 days"
- alert: Node64DynDNSError
expr: node64_dyndns_target_ok == 0
for: 15m
- alert: Node64DNSUpstreamDown
expr: node64_dns64_upstream_up == 0
for: 5m
- alert: Node64CertExpires
expr: node64_cert_expiry_seconds - time() < 14 * 86400 and on(cert_id) node64_cert_auto_renew == 1
annotations:
summary: "Certificate {{ $labels.name }} expires in less than 14 days (renewal failing?)"
- alert: Node64CDN64TargetDown
expr: node64_cdn64_host_target_up == 0 and on(host_id) node64_cdn64_host_enabled == 1
for: 5m
- alert: Node64CDN64TunnelDown
expr: node64_cdn64_online_edge_connected == 0
for: 3m
- alert: Node64IPv64Disconnected
expr: node64_link_linked == 1 and node64_link_connected == 0
for: 10mNode64 already notifies you about outages through its own notification channels. Prometheus alerts are most useful if you want everything in one place – or as a second, independent view of the node itself (such as Node64Down).
Grafana
Grafana doesn't need a separate interface: it reads the values from Prometheus.
- In Grafana, go to Connections → Data sources, add a data source of type Prometheus and enter the address of your Prometheus server.
- Create a new dashboard and add panels with the queries below.
- Optionally add a variable for several nodes, for example with
label_values(node64_info, node_name).
There's no ready-made dashboard template – but with these queries you'll have your own dashboard up in no time:
| Panel | PromQL |
|---|---|
| Checks down (stat) | sum(node64_hc_checks{state="down"}) |
| Response time per check in ms | node64_hc_check_latency_seconds * 1000 |
| Open incidents | node64_hc_incidents_open |
| Days until certificate expiry | (node64_cert_expiry_seconds - time()) / 86400 |
| DNS queries per second by status | sum by (status) (rate(node64_dns64_queries_total[5m])) |
| Share of blocked DNS queries (1 h) | sum(increase(node64_dns64_queries_total{status="blocked"}[1h])) / sum(increase(node64_dns64_queries_total[1h])) |
| Average DNS response time | rate(node64_dns64_query_duration_seconds_sum[5m]) / rate(node64_dns64_query_duration_seconds_count[5m]) |
| DNS cache hit rate | rate(node64_dns64_cache_hits_total[5m]) / (rate(node64_dns64_cache_hits_total[5m]) + rate(node64_dns64_cache_misses_total[5m])) |
| Reverse proxy: requests per second per host | sum by (host) (rate(node64_cdn64_host_requests_total[5m])) |
| Reverse proxy: error rate per host | sum by (host) (rate(node64_cdn64_host_errors_total[5m])) / sum by (host) (rate(node64_cdn64_host_requests_total[5m])) |
| Node uptime in days | node64_uptime_seconds / 86400 |
| Node memory | go_memstats_sys_bytes |
If your node is linked, you'll also find the results of online checks from Nuremberg and Falkenstein in the ipv64.net portal.
Something unclear, missing, or a question about Node64? Ask in the official forum v64.tech.