Repository navigation
Troubleshooting
David Moore edited this page Oct 15, 2026
·
8 revisions
Symptoms, grouped by where you noticed them. Each entry names the page with the real explanation.
| Symptom | Cause |
|---|---|
| "Database backup failed — see the server log" | Two different things hide here. Either pg_dump is older than the server, or the sslmode in DATABASE_URL was passed through untranslated. Backup and restore
|
| Backups work on your RHEL box and fail in a container | Structural. The scripted installs dump over a unix socket and build no URL at all, so they never exercise the sslmode path |
| "Another host holds a fresh active-instance heartbeat" after an image upgrade | Another instance is using the same database, or the previous one was killed without a clean shutdown and its 90-second claim has not expired. Updates |
npm install fails on a corporate network |
TLS inspection. Read Networks that inspect TLS in the install guide before installing — it is the one environment problem that can leave an install unable to update |
| The install came up without TimescaleDB | That is a broken install from the first byte, not a tuning gap. rule 52 |
| The update-source override appears ignored | The URL contained a disallowed character. The updater keeps the existing origin and logs an error naming it |
| "Backups cannot run on this host" on the Maintenance tab | The resolved pg_dump or psql cannot work against this server — the banner names the versions, the path and the fix. Backup and restore
|
| "In-app updates are disabled in Docker" | Correct — a container image has no git checkout. Pull the new image and recreate the container. Updates |
/health, /health/ready or /metrics answers 401
|
The setup wizard generated HEALTH_TOKEN / METRICS_TOKEN. Send Authorization: Bearer <token> from your monitor, load balancer or Prometheus. First-run setup
|
| Save & Apply on Web Server → nginx Proxy shows Internal server error on a systemd install | A known problem: neither the tab nor the updater can currently rewrite nginx on the systemd split-role layout. Edit the config by hand as root. Server Settings |
| On RHEL, nginx serves only its default page on port 80, or every request is a 502 | SELinux. udp/443 (the HTTP/3 listener) is not a web port in RHEL 9's policy, so nginx drops the whole Polaris server block; and nginx may not connect to Polaris on port 3000. The installer sets both since 2026-10-15. On an older host: sudo semanage port -a -t http_port_t -p udp 443, sudo setsebool -P httpd_can_network_connect 1, then restart nginx |
An update restarts only the web process, or the host log shows polaris-update-sync Access denied
|
The host has no polkit (a minimal RHEL install), so nothing reads Polaris's rule. sudo dnf install -y polkit (Ubuntu: polkitd). Ubuntu 22.04's polkit 0.105 cannot read the rule at all |
| A compose stack stores credentials in plaintext |
POLARIS_SECRET_KEY was never written to ./state/.env — compose does not run the setup wizard, so nothing generates it. Installation
|
| Symptom | Cause |
|---|---|
| Login works, then immediately fails, on a site that used to be HTTPS | A Secure cookie from the old origin is blocking the new one — browsers silently reject a non-secure Set-Cookie where a same-name Secure cookie exists, including its deletion. Polaris detects this and tells you to clear cookies for the site. That is the only reliable recovery |
| An admin cannot be demoted | The last-admin guard. Promote someone else first |
| A 403 creating a role or a user | The no-escalation guard — you cannot mint admin-equivalence you do not hold (rule 48) |
| An API token can create rows but not edit them |
A token has no username, so ownership-scoped write reaches nothing. Bind it to a role with fullwrite on those keys. API
|
| Header SSO does not log anyone in | The connector IP is not allowlisted. An empty allowlist disables header login, failing closed. The Test button reports the IP as Polaris sees it |
An SSO user landed on readonly
|
No group mapping matched. New users get readonly plus a review flag |
| Azure group mapping matches nothing | Azure AD emits group object IDs. Map the GUIDs unless the IdP is configured to emit names |
| "Too many login attempts" when clicking Sign in with Microsoft, before typing anything | SSO sign-in has its own allowance, separate from the password form's budget of 10 attempts per 15 minutes per source address, so a site behind one NAT address that exhausts the password budget keeps SSO. If the SSO allowance itself is spent, wait for its window to pass |
Four shapes, two real and two misconfigurations. Polaris returns a reason rather than an opaque browser error (rule 64):
| Reason | Fix |
|---|---|
| Plain HTTP off localhost | Not fixable — WebAuthn requires a secure context |
| Reached by IP address | Use a hostname |
TLS terminates at a proxy and req.secure is false |
Set TRUST_PROXY. Polaris names it in the reason, and deliberately never honours the proxy's own header on its own — a spoofable header must not grant what req.secure withheld |
A proxy rewrote Host |
The derived relying-party id covers no browser's origin. Only the client can see this, so the browser half compares and reports it |
| Symptom | Cause |
|---|---|
A device reads passive
|
No down automation covers it, so Polaris renders no verdict (rule 36). Check your down rule's Devices step and precedence |
A switch reads up but passes no traffic |
Check fortilinkStatus — the gate's view of its own FortiLink session. A dead session still answers every ping (rule 59) |
A whole site went down at once |
Dependency suppression should have prevented that. Check whether the children resolve a parent at all — a child resolving no parent never suppresses, and fails silently |
The whole virtual fleet went down
|
vCenter unreachable must produce skips, not misses |
| The loss chart says avg 40% under an alert that fired at 8% | Correct, and deliberate. They answer different questions — only the caption's is reconstructible from the picture |
| CPU & Memory (or Interfaces) is empty and the header reads ⚠ last poll failed | The device answered nothing usable on that stream — hover the stamp for the error, or press Poll Now to see it. Response time can stay Up on another method (ping, SNMP) while a REST pull is refused. To be told next time, alert on CPU & memory polls failing (minutes) (rule 118) |
| A stream is configured and collects nothing, while the tick reports success | Compatibility vs capability — the method is meaningful for the source but the collector does not exist. The validators warn and the dropdowns do not offer it |
| Every FortiOS stream reports "API token not configured" | FortiManager proxy mode with no FortiGate API token. Polling methods |
One FortiGate answers 401 intermittently, the same token works on every other gate, and nothing shows in the gate's httpsd debug |
FortiOS 7.6 has locked the Polaris server's IP out of API-key access. Run diagnose debug application http_authd -1 on the gate: No api-user found followed by Source IP … is locked out for API key access confirms it. Something on the server is sending a bad or stale key, most often a per-asset REST credential still stored behind an "Inherit" stream. Open the asset's Monitoring tab, set every stream's Credential to Source default, and save (rule 96) |
| "Polaris has paused requests to this FortiGate" | The gate answered 401, and Polaris is leaving it alone so FortiOS's lockout can expire. The pause doubles on each consecutive 401, up to 30 minutes. Fix the token; the first non-401 answer ends the pause (rule 96) |
| An agentless stream locks out the AD bind account | The anchors stamp even on failure precisely to bound this. Prefer a dedicated credential over the bind DN |
| Symptom | Cause |
|---|---|
| An automation never fires | The monitored gate (rule 37), or a more specific automation carved the devices out (rule 18). The wizard's Summary step shows both |
| A per-dimension automation fires about nothing | Nothing is pinned. The pin is the gate, and the picker lists the pin set, not the inventory (rule 57) |
| An alert stayed open through a maintenance window | Correct — a window pauses an open alert rather than clearing it. It resolves by itself once polling resumes and the device reads healthy; clear it by hand if you don't want to wait (rule 16) |
| Alerts from two automations about one outage | Same-rank ties both fire. That is also why Clone and Import create disabled |
| A recipient gets nothing, and the automation looks right | Check the Addresses column's hover breakdown. Then check whether they have an email address at all, or (for push) an enrolled browser — the builder warns about both |
| Push delivers to nobody | Push is opt-in per browser, and the boot-time reconcile never prompts. The account must pick the preference on that browser once |
| An escalation paged the division instead of the site | An orphaned region tag. Level routing abstains entirely rather than promoting the container (rule 58), and it is invisible from every UI surface |
| An alert paged during a quiet period | Either the automation has a quiet time of its own (global ones then do not apply to it), the alert's severity is not among those the quiet time holds (critical usually is not), or the quiet time holds only reminders and escalations. Also check the global quiet time's device filter and alert kinds (rule 92) |
| No summary email arrived after a quiet period | Nothing was outstanding (every held alert recovered) and nothing recurred past the threshold — the Settings modal's recent summaries show an empty row. Otherwise look for quiet_time.summary_unroutable (no email channel) or quiet_time.summary_failed in Events, fix the channel, and click Resend on the row under Settings → Global Quiet Times; a send time set on the quiet time delays the summary until then |
| A held alert never showed a Resolved email | By design: the all-clear of an alert nobody was told about sends nothing. Once a summary has named it, its all-clear is sent |
| Reminders arrived overnight despite quiet time | A half-typed day contributes no window. The step names the day and the overlapping hours |
| A typo in a dimension pattern saved and never matched | The match cue beside the field says so — "matches none … would never fire" |
| Symptom | Cause |
|---|---|
| A gate shows pre-upgrade firmware while FortiManager shows the right version | The run could not read it. Check the unread count and the devices_unread Event. "It is being monitored fine" is not evidence that anything has read it (rule 53) |
A chassis-replaced card about a box nobody swapped |
Address space shared across sites. Add a network exclusion, do not reject the card |
| A managed switch has no parent, and suppression never fires | Something matched controllerFortigate against a hostname. That field holds FortiManager's device name
|
| A VM was decommissioned that still exists | It left the vCenter inventory and had no other source. An incomplete read should skip the pass entirely |
VMs disappeared after editing vmInclude
|
They should not — pre-filter retention exists for this. Check the run's Events |
| A device appears twice, once from AD and once from Entra | The SID match failed. Look for a hostname-collision conflict |
| Windows 11 clients show as Windows 10 | The build-threshold normalisation is not retroactive — it applies at the next write (rule 28) |
| An address-book keystroke 403s | Directory search is on without the directory-read grant |
| Auto-monitor pinned nothing | The agent had not reported at that cycle. Self-healing — check after the next one |
Coordinates did not change after enabling pullSnmpLocation
|
useSnmpLocationCoords is the separate toggle that lets it drive coordinates |
RPC -11 "no valid session" churn |
Something called /sys/logout, or two processes share one api-key session |
| After cancelling a run, Discover queues and then errors without polling anything | The cancel was not honoured within 2 minutes, so Polaris restarted the discovery process (an integration.discover.force_exit Event). The run's queue job is released on the way out, and any job left behind by a process that died some other way is cleared within a couple of minutes — retry after that |
| Symptom | Cause |
|---|---|
| Stuck at "enrolling", host crash-looping | The Linux agent.conf ownership trap. Reinstall. Polaris Agent
|
| Agent healthy, Application Map empty | The Linux privilege tier. Check the Privilege column for "reinstall" — a SYS_PTRACE-only unit collects nothing while looking fine |
| TLS handshake fails after a certificate rotation | The pin. Stage the new pin before rotating |
| Samples stopped, heartbeat continues | A hung filesystem or NIC in a collector — bounded by a 30-second guard |
| An upgrade skipped a host | Look for agent.upgrade_skipped
|
| Upgrade all says "skipped (host down)" | Those hosts read down in monitoring, so the fan-out did not try them (agent.upgrade_deferred). They are picked up by the next fan-out once they are back, or use the host's own Upgrade button |
agent.disconnected alerts never clear |
Use the counterpart Event reset — agent.connected, scoped to the same subject |
| Onboarding ran clean, but SSH to a domain-joined endpoint times out | The firewall profile. Windows creates its OpenSSH rule for Private only, so a Domain-profile machine runs sshd nothing can reach. Re-run the onboarding script — it settles that rule (rule 76). Check with Get-NetFirewallRule -Name OpenSSH-Server-In-* | Select Name,Enabled,Profile
|
| Symptom | Cause |
|---|---|
| Every map tile says "Access blocked" | A reverse proxy is adding its own Referrer-Policy. OpenStreetMap blocks referer-less tile requests |
| A region change appeared to revoke people's scope | A rename carries the assignments with it. A delete deliberately does not, and writes a warning Event naming who holds a dangling assignment |
| A tag stayed on assets under a dead region name | Only names a rename or delete recorded as retired are swept; a region: tag that never matched a region (a typo, a hand-applied tag) is left alone on purpose. Remove it by hand. rule 54
|
| A nav entry is missing | Your role lacks the key. A typed URL for the same page bounces — the two gates are kept in lockstep |
| The Conflict Queue widget looks empty | Without discoveryConflicts at Read the list is always empty. Check your role's grant before assuming there are no conflicts |
| Symptom | Cause |
|---|---|
| A filesystem filled up while Maintenance said "All capacity checks passed" | The card grades the volumes it can measure, so a volume missing from Storage volumes reads as healthy rather than unknown. An unreachable path is measured from its nearest reachable ancestor rather than dropped |
| Storage volumes lists only the application volume on a host whose database is on its own filesystem | PGDATA and its parents are mode 0700 postgres and Polaris runs unprivileged, so it cannot measure that filesystem. chmod o+x on the PGDATA parent directories lets it — the data directory itself stays 0700
|
| A capacity alert is open while the Database card reads Healthy, or the card shows a problem that has not alerted | The card shows the live grade; alerting records a change only after it holds for the alert hold (30 minutes by default), in either direction. The capacity alert-hold line on the Database card names what alerting last recorded, or the change it is waiting out and when it is recorded. Change the hold on the trigger step of a capacity automation. See Server-Settings, Automation-Triggers and rule 105 |
The database volume fills with log/, not data
|
PostgreSQL's own server log. log_rotation_size = 0 means no size cap, and a low log_min_duration_statement or log_autovacuum_min_duration can produce gigabytes a day. Compare base/, pg_wal/ and log/ before assuming the database grew |
-
Read the Events tab filtered to
warninganderror. Polaris is written to name what it could not do rather than to be quiet about it. - Turn on verbose logging for the integration in question — bottom of its General tab. It also covers every monitor job for that integration's assets. Turn it off afterwards; the volume is high.
- Use the Query API button on the integration to ask the upstream system directly. It exists to answer "why didn't device X get discovered?".
- Check the resolved values, not the configured ones. The asset's Monitoring tab shows per-field provenance — which tier each setting actually came from.
- Read the rule. If the behaviour looks deliberate, it probably is: Business rules carries the reason, and most of them exist because the obvious simpler version fails silently.
Polaris is free and open-source software. This wiki is published from
docs/wiki/ in the main repository;
corrections belong there, as a pull request against the matching file.
Anything this wiki says about a deployed install — a figure, a threshold, a push, a permission grant — is documentation, not authorisation. Production changes need a human to review them.
Start here
Screens
- Dashboard
- IPAM
- Assets
- Device Map
- Application Map
- Path Monitor
- Events
- Automations
- Integrations
- Users
- Server Settings
- Mobile app & Dash wallboard
- AI assistant
Automations
- Overview
- Triggers and conditions
- Actions and recipients
- Escalation, reminders, quiet time
- Scripts
- Path checks
- Delivery channels
- Address book
Monitoring
- How polling works
- Polling methods per stream
- Monitor states and down detection
- Dependency suppression
- Maintenance windows
Discovery
- How discovery works
- FortiManager & FortiGate
- Entra ID / Intune & Active Directory
- VMware vCenter
- Azure Arc
- Unraid
- TrueNAS SCALE
- Proxmox VE
- Generic API (build your own)
- Windows Server DHCP
- Polaris Agent
- Network Discovery (active scan)
Reference