Fresh editor and API inspection shows that local and remote paths can coexist, including in published v6.4.5. Explain the actual location picker, per-path uncertainty and safe canonical-versus-legacy update fields, and validate the linked target-field instructions in the Docs renderer. Contract-Neutral: Documentation, shipped mirrors and guidance verification only; existing observation and API semantics are unchanged. Change-source: pulse-maintainer
45 KiB
⚙️ 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_DIRsets the base directory forsystem.json, encrypted files, and the bootstrap token.PULSE_METRICS_DB_PATHsets only the metrics SQLite database path. Use this for tmpfs-backed metrics history without moving secrets or config off the persistent data directory.
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_TOKENSin.envare 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 allowlist (single origin or "*")
"allowEmbedding": false, // Allow iframe embedding
"allowedEmbedOrigins": "", // Comma-separated origins for iframe embedding
"webhookAllowedPrivateCIDRs": "" // Allowlist for private webhook targets
}
Note
:
logFormatis only configurable via theLOG_FORMATenvironment variable, not insystem.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 polling interval (0 = auto) |
backupPollingEnabled |
Enable backup polling |
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 allowlist (single origin or *) |
allowEmbedding |
Allow iframe embedding |
allowedEmbedOrigins |
Comma-separated frame-ancestors allowlist |
webhookAllowedPrivateCIDRs |
Allowlist for private webhook targets |
updateChannel |
Update channel (stable or rc) |
autoUpdateEnabled |
Allow one-click updates |
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,subnetBlocklistmaxHostsPerScan,maxConcurrent,enableReverseDns,scanGatewaysdialTimeoutMs,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 |
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 (0 disables cleanup) |
30 |
LOG_COMPRESS |
Gzip rotated logs | true |
Log Levels
| Level | Description |
|---|---|
error |
Only errors and critical issues |
warn |
Errors + warnings (recommended for minimal logging) |
info |
Standard operational messages (startup, connections, alerts) |
debug |
Verbose output including per-guest/storage polling details |
Tip
: If your syslog is being flooded with Pulse messages, set
LOG_LEVEL=warnto significantly reduce log volume while still capturing important events.
| 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 |
CORS allowed origin (* or a single origin). Empty = same-origin only. |
(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 |
Allow unauthenticated config export on public networks when no auth is configured (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, returns404. A host that can reach the agent port cannot pivot to the management interface. - The main
FRONTEND_PORTlistener 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_PORTand 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.
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 embeddingallowedEmbedOrigins(comma-separated): restrictsframe-ancestorswhen 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 backup job monitoring | true |
BACKUP_POLLING_INTERVAL |
Backup polling frequency | 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 |
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 (days, 0 disables cleanup) |
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 one-click updates | false |
Note
: Update settings are stored in
system.json. Legacy.enventries (UPDATE_CHANNEL,AUTO_UPDATE_ENABLED) are kept in sync for backwards compatibility but are not read at runtime. The formerautoUpdateCheckInterval/autoUpdateTimefields were never consumed and are ignored if present; the update schedule lives in the systemd timer.
stableis the default and recommended production channel.rcis an opt-in preview channel. In v6, unattended systemd auto-updates remainstable-only even whenupdateChannelis set torc.
Auto-Import (Bootstrap)
You can auto-import an encrypted backup on first startup. This is useful for automated provisioning and test environments.
| Variable | Description |
|---|---|
PULSE_INIT_CONFIG_DATA |
Base64 or raw contents of an export bundle (auto-imports on first start) |
PULSE_INIT_CONFIG_FILE |
Path to an export bundle on disk (auto-imports on first start) |
PULSE_INIT_CONFIG_PASSPHRASE |
Passphrase for the export bundle (required) |
Note
:
PULSE_INIT_CONFIG_URLis only supported by the hiddenpulse config auto-importcommand, not by the server startup auto-import.
Configuration import restores server-side agent credentials, but it cannot change the primary Pulse URL persisted on remote agents. When restoring onto a server with a different address, retarget those agents after import. See Moving Pulse to a new address.
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:
metricsRetentionRawHoursmetricsRetentionMinuteHoursmetricsRetentionHourlyDaysmetricsRetentionDailyDays
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)
Pulse uses a powerful alerting engine with hysteresis (separate trigger/clear thresholds) to prevent flapping.
Managed via UI: Alerts → Thresholds
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
0to 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.
Manual Configuration (JSON)
{
"guestDefaults": {
"cpu": { "trigger": 90, "clear": 80 },
"memory": { "trigger": 85, "clear": 72.5 }
},
"schedule": {
"quietHours": {
"enabled": true,
"start": "22:00",
"end": "06:00"
}
}
}
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 probes run the system ping binary, which needs the CAP_NET_RAW
capability. The systemd unit written by the installer hardens the service
with NoNewPrivileges=true, which strips ping's setuid bit and file
capabilities, so the unit also grants the capability directly with
AmbientCapabilities=CAP_NET_RAW. Units written by older versions of the
installer lack that line, and ICMP probes fail with
icmp probe failed: ping: socktype: SOCK_RAW ... missing cap_net_raw+p capability.
To fix an existing install, either re-run the install script (it rewrites the unit) or add the capability as an override:
systemctl edit pulse # pulse-backend on ProxmoxVE community-script installs
[Service]
AmbientCapabilities=CAP_NET_RAW
Then systemctl daemon-reload && systemctl restart pulse.
Docker installs are unaffected: Docker's default capability set includes
NET_RAW. If you run the container with --cap-drop=ALL, add
--cap-add=NET_RAW to keep ICMP probes working. TCP and HTTP/HTTPS probes
need no special privileges.
🔒 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 ...
Important (Docker with HTTPS): Always use
--init(orinit: truein docker-compose) when enabling HTTPS. The Alpine-based healthcheck uses busyboxwget, which spawnsssl_clientsubprocesses. Without an init process to reap them, these become zombie processes over time.
🛡️ Security Best Practices
- Permissions: Ensure
.envandnodes.encare600(read/write by owner only). - Backup hygiene: Back up
.envseparately fromsystem.json. - 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.
- Open Pulse at its trusted HTTPS address and go to Settings → API Access.
- Use New token, name the display, select Kiosk / Monitoring and choose Generate. Store the secret privately.
- 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.
- 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
- Go to Settings → TrueNAS.
- Click Add Connection.
- Enter the URL (e.g.,
https://truenas.local) and an API key. - Click Test Connection to verify, then Save.
Creating a TrueNAS API Key
On your TrueNAS system:
- Navigate to the TrueNAS UI → Settings → API Keys.
- Click Add and create a new read-only key.
- 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 | Recovery page |
| Replication tasks | Recovery page |
| 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
- Go to Settings → Relay.
- Toggle relay On.
- 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 form in Settings → Relay 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
relayfeature key).
Relay config is stored encrypted in relay.enc.