#Troubleshooting
Start with the three basic checks:
curl -s https://<host>/health
sudo docker compose ps
sudo docker compose logs --tail 100 signetmail-api
#Sign-in and access
| Symptom |
Cause |
Fix |
| AADSTS50011 "reply URL does not match" |
Redirect URI missing |
In Entra → App registration → Authentication → Single-page application add https://<host>/portal/ (with the trailing slash). |
| AADSTS700016 application not found |
Wrong ENTRA_CLIENT_ID / tenant |
Compare .env with the app's Overview page; run sudo docker compose up -d. |
| AADSTS50105 user not assigned |
Assignment required is on and the user is not assigned |
Add them under Enterprise applications → SignetMail → Users and groups. |
| Portal says "You have no access" |
Signed in, but no SignetMail role |
An Owner grants a role under Access. For the very first Owner, check PORTAL_OWNER is exactly your UPN. |
| Blank page or endless redirect |
Wrong ENTRA_* values or PublicUrl; or the browser blocks third-party cookies for the Microsoft login |
Verify .env; try a private window. |
| 403 "Forbidden" on the portal |
Your IP is not in PORTAL_ALLOWED_IPS |
Add your address (or connect via VPN) and sudo docker compose up -d. |
| 429 "Too many requests" |
Rate limit reached |
Wait a minute; if legitimate traffic hits it, raise SignetMail:Security:RateLimitPerMinute. |
#Directory and groups
| Symptom |
Cause |
Fix |
lastSyncUtc missing or old |
Graph authentication failing |
Look in the logs. Typical: certificate not uploaded to Entra, wrong GRAPH_CERT_PASSWORD, certificate expired, admin consent missing for User.Read.All. |
| People search is empty |
Directory not loaded yet |
Wait for the first sync after start, or restart and check the log. |
| Insufficient privileges when listing groups |
Group.Read.All / GroupMember.Read.All not granted |
Add them as Application permissions (not Delegated), Grant admin consent, then sudo docker compose restart signetmail-api. |
| Group rule matches nobody |
Membership not read yet, or nested groups |
Membership is read after saving and at each sync. Only direct members are guaranteed; make the intended people direct members. |
| A user's new title is not in the signature |
Directory sync interval |
The directory is refreshed every 60 minutes; the add-in refetches on use, the agent every 4 hours. |
| Roles via group do not work |
Groups claim missing, or groups overage |
See Entra setup, step 6; grant the role directly as a workaround. |
#Signatures and rendering
| Symptom |
Cause |
Fix |
| Signature changes not visible to users |
Draft not published, or the rule uses a different signature |
Check the history in the editor and Who gets which signature for that person. |
| "Not published" next to a signature in the rule dialog |
No live version yet |
Publish it. |
| Empty line where a phone should be |
Value not wrapped in a condition |
Use {% if user.mobilePhone %}…{% endif %} or Condition: only if present. |
| Logo not shown for recipients |
Logo URL not public, or http:// |
Upload the logo in the portal (it is served from https://<host>/uploads/…) and make sure the server is reachable from the internet. A local test server (localhost) is not reachable for recipients. |
| Layout broken in Outlook |
Non-table layout, external CSS |
See the Compatibility check; use tables and inline styles. |
| Save rejected: "scripts … not allowed" |
Script, event handler or javascript: link in the template |
Remove it. |
| Save rejected: "ranges (a..b) not allowed" |
{% for i in (1..5) %} |
Loop over data instead. |
| Publishing fails with a template error |
Syntax error in Liquid |
The message names the line; fix and save again. |
| Two signatures in one mail |
Add-in and Outlook's own signature, or agent + transport rule |
The add-in turns off the manual signature; do not enable the transport rule for agent users. |
Special characters like + look odd in HTML source |
HTML encoding (+) |
Harmless; mail clients display it correctly. |
#Outlook add-in
| Symptom |
Cause |
Fix |
| Add-in not visible |
Deployment still propagating |
Wait (up to hours, sometimes a day); check the assignment in the admin center; restart Outlook. |
| Add-in shows a sign-in error |
brk-multihub://<host> redirect URI missing |
Add it in Entra. |
| Signature does not insert automatically |
Client does not support automatic insertion |
Use the ribbon Signature button; check that the client is new Outlook, OWA or Mac. |
| Wrong or missing signature |
User not matched, or user not in the directory |
Run Who gets which signature with the user's e-mail. |
| Shows an old signature when offline |
Cached copy used |
Reconnect; it refreshes next time. |
#Windows agent
| Symptom |
Cause |
Fix |
| Nothing is written |
No Kerberos ticket / SPN missing |
Check agent.log; verify setspn for the API name; run with --dry-run. |
| Outlook still shows the old signature |
Hash unchanged, or Outlook not restarted |
Run with --force; restart Outlook. |
| Outlook overwrites the signature from the cloud |
Roaming signatures enabled |
The agent sets DisableRoamingSignaturesTemporaryToggle; check it was applied in HKCU. |
#Server and containers
| Symptom |
Cause |
Fix |
| Browser: certificate warning |
Let's Encrypt failed |
DNS must point to the server; ports 80/443 open; sudo docker compose logs caddy. |
pull access denied for the image |
Not logged in to GHCR, or token lacks read:packages |
sudo docker login ghcr.io. |
chmod: Operation not permitted in setup.sh |
Older script version |
git pull in /opt/signetmail, or run sudo chmod 600 certs/graph.pfx manually. |
git: command not found in backup.sh |
git not installed |
sudo dnf -y install git. |
| API restarts in a loop |
Invalid configuration |
Read docker compose logs signetmail-api; typical: bad .env value, unwritable data/ (owner must be uid 1654). |
"Operation not permitted" writing to data/ |
Wrong owner |
sudo chown -R 1654 data. |
| Cannot SSH after hardening |
Firewall or key |
Use the provider's console; check the key and that your IP is allowed in the cloud firewall. |
When you ask for help, include: the output of /health, docker compose ps, the last 100 log lines (remove anything sensitive), the exact error text and what you were doing. Never include .env, the backup passphrase, tokens or certificates.