This commit includes comprehensive codebase cleanup and refactoring: ## Code Cleanup - Remove dead TypeScript code (types/monitoring.ts - 194 lines duplicate) - Remove unused Go functions (GetClusterNodes, MigratePassword, GetClusterHealthInfo) - Clean up commented-out code blocks across multiple files - Remove unused TypeScript exports (helpTextClass, private tag color helpers) - Delete obsolete test files and components ## localStorage Consolidation - Centralize all storage keys into STORAGE_KEYS constant - Update 5 files to use centralized keys: * utils/apiClient.ts (AUTH, LEGACY_TOKEN) * components/Dashboard/Dashboard.tsx (GUEST_METADATA) * components/Docker/DockerHosts.tsx (DOCKER_METADATA) * App.tsx (PLATFORMS_SEEN) * stores/updates.ts (UPDATES) - Benefits: Single source of truth, prevents typos, better maintainability ## Previous Work Committed - Docker monitoring improvements and disk metrics - Security enhancements and setup fixes - API refactoring and cleanup - Documentation updates - Build system improvements ## Testing - All frontend tests pass (29 tests) - All Go tests pass (15 packages) - Production build successful - Zero breaking changes Total: 186 files changed, 5825 insertions(+), 11602 deletions(-)
13 KiB
Docker Deployment Guide
Proxmox VE Users: Consider using the official installer instead, which automatically creates an optimized LXC container.
Quick Start
docker run -d \
--name pulse \
-p 7655:7655 \
-v pulse_data:/data \
--restart unless-stopped \
rcourtman/pulse:latest
- Inspect the bootstrap token generated inside the container:
docker exec -it pulse cat /data/.bootstrap_token - Access
http://your-server:7655 - Paste the bootstrap token when prompted, then complete the mandatory security setup
- Save the generated credentials and API token—they are shown only once and the bootstrap file is deleted after success
First-Time Setup
When you first access Pulse, you'll see the security setup wizard:
-
Create Admin Account
- Choose a username (default: admin)
- Set a password or use the generated one
- An API token is automatically generated
-
Save Your Credentials
- Download or copy them immediately
- They won't be shown again after setup
-
Access Dashboard
- Click "Continue to Login"
- Use your new credentials to sign in
Docker Compose
Basic Setup (Recommended for First-Time Users)
services:
pulse:
image: rcourtman/pulse:latest
container_name: pulse
ports:
- "7655:7655"
volumes:
- pulse_data:/data
restart: unless-stopped
volumes:
pulse_data:
Then:
- Run:
docker compose up -d - Access:
http://your-server:7655 - Complete the security setup wizard
- (Optional) Copy
.env.exampleto.envif you want to pre-configure credentials later
Pre-Configured Authentication (Advanced)
If you want to skip the setup wizard, you can pre-configure authentication:
services:
pulse:
image: rcourtman/pulse:latest
container_name: pulse
ports:
- "7655:7655"
volumes:
- pulse_data:/data
environment:
PULSE_AUTH_USER: 'admin'
# Plain text values are auto-hashed on startup. To use a bcrypt hash,
# escape $ as $$ (e.g. $$2a$$12$$...) so docker compose does not treat it
# as variable expansion.
PULSE_AUTH_PASS: 'super-secret-password'
# Provide one or more API tokens. Tokens can be raw values or SHA3-256 hashes.
# Use distinct tokens per automation target for easier revocation.
API_TOKENS: 'ansible-token,docker-agent-token'
# Optional legacy variable kept for compatibility; newest token is used if both are set.
# API_TOKEN: 'your-48-char-hex-token'
PULSE_PUBLIC_URL: 'https://pulse.example.com' # Used for webhooks/links
# Optional logging controls (v4.24.0+)
LOG_LEVEL: 'info'
LOG_FORMAT: 'auto' # auto | json | console
# LOG_FILE: '/data/pulse.log' # uncomment to mirror logs to a file
# LOG_MAX_SIZE: '100' # MB
# LOG_MAX_AGE: '30' # days
# LOG_COMPRESS: 'true'
# TZ: 'UTC'
restart: unless-stopped
volumes:
pulse_data:
⚠️ Important: If you paste a bcrypt hash instead of a plain-text password, remember that Compose treats $ as variable expansion. Escape each $ as $$. Example: $2a$12$... becomes $$2a$$12$$....
Using External .env File (Cleaner Approach)
Create .env file (no escaping needed here). You can copy .env.example from the repository as a starting point:
PULSE_AUTH_USER=admin
PULSE_AUTH_PASS=super-secret-password # Plain text (auto-hashed) or bcrypt hash
# Optional legacy token (used if API_TOKENS is empty)
API_TOKEN=your-48-char-hex-token # Generate with: openssl rand -hex 24
# Comma-separated list of tokens for automation/agents
API_TOKENS=${ANSIBLE_TOKEN},${DOCKER_AGENT_TOKEN}
PULSE_PUBLIC_URL=https://pulse.example.com # Recommended for webhooks
TZ=Asia/Kolkata # Optional: matches host timezone
# Logging controls (optional; take effect immediately after restart)
LOG_LEVEL=info
LOG_FORMAT=auto
# LOG_FILE=/data/pulse.log
# LOG_MAX_SIZE=100
# LOG_MAX_AGE=30
# LOG_COMPRESS=true
Note: Plain text credentials are automatically hashed for security. You can provide either plain text (simpler) or pre-hashed values (advanced).
Docker-compose.yml:
services:
pulse:
image: rcourtman/pulse:latest
container_name: pulse
ports:
- "7655:7655"
volumes:
- pulse_data:/data
env_file: .env
restart: unless-stopped
volumes:
pulse_data:
Updating Your Stack
docker compose pull # Fetch the latest Pulse image
docker compose up -d # Recreate container with zero-downtime update
If you change anything in .env, run docker compose up -d again so the container picks up the new values.
Generating Credentials (Optional)
Note: Since v4.5.0, plain text credentials are automatically hashed. Pre-hashing is optional for advanced users.
Simple Approach (Recommended)
# Just use plain text - Pulse auto-hashes for you
docker run -d \
-e PULSE_AUTH_USER=admin \
-e PULSE_AUTH_PASS=mypassword \
-e API_TOKENS="ansible-token,docker-agent-token" \
rcourtman/pulse:latest
Tip: Create one token per automation workflow (Ansible, Docker agents, CI jobs, etc.) so you can revoke individual credentials without touching others. Use Settings → Security → API tokens or
POST /api/security/tokensto mint tokens programmatically.
Advanced: Pre-Hashing (Optional)
# Generate bcrypt hash for password
docker run --rm -it rcourtman/pulse:latest pulse hash-password
# Generate random API tokens
ANSIBLE_TOKEN=$(openssl rand -hex 32)
DOCKER_AGENT_TOKEN=$(openssl rand -hex 32)
# Then pass them to the container via API_TOKENS
Data Persistence
All configuration and data is stored in /data:
.env- Authentication credentials (if using setup wizard)*.enc- Encrypted node credentials*.json- Configuration files.encryption.key- Auto-generated encryption key
Backup
docker run --rm -v pulse_data:/data -v $(pwd):/backup alpine tar czf /backup/pulse-backup.tar.gz -C /data .
Restore
docker run --rm -v pulse_data:/data -v $(pwd):/backup alpine tar xzf /backup/pulse-backup.tar.gz -C /data
Docker Workspace Highlights
Once the agent is reporting, open the Docker tab in Pulse to explore:
- Host grid with issues column – surfaces restart loops, health-check failures, and highlights hosts that have missed their heartbeat.
- Inline search – filter by host name, stack label, or container name; results update instantly in the grid and side drawer.
- Container drawers – show CPU/memory charts, restart counters, last exit codes, mounted ports, and environment labels at a glance.
- Time-since heartbeat – every host entry shows the last heartbeat timestamp so you can spot telemetry gaps quickly.
If a host remains offline, review Troubleshooting → Docker Agent Shows Hosts Offline.
Network Discovery
Pulse automatically scans common home/office networks when running in Docker.
How It Works
- Detects Docker environment automatically
- Scans multiple common subnets in parallel:
- 192.168.1.0/24 (most routers)
- 192.168.0.0/24 (very common)
- 10.0.0.0/24 (some setups)
- 192.168.88.0/24 (MikroTik)
- 172.16.0.0/24 (enterprise)
Result: Finds all Proxmox nodes without any configuration!
Custom Networks (Rarely Needed)
Only for non-standard subnets:
environment:
DISCOVERY_SUBNET: "192.168.50.0/24" # Only if using unusual subnet
Common Issues
Can't Access After Upgrade
If upgrading from pre-v4.5.0:
- You'll see the security setup wizard
- Complete the setup - your nodes are preserved
- Use your new credentials to login
Lost Credentials
If you've lost your credentials:
# Stop container
docker stop pulse
# Remove auth configuration
docker exec pulse rm /data/.env
# Restart and go through setup again
docker restart pulse
Setup Wizard Not Showing
This happens if you have auth environment variables set:
- Remove environment variables from docker-compose.yml
- Recreate the container
- Access the UI to see the setup wizard
Password Hash Issues
Common problems:
- Hash truncated: Must be exactly 60 characters
- Not escaped in docker-compose: Use
$$instead of$ - Wrong format: Must start with
$2a$,$2b$, or$2y$
Security Best Practices
- Always use HTTPS in production - Use a reverse proxy (nginx, Traefik, Caddy)
- Strong passwords - Use the generated password or 16+ characters
- Protect API tokens - Treat them like passwords
- Regular backups - Backup the
/datavolume regularly - Network isolation - Don't expose port 7655 directly to the internet
Updates & Rollbacks (v4.24.0+)
Docker images are still updated manually, but Pulse now records every upgrade/rollback attempt in Settings → System → Updates alongside the CLI instructions below.
Update to the latest image
docker pull rcourtman/pulse:latest
docker stop pulse
docker rm pulse
docker run -d --name pulse \
-p 7655:7655 \
-v pulse_data:/data \
--restart unless-stopped \
rcourtman/pulse:latest
- The update history entry includes the image tag, operator, and timestamps. Capture the
event_idin your change log. - After the container is back online, verify the adaptive scheduler is healthy:
curl -s http://localhost:7655/api/monitoring/scheduler/health \ | jq '.queue.depth'
Roll back to a prior release
- Choose a previous tag (for example
v4.23.2) from GitHub Releases or Docker Hub. - Redeploy the container with that tag:
docker pull rcourtman/pulse:v4.23.2 docker stop pulse && docker rm pulse docker run -d --name pulse \ -p 7655:7655 \ -v pulse_data:/data \ --restart unless-stopped \ rcourtman/pulse:v4.23.2 - The update history will log this as a rollback. Make sure to annotate the entry with the reason in your postmortem notes.
Tip: Keep the last known-good tag handy (for example in your compose file or infra repo) so rollbacks are a single change.
Environment Variables Reference
⚠️ Important: Environment variables always override UI/system.json settings. If you set a value via env var (e.g.,
DISCOVERY_SUBNET), changes made in the UI for that setting will NOT take effect until you remove the env var. This follows standard container practices where env vars have highest precedence.
Authentication
| Variable | Description | Example / Default |
|---|---|---|
PULSE_AUTH_USER |
Admin username | admin |
PULSE_AUTH_PASS |
Admin password (plain text auto-hashed or bcrypt hash) | super-secret-password or $2a$12$... |
API_TOKEN |
Legacy single API token (optional fallback) | openssl rand -hex 24 |
API_TOKENS |
Comma-separated list of API tokens (plain or SHA3-256 hashed) | ansible-token,docker-agent-token |
PULSE_AUDIT_LOG |
Enable security audit logging | false |
Locked out while testing a container? Create
/data/.auth_recovery, restart the container, and connect from localhost to reset credentials. Remove the flag file and restart again to restore normal authentication.
Network
| Variable | Description | Default |
|---|---|---|
FRONTEND_PORT |
Port exposed for the UI inside the container | 7655 |
BACKEND_PORT |
API port (same as UI for the all-in-one container) | 7655 |
BACKEND_HOST |
Bind address for the backend | 0.0.0.0 |
PULSE_PUBLIC_URL |
External URL used in notifications/webhooks | (unset) |
ALLOWED_ORIGINS |
Additional CORS origins (comma separated) | Same-origin only |
DISCOVERY_SUBNET |
Override automatic network discovery CIDR | Auto-scans common networks |
CONNECTION_TIMEOUT |
Proxmox/PBS API timeout (seconds) | 10 |
PORT |
Legacy alias for FRONTEND_PORT |
7655 |
System
| Variable | Description | Default |
|---|---|---|
TZ |
Timezone inside the container | UTC |
LOG_LEVEL |
Logging verbosity. Changing the env var and restarting updates Pulse immediately. | info |
LOG_FORMAT |
auto, json, or console output format. |
auto |
LOG_FILE |
Optional path inside the container to mirror logs (e.g. /data/pulse.log). Empty = stdout only. |
(unset) |
LOG_MAX_SIZE |
Rotate LOG_FILE after this size (MB). |
100 |
LOG_MAX_AGE |
Days to retain rotated log files. | 30 |
LOG_COMPRESS |
Compress rotated log files (true / false). |
true |
METRICS_RETENTION_DAYS |
Days of metrics history to keep | 7 |
Advanced Configuration
Custom Network
services:
pulse:
image: rcourtman/pulse:latest
networks:
- monitoring
# ... rest of config
networks:
monitoring:
driver: bridge
Resource Limits
services:
pulse:
image: rcourtman/pulse:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
# ... rest of config
Health Check
services:
pulse:
image: rcourtman/pulse:latest
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:7655/api/health"]
interval: 30s
timeout: 10s
retries: 3
# ... rest of config