No reports are arriving
Work down the chain — most often it’s the first item.
- Has it been 24 hours? Reports are sent once a day per receiving provider. Nothing arrives within minutes of publishing a record.
- Is the DMARC record live? Check it resolves and contains a
rua=:
The Domain detail page’s record inspection shows the same thing, parsed.dig +short TXT _dmarc.yourdomain.com - Does
rua=point at the mailbox you connected? A typo here fails silently — receivers just send reports somewhere else. - Is the source syncing? Check Report sources for the latest run status, and trigger a manual sync rather than waiting an hour.
- Is anyone actually sending mail from the domain? A parked domain with no traffic legitimately produces no reports.
Mailbox authentication fails
Nearly always credentials rather than configuration:
- Use an app password, not the account password — see connecting a mailbox.
- Confirm IMAP (or POP3) is enabled for the account. Many Microsoft 365 tenants disable IMAP basic auth entirely; if so, that mailbox can’t be used yet.
- Verify host and port —
993for IMAP,995for POP3, both with TLS. - If you added
Security__CredentialEncryptionKeyafter saving a mailbox, its password is re-encrypted on the next sync — re-save the mailbox if the key changed.
A POP3 source refuses to sync
the POP3 server does not support UIDL on the source’s health row. UIDL is
optional in the protocol, and without it nothing identifies a message across
sessions — so there is no position to resume from and every pass would re-read the
whole mailbox for ever. The sync refuses rather than doing that.
Move the mailbox to IMAP, or use a POP3 server that implements UIDL. Nothing else about the source is wrong, and no data is lost by switching the protocol on the existing source.
A POP3 or S3 source re-scans everything every pass
The counter for messages or objects scanned stays equal to the whole mailbox or prefix on every run, which is work rather than incorrect data — deduplication means nothing is ingested twice.
- POP3 — the checkpoint UIDL is no longer in the mailbox, so there is no position to resume from. The worker logs a warning saying exactly that. Usually something else is deleting from the same mailbox; a second client configured to “remove from server after download” is the classic one.
- S3 — the checkpoint isn’t advancing, or something is rewriting the objects
and moving their last-modified time forward. Compare
lastProcessedObjectKeyon/api/v1/mailbox-healthagainst the newest key in the prefix.
An S3 source fails with “Access Denied” or “no such bucket”
The run row carries the AWS SDK’s own message, so read it before changing anything — the bucket name, the region or the credential is wrong, or the key is missing a permission.
A read-only source needs s3:ListBucket and s3:GetObject; retention deletion
also needs s3:DeleteObject. If you set an endpoint for MinIO or R2, the region is
ignored, and if you left both credential fields empty the ambient chain has to
supply one — an instance role or IRSA that the container can actually reach.
The worker isn’t ingesting anything
Check it’s running and what it’s saying. On the default single-container install
the polling loop lives in the app container; if you split them, use worker.
docker compose ps
docker compose logs --tail=50 app
Exited— most likely it started against a database with no schema. On a split stack the worker waits for the console to report healthy; if you wrote your own compose file, reproduce that ordering.- Running but idle — with default settings it polls every 60 minutes. Confirm
at least one report source is active and is a polled one:
imap,pop3ands3are polled,apiis pushed to and is never polled by the worker. - Repeated
Worker scheduler pass failed— the message includes the underlying error; a database connection problem is the usual cause.
”Another worker already holds the ingestion lock”
The container exits on startup with that message. It is doing the right thing: only one worker may run against a database. Two ingestion loops duplicate every sync pass and can send duplicate alert and digest email, so the application takes a Postgres advisory lock and a second worker refuses to start.
Usual causes:
- A
workercontainer alongside anAPP_MODE=allone. The combined container already runs the loop. Either drop the separate worker, or switch the app toAPP_MODE=api— thecompose.split.ymloverlay does both at once, which is why using it is safer than editing by hand. docker compose up --scale worker=2. Not supported. Scale the console instead (APP_MODE=apireplicas take no lock).- A worker that was killed abruptly. If the previous container was
SIGKILLed or the host lost power, Postgres still holds its lock until it notices the dead connection — usually a minute or two. The replacement will crash-loop until then and then start normally. Wait rather than intervening.
You can confirm who holds it:
docker compose exec -T postgres psql -U postgres -d dmarc_analyzer -tAc \
"select count(*) from pg_locks where locktype = 'advisory'"
Kubernetes: pods restart a few times on first install
Expected on a fresh install if you are not using the published chart. Every pod in
a release starts at once — Kubernetes has no equivalent of Compose’s
depends_on: condition: service_healthy — so the app can come up before PostgreSQL
is accepting connections, fail with Name or service not known, and be restarted
until the database is ready. It converges, but the restart backoff can turn a
20-second wait into minutes.
The chart handles this with a wait-for-database init container. If you wrote your
own manifests, add one.
Parse failures in the sync history
A run reporting parseFailures above zero means an attachment couldn’t be read.
Common causes:
- An unusual ZIP compression variant.
- Something in the mailbox that is neither a DMARC nor a TLS report — marketing mail and bounces land in shared inboxes.
TLS-RPT reports sharing the mailbox are not a cause: they are recognised, parsed and counted separately as TLS reports. If you are upgrading from a version before this was supported, expect the parse-failure count to drop on the next sync.
The rest of the run still imports; failures are counted, not fatal. If a mailbox shows persistent failures, it’s worth opening an issue with the attachment name.
The dashboard looks empty but reports imported
Analytics windows anchor to your newest report, not today. If you imported only historical data, “last 30 days” covers the 30 days before that newest report — so data exists but the default window may sit past it. Widen the window with the day selector.
The login form accepts my password and returns me to the login page
No error, no failed-login entry that helps, just a loop.
On 0.2.1 and earlier this was a bug, and upgrading fixes it. The session cookie
was always marked Secure, and browsers accept a Secure cookie over http://
only on localhost — so sign-in worked at http://localhost:8080 and silently
failed at http://dmarc.internal:8080 or http://10.0.5.7:8080. The server set
the cookie, the browser discarded it, and the next request arrived
unauthenticated. The flag now follows the request scheme, so plain HTTP on a LAN
signs in normally.
If you are on a newer version and still looping, the usual cause is a reverse
proxy that terminates TLS without telling the app. The browser is on https://,
the app sees http://, and it issues a cookie the browser then treats
inconsistently. Set:
Network__UseForwardedHeaders=true
Network__TrustedNetworks__0=172.16.0.0/12 # or your proxy's address
X-Forwarded-Proto is what tells the app the original request was HTTPS. The
reverse proxy guide has working configs — and the same
setting stops your audit trail recording the proxy instead of the caller.
To separate a cookie problem from a credentials problem without changing anything,
tunnel in and use localhost, which browsers always treat as a secure origin:
ssh -L 8080:localhost:8080 you@the-host # then open http://localhost:8080
I can’t sign in / lost the admin account
Registration is locked after the first-run bootstrap, so you can’t simply re-register. Options:
- Sign in with another
agency_adminaccount and reset the user. - If no admin remains, you’ll need database access to promote an existing user:
docker compose exec -T postgres psql -U postgres -d dmarc_analyzer \ -c "update agency_user set \"Role\"='agency_admin' where \"Email\"='you@example.com';"
If you enabled SSO and it broke, this is why keeping one local admin password matters.
A domain landed under the wrong client
Domains are auto-created under the mailbox source’s default client. Reassign the domain from the Domains page, and change the mailbox’s default client so future domains land correctly.
Cross-tenant requests return 404, not 403
Deliberate. A client_viewer requesting another client’s data gets 404 so the
API never confirms that a resource exists. Not a bug — if you’re seeing unexpected
404s, check the user’s client grants.
Getting help
Include the sync-run counters and the relevant docker compose logs excerpt when
filing an issue —
and please redact domain names and credentials.
On this page
- No reports are arriving
- Mailbox authentication fails
- A POP3 source refuses to sync
- A POP3 or S3 source re-scans everything every pass
- An S3 source fails with “Access Denied” or “no such bucket”
- The worker isn’t ingesting anything
- ”Another worker already holds the ingestion lock”
- Kubernetes: pods restart a few times on first install
- Parse failures in the sync history
- The dashboard looks empty but reports imported
- The login form accepts my password and returns me to the login page
- I can’t sign in / lost the admin account
- A domain landed under the wrong client
- Cross-tenant requests return 404, not 403
- Getting help
On this page
- No reports are arriving
- Mailbox authentication fails
- A POP3 source refuses to sync
- A POP3 or S3 source re-scans everything every pass
- An S3 source fails with “Access Denied” or “no such bucket”
- The worker isn’t ingesting anything
- ”Another worker already holds the ingestion lock”
- Kubernetes: pods restart a few times on first install
- Parse failures in the sync history
- The dashboard looks empty but reports imported
- The login form accepts my password and returns me to the login page
- I can’t sign in / lost the admin account
- A domain landed under the wrong client
- Cross-tenant requests return 404, not 403
- Getting help