Pulse/docs/CONFIGURATION.md
rcourtman a9f1026656 Keep the metrics DB path override on the default org
PULSE_METRICS_DB_PATH sets Config.MetricsDBPath, and GetMonitor builds
each non-default tenant from a deep copy of the base config, which kept
that path. monitoring.New then opened the same metrics.db for every
organization, so per-org charts, reports and Assistant summaries read
from one shared store.

Clear MetricsDBPath on non-default tenant configs so each org opens
metrics.db under its own data directory. Org deletion already removes
that directory, so its metrics go with it. The override still moves the
default org's database, which is the single-install case it exists for.
2026-10-17 12:41:12 +01:00

74 KiB
Raw Permalink Blame History

⚙️ Configuration Guide

Pulse uses a split-configuration model to ensure security and flexibility.

File Purpose Security Level
.env Authentication & Secrets 🔒 Critical (Owner access only)
.encryption.key Encryption key for .enc files 🔒 Critical
audit/.audit-signing.key Encrypted signing key for the default audit store; preserve with its history and matching encryption key 🔒 Sensitive
system.json General Settings 📝 Standard
nodes.enc Node Credentials 🔒 Encrypted (AES-256-GCM)
alerts.json Alert Rules 📝 Standard
email.enc SMTP settings 🔒 Encrypted
webhooks.enc Webhook URLs + headers 🔒 Encrypted
apprise.enc Apprise notification config 🔒 Encrypted
oidc.enc OIDC provider config 🔒 Encrypted
sso.enc SAML/SSO provider config 🔒 Encrypted
api_tokens.json API token records (hashed) 🔒 Sensitive
ai.enc AI settings and credentials 🔒 Encrypted
ai_findings.json AI Patrol findings 📝 Standard
ai_patrol_runs.json AI Patrol run history 📝 Standard
ai_usage_history.json AI usage history 📝 Standard
ai_chat_sessions.json Legacy AI chat sessions (UI sync) 📝 Standard
license.enc Relay/Pro/legacy Pro+/Cloud license key 🔒 Encrypted
report_schedules.json Scheduled report definitions, recipients, and last-run metadata 🔒 Sensitive (encrypted when data-dir encryption is enabled)
host_metadata.json Host notes, tags, and AI command overrides 📝 Standard
docker_metadata.json Docker metadata cache 📝 Standard
guest_metadata.json Guest notes and metadata 📝 Standard
agent_profiles.json Agent configuration profiles (Pro/legacy Pro+/Cloud) 📝 Standard
agent_profile_assignments.json Agent profile assignments (Pro/legacy Pro+/Cloud) 📝 Standard
profile-versions.json Agent profile version history (Pro/legacy Pro+/Cloud) 📝 Standard
profile-deployments.json Agent profile deployment status (Pro/legacy Pro+/Cloud) 📝 Standard
profile-changelog.json Agent profile change log (Pro/legacy Pro+/Cloud) 📝 Standard
recovery_tokens.json Recovery tokens (short-lived) 🔒 Sensitive
sessions.json Persistent sessions (includes OIDC refresh tokens) 🔒 Sensitive
update-history.jsonl Update history log (in-app updates) 📝 Standard
metrics.db Persistent metrics history (SQLite) 📝 Standard
audit/audit.db Default audit database; capture on all plans, licensed query/export 🔒 Sensitive
baselines.json AI baseline data for anomaly detection 📝 Standard
ai_correlations.json AI correlation analysis cache 📝 Standard
ai_patterns.json AI pattern detection data 📝 Standard
ai_remediations.json AI remediation suggestions 📝 Standard
ai_incidents.json AI incident tracking 📝 Standard
org.json Organization metadata (multi-tenant) 📝 Standard

Guest metadata entries are keyed by the canonical guest ID format instance:node:vmid (for example, pve1:node1:100). Legacy dash-separated keys are migrated automatically.

Paths are relative to /etc/pulse/ (Systemd) or /data/ (Docker/Kubernetes) by default. The audit database and default signing key are in the audit/ subdirectory. Runtime-specific audit storage and non-default organisation paths can differ; see Audit storage and safe recovery. Do not reset signing or encryption keys as a troubleshooting step.

Path overrides:

  • PULSE_DATA_DIR sets the base directory for system.json, encrypted files, and the bootstrap token.
  • PULSE_METRICS_DB_PATH sets only the metrics SQLite database path. Use this for tmpfs-backed metrics history without moving secrets or config off the persistent data directory. It applies to the default org only; each non-default org keeps its own metrics.db in /orgs/<org-id>/.

Multi-tenant layout:

  • Default org uses the root data directory for backward compatibility.
  • Non-default orgs store data under /orgs/<org-id>/.
  • Migration may create /orgs/default/ and symlinks in the root data directory.

🔐 Authentication (.env)

For a new install, leave PULSE_AUTH_USER and PULSE_AUTH_PASS unset and complete the bootstrap-token setup in your browser. See First login. Do not deploy a shared example password.

The Pulse-generated .env controls local authentication and is not exposed to the UI. Preserve its owner-only permissions. A bcrypt hash is sensitive too: someone who obtains it can attempt offline password cracking.

# Pulse-generated file, not a command to paste into a shell
PULSE_AUTH_USER='admin'
PULSE_AUTH_PASS='<complete-bcrypt-hash>'

Deployment-supplied environment values take precedence over this file. Changing the generated file or using Pulse's password-change UI does not replace a password supplied by your deployment manager; update that managed source too.

Private Docker authentication file

If automation must skip browser setup, keep the credentials in a private file, not a docker run -e PULSE_AUTH_PASS=... command, shell history or a shared Compose file. On the Docker host, prepare the file below. These commands do not erase an existing file; edit it locally, without printing its contents:

(
  set -e
  umask 077
  auth_dir="$HOME/.config/pulse"
  auth_file="$auth_dir/docker-auth.env"
  if [ -L "$auth_dir" ] || [ -L "$auth_file" ]; then
    printf 'Refusing a symlinked credential path.\n' >&2
    exit 1
  fi
  mkdir -p "$auth_dir"
  chmod 700 "$auth_dir"
  touch "$auth_file"
  chmod 600 "$auth_file"
  vi "$auth_file"
)

In that editor, enter these two settings and replace the placeholder with a complete bcrypt hash generated by a trusted local tool with an interactive password prompt. Do not put the password in that tool's command arguments. Unlike Pulse's own quoted .env, Docker's --env-file input uses unquoted values; keep the hash's $ characters literal, with no $$ substitution:

PULSE_AUTH_USER=admin
PULSE_AUTH_PASS=<complete-bcrypt-hash>

For a new Community container, replace vX.Y.Z with the exact published tag you intend to install, then run:

(
  set -e
  if [ ! -s "$HOME/.config/pulse/docker-auth.env" ]; then
    printf 'Prepare the private authentication file first.\n' >&2
    exit 1
  fi
docker run -d \
  --name pulse \
  -p 7655:7655 \
  -v pulse_data:/data \
  --env-file "$HOME/.config/pulse/docker-auth.env" \
  -e PULSE_DEPLOYMENT_METHOD=docker_run \
  --restart unless-stopped \
  rcourtman/pulse:vX.Y.Z
)

This keeps credential values out of the command arguments, not out of Docker's container environment: administrators with Docker access can still read them. Pulse hashes a plaintext PULSE_AUTH_PASS for authentication at startup, but does not scrub its original value from Docker or your deployment file. Keep the file private, outside the repository, and do not share docker inspect or resolved Compose configuration. Do not source the file into your shell.

For an existing or paid Pro deployment, retain its image, mounts and managed configuration; this new-container example is not an upgrade procedure. Compose interpolation and Docker --env-file are different formats. Prefer bootstrap setup for Compose unless your deployment manager already has a private credential mechanism. See the Docker guide for setup and updates.

Advanced: OIDC / SSO

Configure Single Sign-On in Settings → Security → Single Sign-On, or use environment variables to lock the configuration.

See OIDC Documentation and Proxy Auth for details.

Environment overrides (lock the corresponding UI fields):

Variable Description
OIDC_ENABLED Enable OIDC (true/false)
OIDC_ISSUER_URL Issuer URL from your IdP
OIDC_CLIENT_ID Client ID
OIDC_CLIENT_SECRET Client secret
OIDC_REDIRECT_URL Override redirect URL (defaults to <public-url>/api/oidc/<provider-id>/callback)
OIDC_LOGOUT_URL Optional logout URL
OIDC_SCOPES Space or comma-separated scopes
OIDC_USERNAME_CLAIM Claim for username (default: preferred_username)
OIDC_EMAIL_CLAIM Claim for email (default: email)
OIDC_GROUPS_CLAIM Claim for groups
OIDC_ALLOWED_GROUPS Allowed groups (space or comma-separated)
OIDC_ALLOWED_DOMAINS Allowed email domains (space or comma-separated)
OIDC_ALLOWED_EMAILS Allowed emails (space or comma-separated)
OIDC_GROUP_ROLE_MAPPINGS Comma-separated group=role mappings (built-in roles on every plan; custom-role administration requires Pro RBAC)
OIDC_CA_BUNDLE Custom CA bundle path

Note

: API_TOKEN / API_TOKENS in .env are legacy and ignored at runtime in v6. Manage API tokens in the UI (api_tokens.json) for supported behavior.


🖥️ System Settings (system.json)

Controls runtime behavior like logging, polling intervals, and UI preferences. Legacy port fields in system.json are ignored; use FRONTEND_PORT instead.

Example system.json
{
  "pvePollingInterval": 10,       // Seconds
  "backendPort": 3000,            // Legacy (unused)
  "frontendPort": 7655,           // Legacy (ignored; use FRONTEND_PORT)
  "logLevel": "info",             // debug, info, warn, error
  "autoUpdateEnabled": false,     // Enable auto-update checks
  "adaptivePollingEnabled": false, // Smart polling for large clusters
  "allowedOrigins": "",           // CORS: empty or comma-separated exact origins
  "allowEmbedding": false,        // Allow iframe embedding
  "allowedEmbedOrigins": "",      // Comma-separated origins for iframe embedding
  "webhookAllowedPrivateCIDRs": "" // Allowlist for private webhook targets
}

Note

: logFormat is only configurable via the LOG_FORMAT environment variable, not in system.json.

Supported system.json Keys

Numeric intervals are seconds unless noted otherwise.

Key Description
pvePollingInterval PVE polling interval
pbsPollingInterval PBS polling interval
pmgPollingInterval PMG polling interval
backupPollingInterval Backup-record polling interval (0 = auto, not disabled). See Backup polling and guest safety.
backupPollingEnabled Enable scheduled backup-record collection, not a guest-agent pause. See Backup polling and guest safety.
adaptivePollingEnabled Enable adaptive polling
adaptivePollingBaseInterval Base interval for adaptive polling
adaptivePollingMinInterval Minimum adaptive polling interval
adaptivePollingMaxInterval Maximum adaptive polling interval
connectionTimeout API connection timeout
logLevel Server log level (debug, info, warn, error)
allowedOrigins CORS: empty grants no cross-origin browser permission; comma-separated exact origins allow credentialed browser requests. * allows any origin without credentials.
allowEmbedding Allow iframe embedding
allowedEmbedOrigins Comma-separated frame-ancestors allowlist
webhookAllowedPrivateCIDRs Allowlist for private webhook targets
updateChannel Update channel (stable or rc)
autoUpdateEnabled Allow unattended automatic updates from the systemd update timer (stable channel only)
publicURL Public URL used in links/notifications
hideLocalLogin Hide username/password login form
temperatureMonitoringEnabled Enable temperature monitoring (where supported)
dnsCacheTimeout DNS cache timeout
sshPort Default SSH port for temperature collection
discoveryEnabled Enable auto-discovery
discoverySubnet CIDR or auto
discoveryConfig Discovery tuning object (see below)
theme UI theme (light, dark, or empty for system)
fullWidthMode UI layout preference
metricsRetentionRawHours Raw metrics retention (hours)
metricsRetentionMinuteHours Minute metrics retention (hours)
metricsRetentionHourlyDays Hourly metrics retention (days)
metricsRetentionDailyDays Daily metrics retention (days)
disableDockerUpdateActions Hide Docker update actions in UI
backendPort Legacy (unused)
frontendPort Legacy (ignored; use FRONTEND_PORT)

discoveryConfig supports:

  • environmentOverride, subnetAllowlist, subnetBlocklist
  • maxHostsPerScan, maxConcurrent, enableReverseDns, scanGateways
  • dialTimeoutMs, httpTimeoutMs

Common Overrides (Environment Variables)

Environment variables take precedence over system.json.

Variable Description Default
FRONTEND_PORT Public listening port (web UI, API, and agent ingest) 7655
PORT Deprecated legacy alias for FRONTEND_PORT, honored only when FRONTEND_PORT is unset. Logs a deprecation warning at startup; switch to FRONTEND_PORT. (unset)
PULSE_AGENT_INGEST_PORT Optional dedicated port for the complete agent control plane: reports/config (/api/agents/*), command admission (/api/agent/ws), version checks, and bootstrap downloads. The web UI and management API stay isolated. 0 = disabled (single port). See Split-Port Agent Ingest. 0
HTTP_REDIRECT_PORT Optional plain-HTTP port that redirects to HTTPS on FRONTEND_PORT. Requires HTTPS_ENABLED=true with TLS_CERT_FILE and TLS_KEY_FILE. Must differ from FRONTEND_PORT. 0 = disabled. 0
LOG_LEVEL Log verbosity (see below) info
LOG_FORMAT Log output format (auto, json, console) auto
LOG_FILE Log file path (enables file logging) (unset)
LOG_MAX_SIZE Log rotation size (MB) 100
LOG_MAX_AGE Keep rotated logs for N days (positive integer; zero or negative values are ignored with a startup warning and the default applies) 30
LOG_COMPRESS Gzip rotated logs true
PULSE_AUDIT_ASYNC Queues audit-log writes in memory (up to 4096 events) and saves them in the background, so requests do not wait on audit storage. If the queue is full, the event is written synchronously instead. Only the value false (case-insensitive) turns the queue off and writes each event synchronously. Events still queued when the process is killed without a clean shutdown are not written. true

Log Levels

Level Description
error Only errors and critical issues
warn Errors + warnings; suppresses info and debug records
info Standard operational messages (startup, connections, alerts)
debug Verbose output including per-guest/storage polling details

The Log Level control in Settings → Support → System Logs changes the server-wide logging threshold, not a browser-only filter. The saved level is used at startup unless your deployment sets LOG_LEVEL: LOG_LEVEL takes precedence at startup. info is the default. Choosing warn can reduce volume, but warn also suppresses info operational records; absence of a log line is not evidence that an event did not occur.

Start with the current level and the bounded log readers. Debug can produce much more output, including raw guest-filesystem responses and private names or paths; do not enable it just to obtain a report. If the interface stalls after enabling Debug, return to the previous working level through your deployment's existing log-level control, without repeating the stall. A responsive interface afterwards is not proof that the underlying problem is fixed; retain the original time, affected view and redacted error.

Pause Stream and Clear Log Output only affect the browser display. They do not stop monitoring, reduce server logging or erase the server's logs. A Paused log view is not a backup safety pause.

Support Bundle downloads a complete configured log file, or the retained server log buffer when no log file can be opened, plus configuration and environment information. It is not just the visible lines: pausing or clearing the display does not remove those records from the download. Some secrets are masked, but that is not a guarantee that all content is safe to publish. Keep the archive private and share only relevant, manually reviewed, redacted excerpts, not the whole bundle.

Variable Description Default
PULSE_PUBLIC_URL URL for UI links, notifications, and OIDC. For reverse proxies, keep this as the public URL and use PULSE_AGENT_CONNECT_URL for agent installs if you need a direct/internal address. Auto-detected
PULSE_PRO_TRIAL_SIGNUP_URL Legacy hosted commercial base URL retained for hosted entitlement refresh compatibility. The path is ignored for refresh and normal self-hosted v6 UI must not surface trial signup. Must be absolute http(s) URL. https://cloud.pulserelay.pro
PULSE_AGENT_CONNECT_URL Dedicated direct URL for agents (overrides PULSE_PUBLIC_URL for agent install commands). Alias: PULSE_AGENT_URL. (unset)
PULSE_AGENT_CONFIG_SIGNING_KEY Base64 Ed25519 private key used to sign remote agent config payloads. (unset)
PULSE_AGENT_CONFIG_PUBLIC_KEYS Comma-separated base64 Ed25519 public keys (raw 32-byte or PKIX-encoded) trusted by agents. (unset)
PULSE_AGENT_CONFIG_SIGNATURE_REQUIRED Require signed remote config payloads (set on Pulse and agents). false
ALLOWED_ORIGINS Overrides the saved CORS allowlist with comma-separated exact origins. * permits any origin without credentialed browser access; not a login/proxy repair. (unset)
DISCOVERY_ENABLED Auto-discover nodes false
DISCOVERY_SUBNET CIDR or auto auto
DISCOVERY_ENVIRONMENT_OVERRIDE Force discovery environment (auto, native, docker-host, docker-bridge, lxc-privileged, lxc-unprivileged) auto
DISCOVERY_SUBNET_ALLOWLIST Comma-separated CIDRs allowed for discovery (empty)
DISCOVERY_SUBNET_BLOCKLIST Comma-separated CIDRs excluded from discovery 169.254.0.0/16
DISCOVERY_MAX_HOSTS_PER_SCAN Max hosts to scan per run 1024
DISCOVERY_MAX_CONCURRENT Max concurrent discovery probes 50
DISCOVERY_ENABLE_REVERSE_DNS Enable reverse DNS lookup (true/false) true
DISCOVERY_SCAN_GATEWAYS Include gateway IPs in discovery (true/false) true
DISCOVERY_DIAL_TIMEOUT_MS TCP dial timeout (ms) 1000
DISCOVERY_HTTP_TIMEOUT_MS HTTP probe timeout (ms) 2000
PULSE_AUTH_HIDE_LOCAL_LOGIN Hide username/password form false
DEMO_MODE Enable read-only demo mode false
PULSE_TRUSTED_PROXY_CIDRS Comma-separated immediate proxy IPs/CIDRs trusted for forwarded client IP, scheme, host and port. Use the peer seen by Pulse; wildcard ranges are rejected. See Reverse Proxy. (unset)
PULSE_TRUSTED_NETWORKS Comma-separated CIDRs treated as trusted local networks (does not bypass auth) (unset)
ALLOW_UNPROTECTED_EXPORT Set to true to allow unauthenticated config export from any non-loopback client (private LAN as well as public networks), only while no password, API token, proxy secret, SSO or hosted mode is configured. Export only: import is never enabled by this (use with caution) false

Split-Port Agent Ingest (Network Isolation)

By default Pulse serves the web UI, the REST API, and the agent control plane together on FRONTEND_PORT. For deployments that expose Pulse to monitored hosts across an untrusted network (for example, a managed service provider whose clients' Proxmox nodes reach a central Pulse server over the internet), you can expose the agent control plane on its own dedicated port and keep the web UI and management API on a separate, firewalled port.

Set PULSE_AGENT_INGEST_PORT to a port other than FRONTEND_PORT:

PULSE_AGENT_INGEST_PORT=7656

When enabled:

  • The dedicated port serves only the agent-owned routes required for a complete lifecycle: /api/agents/*, /api/agent/ws, /api/agent/version, /api/server/info, /install.sh, /install.ps1, and /download/pulse-agent. Every other path, including the web UI, login, and management APIs, returns 404. A host that can reach the agent port cannot pivot to the management interface.
  • The main FRONTEND_PORT listener is unchanged and still serves everything (including agent ingest), so existing single-port installs keep working. The dedicated listener is purely additive.
  • The value is validated at startup: it must be between 1 and 65535 and must differ from FRONTEND_PORT and the HTTP redirect port. An invalid value is rejected.

Expose only PULSE_AGENT_INGEST_PORT to your monitored hosts and keep FRONTEND_PORT on a private network or behind your firewall/VPN. Point agents at the dedicated port by setting PULSE_AGENT_CONNECT_URL to that port's public address, so generated agent install commands send check-ins there:

PULSE_AGENT_INGEST_PORT=7656
PULSE_AGENT_CONNECT_URL=https://agents.example.com:7656

Agents then post telemetry to https://agents.example.com:7656/api/agents/agent/report and establish their command channel at wss://agents.example.com:7656/api/agent/ws, while the web UI and management API remain reachable only on the private FRONTEND_PORT listener. If command execution is enabled, both routes must traverse the same proxy/firewall path; a successful report does not prove that the WebSocket is admitted.

Multiple Proxmox installations

Pulse is intended to monitor multiple Proxmox clusters and standalone nodes through separate saved Proxmox connections. Native node names and VMIDs can repeat across independent installations; a matching name or VMID alone does not identify the same resource.

A distinct Pulse connection name helps you recognise the saved connection. Neither that label nor a cluster member display name renames the native Proxmox node or VMID, or repairs incorrect attribution. If node errors, backup status or agent-backed Docker monitoring become mixed after adding a connection, use the cross-installation identity checks. Do not rename production nodes or repeat the addition as a diagnostic.

Proxmox Cluster Node Display Names

Nodes discovered through one Proxmox VE cluster connection can have an optional Pulse display name. Open Settings → Infrastructure, edit the Proxmox VE connection, and set Display name beside a cluster member.

  • Display names are presentation only. They do not change the Proxmox node name, API address, credentials, TLS fingerprint, routing, or action target.
  • An empty display name uses the current native Proxmox node name.
  • Names are trimmed, may contain Unicode, are limited to 128 characters, and cannot contain control characters. Duplicate display names are allowed because they are cosmetic; Pulse still uses a separate immutable identity.
  • The override is stored with that Proxmox connection and survives reloads, restarts, member address changes, native node renames, and temporary cluster membership loss. Confirmed removed members retain their identity metadata so a later reappearance can recover the override.
  • Pulse keeps the native Proxmox node name, prior native names, numeric node ID when available, and immutable Pulse identity for diagnostics and search. These fields are also available to API and mobile clients.

For legacy configuration first seen before Pulse records a Proxmox numeric node ID, changing both the native node name and every known member address at once is intentionally treated as a new member. Pulse does not guess across ambiguous evidence because that could transfer an override to the wrong node.

Iframe Embedding (system.json)

Embedding is controlled by system.json and the UI (Settings → System → Network):

  • allowEmbedding (boolean): enables iframe embedding
  • allowedEmbedOrigins (comma-separated): restricts frame-ancestors when embedding is enabled

When allowEmbedding is false, Pulse sends X-Frame-Options: DENY and frame-ancestors 'none'.

Monitoring Overrides

Variable Description Default
PVE_POLLING_INTERVAL PVE metrics polling frequency 10s
PBS_POLLING_INTERVAL PBS metrics polling frequency 60s
PMG_POLLING_INTERVAL PMG metrics polling frequency 60s
CONNECTION_TIMEOUT API connection timeout 60s
BACKUP_POLLING_CYCLES Poll cycles between backup checks 10
ENABLE_BACKUP_POLLING Enable scheduled backup-record collection, not a guest-agent pause. See Backup polling and guest safety. true
BACKUP_POLLING_INTERVAL Backup-record polling interval (0 = auto, not disabled). See Backup polling and guest safety. 0 (Auto)
ENABLE_TEMPERATURE_MONITORING Enable temperature monitoring (where supported) true
SSH_PORT SSH port for temperature collection over SSH 22
ADAPTIVE_POLLING_ENABLED Enable smart polling for large clusters false
ADAPTIVE_POLLING_BASE_INTERVAL Base interval for adaptive polling 10s
ADAPTIVE_POLLING_MIN_INTERVAL Minimum adaptive polling interval 5s
ADAPTIVE_POLLING_MAX_INTERVAL Maximum adaptive polling interval 5m
GUEST_METADATA_MIN_REFRESH_INTERVAL Minimum refresh for guest metadata 2m
GUEST_METADATA_REFRESH_JITTER Jitter for guest metadata refresh 45s
GUEST_METADATA_RETRY_BACKOFF Retry backoff for guest metadata 30s
GUEST_METADATA_MAX_CONCURRENT Max concurrent guest metadata fetches 4
DNS_CACHE_TIMEOUT Cache TTL for DNS lookups 5m
MAX_POLL_TIMEOUT Maximum time per polling cycle 3m
PULSE_DISABLE_DOCKER_UPDATE_ACTIONS Hide Docker update buttons (read-only mode) false
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_DETECTION Allow Proxmox-side LXC Docker socket hinting with pct exec false
PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY Allow Proxmox-side minimal LXC Docker inventory collection with pct exec; collects Docker host/container summary, not inspect/env/mount/process data. Admins can also toggle this in Settings → System → General; setting the env var locks the toggle false
PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS Optional comma-separated VMID allowlist for Proxmox-side LXC Docker discovery; when set, only these guests are socket-probed and inventoried. Empty means all running LXCs are eligible when detection or inventory is enabled (unset)
PULSE_TELEMETRY Outbound usage telemetry (details); set false to disable true
PULSE_DEPLOYMENT_METHOD Optional closed telemetry label: docker_compose, docker_run, container_other, systemd, binary_other, or other; invalid values are reported only as the safe runtime fallback Inferred as container_other or binary_other

Backup polling and guest safety

The Recovery panel's Enable backup polling switch (backupPollingEnabled, or ENABLE_BACKUP_POLLING) controls whether Pulse schedules collection of Proxmox/PBS backup records and guest snapshots. Turning it off does not pause ordinary PVE monitoring or its QEMU Guest Agent disk, memory and metadata reads. It does not stop Proxmox or PBS from running backup jobs, and changing the setting does not cancel work already in flight. Do not use this switch as a freeze-enabled backup safety precaution.

backupPollingInterval and BACKUP_POLLING_INTERVAL control that collection's cadence. 0 means automatic cadence, not disabled. Longer intervals delay backup evidence refresh; disabled polling leaves it unrefreshed. A retained backup row or posture is not proof of a current observation, a successful restore or guest thaw. Check the matching workload, datastore, namespace and artifact time in the provider's own tools before relying on it; do not clear history or run another backup to repair a Pulse display. Environment overrides take precedence over system.json and lock the corresponding UI controls. Schedule configuration changes outside backup or freeze/thaw windows.

For an affected installation, follow the deployment-specific backup safety precaution before a planned freeze-enabled backup: pause the actual Pulse server and prevent its updater or deployment controller from restarting it. This is not incident recovery, and stopping Pulse does not cancel a guest-agent request already issued. Keep Pulse stopped until the backup has ended and independent post-backup checks confirm thaw, fresh successful workload writes to every filesystem covered by the backup, and workload liveness. Use established checks independent of Pulse and the QEMU Guest Agent, not new probes or forced writes. If any check fails or is unavailable, leave Pulse and its updater paused and use the guest/platform's recovery procedure. After all checks pass, restore only services and timers that were active before the pause; do not guess unknown pre-pause states. Pulse monitoring and alerts are unavailable while stopped: arrange independent outage coverage. This precaution does not establish a fixed monitoring defect.

Logging Overrides

Variable Description Default
LOG_FILE Log file path (empty = stderr only) (unset)
LOG_MAX_SIZE Log file max size (MB) 100
LOG_MAX_AGE Log file retention (positive days; zero or negative values are ignored with a startup warning and the default applies) 30
LOG_COMPRESS Compress rotated logs true

Update Settings (system.json)

These are stored in system.json and managed via the UI.

Key Description Default
updateChannel Update channel (stable or rc) stable
autoUpdateEnabled Allow unattended automatic updates from the systemd update timer (stable channel only) false

Note

: Update settings are stored in system.json. Legacy .env entries (UPDATE_CHANNEL, AUTO_UPDATE_ENABLED) are kept in sync for backwards compatibility but are not read at runtime. The former autoUpdateCheckInterval / autoUpdateTime fields were never consumed and are ignored if present; the update schedule lives in the systemd timer.

stable is the default and recommended production channel. rc is an opt-in preview channel. In v6, unattended systemd auto-updates remain stable-only even when updateChannel is set to rc.

Auto-Import (Bootstrap)

Startup import is for deliberately provisioning an isolated destination, not resetting an existing installation. It uses the same encrypted configuration-transfer scope as the Recovery settings form, not a full-state backup. Included settings and API-token records are replaced, not merged; imported SSO settings can also change access.

The startup gate requires an empty data directory (PULSE_DATA_DIR, or the deployment default), or a destination that does not yet exist. A regular .env file for destination-local deployment overrides is the only exception. Existing settings, API-token records, sessions, encryption keys, organisation directories and unknown contents all prevent startup import, even without nodes.enc. Unreadable directories and symbolic links are not treated as empty. The importer checks the destination again before writing, but this is not a lock against another running Pulse process. Do not delete nodes.enc or other state, change the data path or remove credentials to force an import; resolve an unexpected path or access failure first. For an existing installation, use the reviewed transfer procedure instead, with a private backup of the destination before replacing settings.

Variable Description
PULSE_INIT_CONFIG_DATA Encrypted export contents; used only when PULSE_INIT_CONFIG_FILE is unset
PULSE_INIT_CONFIG_FILE Path to the private encrypted export, readable by the Pulse service; takes precedence over PULSE_INIT_CONFIG_DATA
PULSE_INIT_CONFIG_PASSPHRASE Passphrase for the export bundle (required)

An unreadable or invalid file fails the attempt; it does not fall back to the data variable. PULSE_INIT_CONFIG_URL is not supported by server startup; the separate hidden pulse config auto-import command has a different path. Keep the input archive outside the destination data directory. An archive placed inside it also makes the destination nonempty; startup skips import and retains those contents. Use a separate private file or read-only mount, not deletion of useful destination state.

Keep the export owner-only and its passphrase in the deployment's private credential source, separate from the backup. Do not put a passphrase or bundle in command arguments, URLs, shared Compose files, logs or issue reports; base64 is not redaction. Startup has no passphrase-file variable. For an interactive transfer, use the authenticated Recovery form instead.

Before enabling startup import, preserve the intact source and any useful destination state. Isolate the trial destination from agents and notification destinations: imported settings can activate monitoring and delivery. Keep only one active installation at cutover, with independent outage coverage.

A running server is not proof that import succeeded. A returned import error is logged and startup continues with normal configuration loading, which can itself fail. Review the existing error privately, stop the trial destination on an unexpected result, and reconcile its state before another attempt. Do not repeat import, replay notifications or induce a workload action to test recovery. After verifying the intended settings and access, remove only the three PULSE_INIT_CONFIG_* settings above from the deployment's managed source so a later start cannot reuse them. Apply environment changes through the existing deployment during suitable maintenance, preserving its image, data and other credentials; a Docker restart alone does not apply a changed environment.

Configuration transfer includes API-token records, but not server-side agent inventory or enrolment state, local login credentials or monitoring history. It cannot change the primary URL saved on remote agents. If the address changes, follow agent retargeting, preserving each agent's identity, credential and TLS trust. Verify fresh admission and ordinary reports before retiring the source; imported records alone do not establish agent recovery.

Developer/Test Overrides (Environment Variables)

These are primarily for development or test harnesses and should not be used in production.

Variable Description Default
PULSE_UPDATE_SERVER Override update server base URL (testing only) (unset)
PULSE_UPDATE_STAGE_DELAY_MS Adds artificial delays between update stages (testing only) (unset)
PULSE_ALLOW_DOCKER_UPDATES Expose update UI/actions in Docker (debug only) false
PULSE_DEV_ALLOW_CONTAINER_SSH Allow SSH-based temperature collection from containers (dev/test only) false
PULSE_AI_ALLOW_LOOPBACK Allow AI tool HTTP fetches to loopback addresses false
PULSE_LICENSE_PUBLIC_KEY Override embedded license public key (base64, dev only) (unset)
PULSE_LICENSE_DEV_MODE Skip license verification (development only) false

Metrics Retention (Tiered)

Persistent metrics history uses tiered retention windows. These values are stored in system.json and can be adjusted for storage vs history depth:

  • metricsRetentionRawHours
  • metricsRetentionMinuteHours
  • metricsRetentionHourlyDays
  • metricsRetentionDailyDays

See METRICS_HISTORY.md for details.

Prometheus Metrics Endpoint

The /metrics listener is separate from the main UI/API listener and binds to loopback by default.

Variable Description Default
PULSE_METRICS_PORT Metrics listener port 9091
PULSE_METRICS_BIND_ADDRESS Metrics listener bind address 127.0.0.1
PULSE_METRICS_TOKEN Optional bearer token for /metrics (empty)
PULSE_METRICS_ALLOW_INSECURE_REMOTE Explicit opt-in to serve a metrics bearer token over non-loopback plaintext HTTP false

For remote scraping with PULSE_METRICS_TOKEN, prefer a local scraper, tunnel, VPN-private path, or TLS/mTLS reverse proxy. Pulse refuses non-loopback plaintext token scraping unless PULSE_METRICS_ALLOW_INSECURE_REMOTE=true is set.


🔔 Alerts (alerts.json)

Manage alert rules in Alerts → Thresholds. Choose the platform and resource before editing; a group default can affect many resources, while a custom resource override takes precedence. Finish the edit and use Save Changes; an unsaved value is not the running policy. Reload after a successful save to check that the intended value persisted.

For TrueNAS physical disks, read the disk temperature and health guide before changing a rule: heat, SMART faults and missing readings are different signals. Storage temperature colouring and heat reasons follow the saved per-disk override, then the TrueNAS-wide default, then the inherited by-type policy. Off leaves the temperature visible but unjudged; last known readings are not current heat. Silencing an alert is not disk recovery.

Metric thresholds, Off and inheritance

For numeric metric rules such as CPU, memory and disk usage:

Setting Meaning
Positive metric threshold Enables that metric rule at the entered value, subject to other alert policies. Read the column's unit: percentages, temperatures and throughput are not interchangeable.
Metric Off Disables that metric rule, not collection of its readings. Use the On/Off control rather than an empty input to disable it.
Blank per-resource metric value Inherits the group default; it does not mean Off. An inherited Off default remains Off.
Saved numeric metric trigger 0 or a negative value Disables that metric rule. Zero does not mean “alert on any usage”. Older saved rules may use 0; the current metric Off control writes -1.

Metric Off is not a metric-hiding control. The reading remains visible; Off does not mark it ignored or N/A, nor prove that the workload is healthy. For an appliance VM whose hypervisor memory reading does not represent useful guest memory pressure, turn off only that VM's Memory metric rule if those alerts are unwanted. Keep its CPU, disk, backup and uptime monitoring in place; do not disable the whole resource or global alerts to quiet one metric.

A near-100% hypervisor memory reading alone does not establish guest memory pressure or an appliance fault. Check the appliance's own ordinary status where available. Do not install an agent, restart the VM or change its configuration solely to remove the reading. Pulse has no per-VM control here to replace one metric with an explicitly ignored reading. For a safe comparison, read guest memory, cache and available memory. Memory metric Off does not dismiss a separate Patrol finding.

Zero has different meanings in different fields. A zero metric trigger disables the rule, but zero powered-off tolerance below means immediate eligibility on an authoritative stopped observation. Do not copy one field's meaning into another. Platform/resource alert switches, offline alerts and notification delivery have their own controls.

Usage rules have separate trigger and clear values (hysteresis), so a small drop below the trigger does not repeatedly close and reopen the alert. In the manual CPU example below, trigger: 90 and clear: 80 mean:

  • A fresh evaluated value of 90% or higher is eligible to activate the alert.
  • An already active alert stays active above 80%, even at 85%.
  • A fresh evaluated value of 80% or lower is eligible to clear it.

Configured evaluation windows and activation/recovery delays still apply; these are not promises of immediate notifications. The excerpt illustrates a saved rule, not the clear value produced by every UI edit.

Disabling a metric can close its existing alert even while usage remains high. That disappearance is a policy change, not measured recovery. Missing or stale readings are not a healthy zero either. Check fresh readings and the workload before judging recovery; do not lower thresholds, create load or stop a workload just to test the rule. To quiet notifications without disabling a metric rule, review Quiet hours and their critical-alert exceptions. Check Recent delivery activity separately: a saved threshold or a successful destination Test does not prove ordinary alert delivery.

VM and container powered-off tolerance

Open Alerts → Thresholds → Alert intent & grace to configure how long a Proxmox VM or LXC container may remain stopped before Pulse raises its powered-off alert.

  • Leave the VM/container default blank to inherit the existing policy. An installation with no applicable policy keeps the legacy two-poll behavior.
  • Set it to 0 to alert on the first authoritative stopped observation.
  • Set a positive number of seconds (for example, 300) to tolerate a short stop regardless of polling cadence. Per-resource values override the VM/container default; a blank per-resource value inherits it.
  • Extend offline grace during Proxmox backups applies only while Pulse has fresh matching evidence of an active backup. The maximum deferral is a hard cap, so a stale or stuck backup cannot hide a sustained outage.

This tolerance delays activation; it does not disable powered-off monitoring. Use the existing guest offline-alert toggle when a guest should never produce powered-off alerts.

Destination severity and tag routing

Open Alerts → Notifications and edit the email or webhook destination. For ordinary firing alerts, an enabled destination must match both its Minimum alert severity and Resource tag routing. Matching one does not bypass the other, or the separate notification schedules and holds.

Minimum alert severity Eligible firing levels
All alerts Informational, warning and critical alerts; other policies still apply.
Warnings and critical alerts Warning and critical alerts, not informational alerts.
Critical alerts only Critical alerts, not warning or informational alerts.

These are minimums, not exclusive channels. A critical alert can match both a Warnings and critical alerts destination and a Critical alerts only destination. There is no warning-only severity setting; tag filters select resources, not a severity exclusion. Each destination is evaluated independently, so one alert can go to more than one destination. Grouped alerts are filtered member by member: a critical member does not make the group's warning members eligible for a critical-only destination.

An empty tag filter removes the resource-tag restriction only. It does not override minimum severity, paused delivery, a disabled destination or other notification policies. With tags selected:

  • Match all tags requires every selected tag on the same alert's routing metadata. Match any tag requires at least one. An alert without usable routing tags does not match a nonempty filter.
  • Matching is exact, ignoring case and surrounding whitespace; it does not interpret wildcards, prefixes or regular expressions. A tag named critical is a resource tag, not an alert-severity rule.
  • Proxmox resource tags use their tag text. Docker container and service labels become key:value tags, or key when the value is empty: the label env=prod is matched by env:prod, not just prod.

For example, select env:prod and team:ops, Match all tags, and Warnings and critical alerts. A warning or critical alert carrying both tags is eligible for that destination. An informational alert with both tags is not. A critical alert carrying only env:prod also fails this filter; critical severity does not bypass tag routing. Choosing Match any tag would allow either tag, but would still exclude informational alerts.

A successful Test does not exercise ordinary alert routing. Check an already occurring alert and Recent delivery activity instead of creating an outage, removing filters or changing workload labels just to test delivery. Missing delivery is not evidence that the workload is healthy. Recovery uses the successful firing receipt for the same occurrence and destination, rather than reapplying changed tags or minimum severity; the separate recovery controls and holds still apply. Retained queued work also keeps its saved destination settings.

Alert reminders and recovery notifications

Open Alerts → Schedule. These controls govern notification eligibility, not when a threshold first activates or when fresh readings clear an incident. Save changes and reload to confirm the running settings.

Control Effect
Alert cooldown on Cooldown period is the minimum interval, in minutes, before an existing alert can send an ordinary reminder after its last notification. It does not delay the first eligible notification.
Alert cooldown off Stops ordinary reminders for the same alert occurrence after its first notification. Off does not mean “send on every poll” or “no rate limit”, and it does not turn off monitoring or initial delivery.
Max alerts / hour Limits ordinary notifications per workload/metric combination over a rolling hour, not across the whole installation. Saving cooldown as off retains this separate limit; it does not remove it.
Recovery notifications on Enables eligible recovery messages for destinations with a recorded successful firing delivery for that same alert occurrence. It does not send an all-clear to every configured destination.
Recovery notifications off Stops recovery messages, not detection of recovery or closure of the incident in Pulse.

For example, with a 30-minute cooldown, an alert last notified at 10:00 is not eligible for an ordinary reminder before 10:30. That is a minimum interval, not a promise of a message at 10:30: the alert must still be active and its other policies must permit delivery. Severity changes and configured escalation are handled separately; cooldown off is not a way to silence them. The hourly limit is not a count of provider requests: delivery retries and multiple recipients are separate from alert eligibility.

Recovery follows the configured grouping window too, so it need not send immediately when the incident clears. A destination that never successfully received that occurrence's firing notification is not eligible for its recovery. A successful delivery record means provider acceptance, not that a person read the message. Acknowledgement, snooze, quiet hours, destination settings and provider failures can also prevent recovery delivery; enabling the toggle does not bypass those controls.

A missing reminder or recovery message is not a workload health check. Check fresh readings and the incident's state in Alerts → Overview or History, then correlate Recent delivery activity. Keep these policies distinct from acknowledgement and snooze, quiet hours and retrying retained failures. Do not lower thresholds, create load or stop a workload to test reminders or recovery messages; inspect the next normally occurring incident instead.

Acknowledge and snooze existing alerts

In Alerts → Overview, these actions apply to an existing incident, not its threshold rule. Monitoring continues; neither action repairs the workload or confirms recovery.

Action Effect
Acknowledge Marks the incident as seen. While acknowledged, further firing notifications, escalation and its recovery notification are suppressed. The underlying alert can remain active.
Unacknowledge Removes that acknowledgement. Normal notification policies apply again; this is not a guaranteed immediate resend.
Snooze Pauses notifications and escalation for this incident until the selected time, including critical notifications. Monitoring can still detect recovery during the snooze.
Resume Ends this incident's snooze early. It does not remove an acknowledgement or clear the underlying alert.

Acknowledged alerts are hidden from the default active list and excluded from its Active count. Use Show acknowledged to inspect them and Unacknowledge only when they need attention again. No unacknowledged alerts does not mean every workload recovered. Before Acknowledge all, read the count and scope: the toolbar applies to all unacknowledged active alerts; a group's button applies to that group, including its collapsed related alerts. Use the individual action when only one incident has been reviewed.

Choose a Snooze duration on the incident's card and check its Snoozed until time. Until tomorrow at 9:00 uses the browser's local timezone, not the quiet-hours timezone. On expiry or Resume, an acknowledged incident remains acknowledged; remove that hold separately if intended. Other schedules, routing, cooldowns and destination settings still apply, so ending a snooze is not proof of delivery or a promise to replay missed notifications.

These incident actions are not Dismiss retained failures or Retry retained deliveries. They do not retry a failed notification. Check fresh readings and the workload for recovery, and Recent delivery activity for delivery. Do not create an outage or lower thresholds to test an action.

Quiet hours and notification holds

Open Alerts → Schedule → Quiet hours to enable a notification quiet period. Choose the start and end times, an IANA timezone such as Europe/London, and Quiet days. No selected days means no quiet period, even when enabled. Times are interpreted in the selected timezone, not your browser's timezone.

Quiet hours hold non-critical notifications; they do not stop monitoring, clear the alert or confirm recovery. Critical notifications remain eligible unless you explicitly select their Suppress categories (Performance, Storage or Offline). Selecting a category also holds its critical notifications, including urgent failures; leave it unchecked when those must still reach you. Other routing, mute and delivery policies still apply.

A window can cross midnight. Days refer to the current local calendar day: with only Monday selected, 22:00–06:00 covers Monday's early morning and late evening, not Tuesday's early morning. Select Tuesday too if that part of the night must be quiet. The configured end minute is included: an end of 06:00 remains quiet through 06:00:59.

Eligible queued notifications are held for re-evaluation when the quiet period ends. This is not a promise to send every held item at that instant: current alert state, destination settings, other policies and provider failures still matter. Use Recent delivery activity to distinguish a quiet-hours hold from a failed delivery; a settings Test skips the queue and does not validate this schedule. Do not create an outage to test it.

The example below makes every day quiet from 22:00 through the 06:00 end minute in Europe/London, while leaving critical categories unsuppressed. Quiet hours are off by default. This is an illustrative excerpt, not a complete alerts.json: prefer the UI, and preserve existing rules and settings rather than replacing the file with this example.

Manual Configuration (JSON excerpt)
{
  "guestDefaults": {
    "cpu": { "trigger": 90, "clear": 80 },
    "memory": { "trigger": 85, "clear": 72.5 }
  },
  "schedule": {
    "quietHours": {
      "enabled": true,
      "start": "22:00",
      "end": "06:00",
      "timezone": "Europe/London",
      "days": {
        "monday": true,
        "tuesday": true,
        "wednesday": true,
        "thursday": true,
        "friday": true,
        "saturday": true,
        "sunday": true
      },
      "suppress": {
        "performance": false,
        "storage": false,
        "offline": false
      }
    }
  }
}

Availability Checks (availability_targets.enc)

Availability checks are agentless probes for devices and services where Pulse cannot install an agent or does not need full machine telemetry. Use them for simple ping monitoring, TCP service checks, and HTTP/HTTPS status checks.

Managed via UI: Settings -> Monitoring -> Availability checks

Supported protocols:

Protocol Use case Required fields
icmp Ping-only reachability for devices, computers, and appliances address
ping API input alias for icmp; saved targets return icmp address
tcp A reachable port such as MQTT, SSH, or a custom service address, port
udp A request/response UDP service, or an open-or-filtered observation address, port, UDP result policy
http / https Web UI or health endpoint availability address, optional port, optional path

Saved targets include name, targetKind (machine, service, or device), address, protocol, enabled, polling interval, timeout, failure threshold, and an optional linkedResourceId. UDP targets select response_required (send up to 512 UTF-8 bytes and require a response) or open_or_filtered (silence is indeterminate, not proof of reachability); an optional expected response must match exactly. HTTPS targets monitor certificate validity by default and warn 30 days before expiry unless the target overrides or disables that check. ICMP ping is the default probe for new targets. Availability targets publish network-endpoint resources and can raise downtime alerts after the configured failure threshold and certificate alerts inside the configured expiry window.

Example API payload for simple ping monitoring:

{
  "name": "Garage temperature sensor",
  "targetKind": "device",
  "address": "garage-sensor.local",
  "protocol": "ping",
  "enabled": true
}

Pulse stores and returns that target as protocol: "icmp" so dashboards, alerts, and resource projections keep one canonical protocol value.

External probes (Pro)

External probes change where a check runs, not where alerts are sent. By default, checks run on the Pulse server. With the Pro external_probe entitlement, select one or more connected agents under Observation locations in the check editor. An agent on a cloud VM or at another site can observe a service from outside its local network. Keep This Pulse server selected if you also want a local observation; deselect it for agent-only checks. Pulse keeps each location's evidence separate, so a failure on one path is not a universal outage. The API uses observationLocationIds; see the target fields.

The assigned agent receives signed configuration, runs the check and reports results back to Pulse. Only agents assigned to that target may submit results; the server runs a local check only when This Pulse server is selected. The agent does not send notifications directly. The Pulse server must be running and able to reach the notification destination to evaluate results and send alerts.

Situation What the check can tell you
Pulse is running and receives probe results Target observations are evaluated through the normal alert and notification policies.
Pulse is running but probe results stop arriving The affected location becomes indeterminate: "no recent report from probe agent". This is missing evidence, not proof the target is down. Other locations retain their own evidence.
Pulse is stopped or cannot reach its notification destination An external agent cannot deliver Pulse alerts in its place. Use an independent external watchdog for this failure.

The missing-report window is the longer of five minutes or three check intervals, measured from server receipt time, not the agent's clock. During normal server evaluation, eligible stale probes raise one warning per agent, not per assigned check. Alert and connectivity policies still apply. If the agent heartbeat is also offline, the host-offline alert owns the incident instead of a duplicate probe warning. See the agent probe guide for buffering limits.

For an outage of Pulse itself, configure an external watchdog outside the power and network failure you need to detect, with an independently reachable notification destination. Verify actual receipt in an authorised test environment; a successful destination test does not prove outage coverage.

Existing paired Pulse Mobile/Relay users retain their current push path until 31 March 2027. Relay is no longer sold. Its instance-disconnect push does not evaluate individual targets while Pulse is offline, and is not a permanent substitute for the watchdog. See Mobile retirement.

If the entitlement lapses, the check resumes running from the Pulse server: this changes its observation location and may change its result. Checks without an agent assignment remain available in every edition.

ICMP probe privileges

ICMP checks execute the system ping binary on the selected observation host. A local permission error (Operation not permitted, cap_net_raw or icmp probe blocked) is not evidence that the target is down, nor proof that one particular service setting caused it. First distinguish This Pulse server from an external agent observation. Changing the server's unit cannot repair an agent's permissions on another host.

For Linux systemd servers, the current installer grants both AmbientCapabilities=CAP_NET_RAW and CapabilityBoundingSet=CAP_NET_RAW, while keeping NoNewPrivileges=true. This allows the child ping to inherit the capability without gaining it through setuid or file capabilities. Older units can lack the ambient grant. Updates preserve an existing service unit; rerunning an installer is not a reliable unit repair, even if an older error message recommends it. Do not reinstall over a working data directory.

Inspect the actual systemd server first

On the machine running the Pulse server, read only these effective properties. For a Pulse LXC, run this inside the Pulse LXC, not on the Proxmox host or a monitored guest. The example uses pulse.service; substitute your actual server unit throughout, such as pulse-backend.service on some older installs, not pulse-agent.service:

timeout --signal=TERM --kill-after=1s 8s systemctl show pulse.service \
  --property=LoadState,ActiveState,User,NoNewPrivileges,AmbientCapabilities,CapabilityBoundingSet

Interpret the result only after a successful read with LoadState=loaded. A missing unit, failed or timed-out read is unknown, not an empty capability set or a stopped server. This command does not print environment values, credentials or full unit contents; keep those private.

Existing observation What follows
NoNewPrivileges=yes, ceiling includes cap_net_raw, ambient grant missing An older non-root server unit may need the narrow override below.
Both capability properties already include cap_net_raw Do not add the grant again. Check the selected observation host, installed ping and its OS/container restrictions through normal administration.
Ceiling excludes cap_net_raw, or the deployment deliberately forbids it An ambient line alone cannot overcome the ceiling. Preserve that restriction; do not replace the capability set with a permissive one.
User= is empty/root, or the service is custom This is not the standard non-root server recipe. Review its existing privilege and data-access requirements using manual-service guidance. Do not change the user or grant broader privileges just to clear the error.

Narrow override for an older non-root server

Use this only for an active non-root server whose capability ceiling already permits cap_net_raw, whose ambient grant is missing, and where granting it is consistent with the deployment's intended policy. Keep deliberately inactive services inactive. Record the existing drop-in locally before editing; it may contain private configuration. Preserve every unrelated setting:

sudo systemctl edit pulse.service

Add just this service setting to the drop-in:

[Service]
AmbientCapabilities=CAP_NET_RAW

Keep NoNewPrivileges enabled; do not run Pulse as root, set file capabilities on the system-wide ping binary or remove sandbox settings. Do not reset or widen CapabilityBoundingSet to make this example apply to a different profile.

sudo systemctl daemon-reload

If editing or reloading fails, stop. Repeat the read-only property check above to confirm the intended ambient grant and preserved ceiling/hardening. Reload changes systemd's configuration, not the running process's capabilities.

Schedule the restart as maintenance, outside backups or other guest operations. Pulse monitoring and alerts are unavailable during the restart; arrange independent outage coverage. Only after the checks pass, for the server that was active beforehand:

sudo systemctl restart pulse.service && \
  timeout --signal=TERM --kill-after=1s 8s systemctl is-active pulse.service

active proves service startup, not ICMP recovery or notification delivery. Check the next normally scheduled observation from the same location; do not induce a workload failure or another notification to prove recovery. A failed/unknown restart needs normal recovery, not repeated restarts. To undo this change, edit only the added line, preserve other drop-in content and reload; do not use systemctl revert, which can remove unrelated overrides. Apply any further restart only under the same maintenance and prior-active conditions. Preserve the data directory and encryption key throughout.

Containers and external agents

Docker's default capability set includes NET_RAW, but that does not prove ICMP works in every rootless, non-root or restricted container. For an intentionally restricted deployment, review only the selected observation host's ping/socket permissions and saved deployment. Do not use --privileged, --cap-add=ALL, disable user namespaces or recreate a container as a diagnostic. A deliberate capability change must use the deployment's normal maintenance route while preserving data and configuration; the systemd override above does not apply.

External agents have their own OS/service requirements; see Agent Security. Do not grant command execution or widen agent privileges merely because the server's ICMP check failed. TCP and HTTP/HTTPS checks need no raw-socket capability, but measure different behaviour; switching protocol or observation location is not proof that ICMP recovered.


🔒 HTTPS / TLS

Enable HTTPS by providing certificate files via environment variables.

# Systemd
HTTPS_ENABLED=true
TLS_CERT_FILE=/etc/pulse/cert.pem
TLS_KEY_FILE=/etc/pulse/key.pem

# Docker
docker run --init -e HTTPS_ENABLED=true \
  -v /path/to/certs:/certs \
  -e TLS_CERT_FILE=/certs/cert.pem \
  -e TLS_KEY_FILE=/certs/key.pem ...

To redirect plain-HTTP requests to HTTPS, set HTTP_REDIRECT_PORT to a port other than FRONTEND_PORT (for example 80). Pulse listens on that port on the same bind address as the web UI and answers requests with a 301 redirect to the same host on the HTTPS FRONTEND_PORT. The redirect listener starts only when HTTPS_ENABLED=true and both TLS_CERT_FILE and TLS_KEY_FILE are set; otherwise the value has no effect. A non-numeric or out-of-range value is ignored silently, which leaves the redirect disabled.

Important (Docker with HTTPS): Always use --init (or init: true in docker-compose) when enabling HTTPS. The Alpine-based healthcheck uses busybox wget, which spawns ssl_client subprocesses. Without an init process to reap them, these become zombie processes over time.


🛡️ Security Best Practices

  1. Permissions: Ensure .env and nodes.enc are 600 (read/write by owner only).
  2. Backup hygiene: Back up .env separately from system.json.
  3. Tokens: Use scoped API tokens for agents instead of the admin password.

🔑 API Tokens

API tokens provide scoped, revocable access to Pulse. Manage them in Settings → API Access, in the Security group. An account without token management permission can review the inventory but cannot create or revoke tokens.

The token shown during first-run setup is the primary automation API token for that Pulse instance, not your web login password. Do not reuse a full-access setup token for every consumer. Give each agent, script, integration or display only the scopes it needs, with a name that identifies its use. Tokens are shown once; later rows show hints and metadata, not a recoverable secret. Store the secret privately before dismissing it.

Replace or revoke a token

Revoking a token stops consumers still using it from authenticating. For a planned rotation, create a least-privilege replacement, install it privately in each affected consumer and verify a fresh authenticated result before revoking the old token. Last-used metadata alone does not account for every consumer. Do not reinstall an agent or delete its saved identity just to replace a credential; use its private token-file or managed configuration.

If a token or kiosk link has been exposed, revoke it promptly, even if that interrupts monitoring or a display. Replace it through the same private setup path. Do not leave a leaked token active while arranging a gradual rotation. For a server-address change without credential exposure, follow agent retargeting instead.

Never put a token or session cookie in a diagnostic command, screenshot or issue thread. For scripted API access, use the private header-file procedure, keep TLS verification enabled and do not share verbose or trace output.

Token Scopes

Scope Description
* (Full access) All permissions (legacy, not recommended)
monitoring:read View dashboards, metrics, alerts
monitoring:write Acknowledge, silence and clear alerts
docker:report Docker / Podman agent telemetry submission
docker:manage Docker / Podman container lifecycle actions (restart, stop)
kubernetes:report Kubernetes agent telemetry submission
kubernetes:manage Kubernetes cluster management
agent:report Agent host telemetry submission
agent:config:read Read agent config payloads
agent:manage Agent lifecycle/configuration changes and unregistering; not needed by the reporting preset
agent:exec Establish agent command WebSocket connections; not a reporting permission
ai:chat Use Pulse Assistant chat and read knowledge
ai:execute Use governed Patrol plans, approvals, actions, and history
settings:read Read configuration
settings:write Modify configuration, manage tokens and trigger updates
audit:read Read audit events, verification results, summaries, and exports

Presets

Use New token, enter a name under Create token, select a Quick preset and choose Generate. The current presets are:

Preset Scopes Use Case
Kiosk / Monitoring monitoring:read Read-only dashboard displays
Agent agent:report, agent:config:read Host telemetry and the agent's bound configuration
Docker / Podman report docker:report Docker / Podman agent (read-only)
Docker / Podman manage docker:report, docker:manage Docker / Podman agent with actions
Settings read settings:read Read-only config access
Settings admin settings:read, settings:write Full config access
Audit read audit:read Audit events, verification history, summaries and exports

Reporting is not remote execution. The Agent preset does not grant agent:manage or agent:exec; Docker / Podman report does not grant docker:manage. Add lifecycle or action authority only for the operation you intend, not to fix a missing reading. A settings-read token can read sensitive configuration and diagnostics; it is not a public-display credential. See agent security for the separate collector and action-runner requirements.

If Patrol external agent is offered, its scopes come from the current Patrol requirements. Use the displayed set rather than guessing or adding Full access. The separate Full access choice grants the legacy * wildcard, not a least-privilege preset. Hiding controls does not reduce a token's permissions.

Kiosk Mode

For a wall monitor, create a dedicated Kiosk / Monitoring token with only monitoring:read. Never use Full access, a settings token or your administrator's logged-in browser profile on an unattended display.

  1. Open Pulse at its trusted HTTPS address and go to Settings → API Access.
  2. Use New token, name the display, select Kiosk / Monitoring and choose Generate. Store the secret privately.
  3. After closing the token-reveal dialog, the Magic Kiosk Link offers Copy Link for this monitoring-only token. Transfer that link privately to the display; do not put it in shared bookmarks, screenshots or reports.
  4. Check the display using the link in a separate browser profile with no administrator session. Confirm it shows the intended monitoring data and does not offer settings or write actions. A hidden control alone is not proof of restricted access.

The link is a bearer credential: anyone with it can read the permitted infrastructure data until the token expires or is revoked. The kiosk=1 option only hides navigation and filters; it does not authenticate a browser or change the token's scopes.

Pulse removes the token from the page URL after loading and keeps it in that tab's session storage. The initial request already carried the token; HTTPS and the cleaned address bar do not remove copies from proxy/server logs, browser history or the clipboard. Do not rely on this session surviving a closed tab, browser restart or cleared site data. Keep the original link private if the display needs it again, and clear clipboard copies after setup.

If the display or link is lost or exposed, revoke its dedicated token in API Access and create a replacement. Revoking a display token should not require rotating unrelated agents' credentials.


TrueNAS

Pulse v6 supports first-class TrueNAS SCALE and CORE monitoring.

Adding a TrueNAS Instance

  1. Go to Settings → Infrastructure.
  2. Click Add infrastructure and choose TrueNAS.
  3. Enter the host (e.g., truenas.local), the port and whether to use HTTPS, then the API key and the username that owns the key.
  4. Click Test connection to verify, then Add connection.

Creating a TrueNAS API Key

On your TrueNAS system:

  1. Navigate to the TrueNAS UI → Settings → API Keys.
  2. Click Add and create a new read-only key.
  3. Copy the key value and paste it into Pulse.

What Gets Monitored

Data Where it appears
System info (CPU, memory, uptime) Infrastructure page
Virtual machines TrueNAS Overview
Apps TrueNAS Overview
ZFS Pools & datasets Storage page
Physical disks Storage page
ZFS Snapshots TrueNAS → Protection
Replication tasks TrueNAS → Protection
TrueNAS alerts Alerts page

TrueNAS connections are stored encrypted in truenas.enc.


Relay

The relay protocol provides end-to-end encrypted remote access foundations for Pulse mobile connectivity.

Supported Pulse Mobile clients pair here using the generated QR code or deep link once relay is enabled for this instance.

Configuration

  1. Go to Settings → Pulse Mobile.
  2. Turn on Allow Pulse Mobile connections.
  3. Use the QR Code or Deep Link to pair a supported Pulse Mobile client.

Environment Overrides

For headless / container deployments that need to bootstrap relay without going through the UI, two environment variables override the persisted relay.enc values at load time:

Variable Description Default
PULSE_RELAY_ENABLED Enable/disable relay (true/false/yes/no/1/0). Unset or unrecognized values leave the file value untouched. (unset)
PULSE_RELAY_SERVER Override relay server URL. Must be a valid ws:// or wss:// URL with no userinfo, query, or fragment. Invalid values are logged and ignored. wss://relay.pulserelay.pro/ws/instance

Precedence: env vars beat the file. If you set PULSE_RELAY_ENABLED=true, saving the relay settings in Settings → Pulse Mobile will then persist the env-effective state to disk, so removing the env var later does not automatically revert relay back to its previous file-stored state — clear relay in the UI as well if you want to fully disable it.

Security

  • All data is encrypted end-to-end using ECDH key exchange.
  • The relay server never sees plaintext monitoring data.
  • Each mobile session has its own encryption channel.
  • Requires a valid Relay, Pro, legacy Pro+, or Cloud license (gated by the relay feature key).

Relay config is stored encrypted in relay.enc.