Monitor who's sending email on behalf of your domain. Catch spoofing. Stop phishing.
DMARC (Domain-based Message Authentication, Reporting & Conformance) helps protect your domain from email spoofing and phishing. When you enable DMARC on your domain, email providers like Gmail, Outlook, and Yahoo send you aggregate reports showing:
- Who's sending email claiming to be from your domain
- Which emails passed or failed authentication (SPF/DKIM)
- How many emails were sent, and from which IP addresses
- Whether malicious actors are trying to impersonate your domain
The Problem: These reports arrive as compressed XML attachments in your inbox - nearly impossible to read or analyze manually.
The Solution: Parse DMARC automatically fetches these reports from your inbox, parses them, and displays everything in a beautiful dashboard. All in a single 14MB Docker image.
- 📧 Auto-fetches reports from any IMAP inbox (Gmail, Outlook, etc.)
- 📊 Beautiful dashboard with real-time statistics
- 🔍 See exactly who's sending email as your domain
- 🔧 Built-in DNS record generator for easy DMARC setup
- 📦 Single binary - no databases to install, no complex setup
- 🚀 Tiny 14MB Docker image
- 🔒 Secure TLS support
- 🌙 Dark mode support
This is the most important step! Without this, you won't receive any reports to analyze.
Add a DMARC TXT record to your domain's DNS:
Name: _dmarc.yourdomain.com
Type: TXT
Value: v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
What this means:
p=none- Monitor only (don't block emails yet)rua=mailto:dmarc@yourdomain.com- Send aggregate reports to this email address
Important: Replace dmarc@yourdomain.com with an actual email inbox you control. This is where Gmail, Outlook, Yahoo, etc. will send your DMARC reports.
DNS Examples:
- Cloudflare: DNS > Add record > Type: TXT, Name:
_dmarc, Content:v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com - Google Domains: DNS > Custom records > TXT, Name:
_dmarc, Data:v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com - AWS Route53: Create record > Type: TXT, Name:
_dmarc.yourdomain.com, Value:"v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com"
Reports typically start arriving within 24-48 hours.
Create a configuration file:
mkdir -p data
cat > config.json <<EOF
{
"imap": {
"host": "imap.gmail.com",
"port": 993,
"username": "dmarc@yourdomain.com",
"password": "your-app-password",
"mailbox": "INBOX",
"use_tls": true
},
"database": {
"path": "/data/db.sqlite"
},
"server": {
"port": 8080,
"host": "0.0.0.0"
}
}
EOFFor Gmail users: You'll need an App Password, not your regular Gmail password.
Run the container:
docker run -d \
--name parse-dmarc \
-p 8080:8080 \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/data:/data \
ghcr.io/meysam81/parse-dmarc:latestAccess the dashboard: Open http://localhost:8080 in your browser.
Once DMARC reports start arriving and Parse DMARC processes them, your dashboard will show:
- Total messages analyzed across all reports
- DMARC compliance rate (SPF/DKIM pass rates)
- Top sending sources (IP addresses and organizations sending as your domain)
- Authentication results (which emails passed/failed SPF and DKIM)
- Policy actions (how receiving servers handled your email)
This helps you:
- Verify your legitimate email services are properly configured
- Detect unauthorized use of your domain
- Gradually move from monitoring (
p=none) to enforcement (p=quarantineorp=reject)
Gmail:
{
"host": "imap.gmail.com",
"port": 993,
"username": "your-email@gmail.com",
"password": "your-app-password",
"use_tls": true
}Requires App Password
Outlook/Office 365:
{
"host": "outlook.office365.com",
"port": 993,
"username": "your-email@outlook.com",
"password": "your-password",
"use_tls": true
}Generic IMAP:
Most providers use port 993 with TLS. Check your provider's documentation.
# Fetch once and exit (useful for cron jobs)
docker exec parse-dmarc ./parse-dmarc -fetch-once
# Serve dashboard only (no fetching)
docker exec parse-dmarc ./parse-dmarc -serve-only
# Custom fetch interval (in seconds, default 300)
docker exec parse-dmarc ./parse-dmarc -fetch-interval=600Q: I'm not receiving any reports. What's wrong?
A: Check these things in order:
- Did you add the
_dmarcTXT record to your DNS? (Use a DNS checker likedig _dmarc.yourdomain.com TXT) - Wait 24-48 hours - reports aren't instant
- Is your domain sending/receiving email? No email = no reports
- Check your IMAP credentials are correct in
config.json
Q: Do I need SPF and DKIM set up first?
A: No! DMARC reports will show you whether SPF and DKIM are passing or failing, which helps you configure them correctly.
Q: What should my DMARC policy be?
A: Start with p=none (monitoring only). After reviewing reports and fixing any issues, gradually move to p=quarantine and then p=reject.
Q: How much email traffic do I need?
A: Any amount works. Even small domains with a few emails per day will receive useful reports.
Q: Can I use a Gmail account to receive reports?
A: Yes! Create a dedicated Gmail like dmarc@yourdomain.com, forward it to your personal Gmail if needed, and use Gmail's IMAP settings.
git clone https://github.com/meysam81/parse-dmarc.git
cd parse-dmarc
just install-deps
just build
./bin/parse-dmarc -config=config.jsonSee compose.yml for Docker Compose configuration.
GET /api/statistics- Dashboard statisticsGET /api/reports- List of reports (paginated)GET /api/reports/:id- Detailed report viewGET /api/top-sources- Top sending source IPsGET /metrics- Prometheus metrics endpoint
Parse DMARC includes production-ready Prometheus metrics for monitoring and alerting. Metrics are enabled by default and exposed at /metrics.
| Metric | Type | Description |
|---|---|---|
parse_dmarc_build_info |
Gauge | Build information (version, commit, build_date) |
| Metric | Type | Description |
|---|---|---|
parse_dmarc_reports_fetched_total |
Counter | Total DMARC report emails fetched from IMAP |
parse_dmarc_reports_parsed_total |
Counter | Total DMARC reports successfully parsed |
parse_dmarc_reports_stored_total |
Counter | Total DMARC reports stored in database |
parse_dmarc_reports_parse_errors_total |
Counter | Total parse errors |
parse_dmarc_reports_store_errors_total |
Counter | Total storage errors |
parse_dmarc_reports_attachments_total |
Counter | Total attachments processed |
parse_dmarc_reports_fetch_duration_seconds |
Histogram | Duration of fetch operations |
parse_dmarc_reports_last_fetch_timestamp_seconds |
Gauge | Unix timestamp of last successful fetch |
parse_dmarc_reports_fetch_cycles_total |
Counter | Total fetch cycles executed |
parse_dmarc_reports_fetch_errors_total |
Counter | Total fetch cycle errors |
| Metric | Type | Labels | Description |
|---|---|---|---|
parse_dmarc_imap_connections_total |
Counter | status | IMAP connection attempts (success/error) |
parse_dmarc_imap_connection_duration_seconds |
Histogram | IMAP connection establishment duration |
| Metric | Type | Description |
|---|---|---|
parse_dmarc_dmarc_reports_total |
Gauge | Total reports in database |
parse_dmarc_dmarc_messages_total |
Gauge | Total messages across all reports |
parse_dmarc_dmarc_compliant_messages_total |
Gauge | Total DMARC-compliant messages |
parse_dmarc_dmarc_compliance_rate |
Gauge | Overall compliance rate (0-100) |
parse_dmarc_dmarc_unique_source_ips |
Gauge | Number of unique source IPs |
parse_dmarc_dmarc_unique_domains |
Gauge | Number of unique domains |
| Metric | Type | Labels | Description |
|---|---|---|---|
parse_dmarc_dmarc_messages_by_domain |
Gauge | domain | Messages per domain |
parse_dmarc_dmarc_compliance_rate_by_domain |
Gauge | domain | Compliance rate per domain |
parse_dmarc_dmarc_reports_by_org |
Gauge | org_name | Reports per organization |
parse_dmarc_dmarc_messages_by_disposition |
Gauge | disposition | Messages by disposition type |
| Metric | Type | Labels | Description |
|---|---|---|---|
parse_dmarc_dmarc_spf_results |
Gauge | result | SPF authentication result counts |
parse_dmarc_dmarc_dkim_results |
Gauge | result | DKIM authentication result counts |
| Metric | Type | Labels | Description |
|---|---|---|---|
parse_dmarc_http_requests_total |
Counter | method, path, status | Total HTTP requests |
parse_dmarc_http_request_duration_seconds |
Histogram | method, path | HTTP request duration |
parse_dmarc_http_requests_in_flight |
Gauge | Current in-flight requests |
Standard Go runtime metrics are also exposed:
go_goroutines- Number of goroutinesgo_memstats_*- Memory statisticsgo_gc_*- Garbage collection metricsprocess_*- Process metrics (CPU, memory, file descriptors)
To disable the metrics endpoint:
# Command line
./parse-dmarc --metrics=false
# Environment variable
export PARSE_DMARC_METRICS=false
# Docker
docker run -e PARSE_DMARC_METRICS=false ghcr.io/meysam81/parse-dmarc:latestAdd Parse DMARC to your prometheus.yml:
scrape_configs:
- job_name: "parse-dmarc"
static_configs:
- targets: ["parse-dmarc:8080"]
scrape_interval: 30s
metrics_path: /metricsFor Kubernetes with ServiceMonitor (Prometheus Operator):
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: parse-dmarc
labels:
app: parse-dmarc
spec:
selector:
matchLabels:
app: parse-dmarc
endpoints:
- port: http
path: /metrics
interval: 30sA production-ready Grafana dashboard is included in grafana/dashboard.json.
- In Grafana, go to Dashboards > Import
- Upload
grafana/dashboard.jsonor paste its contents - Select your Prometheus datasource
- Click Import
# Copy dashboard to Grafana dashboards directory
cp grafana/dashboard.json /var/lib/grafana/dashboards/parse-dmarc/
# Copy provisioning config
cp grafana/provisioning.yaml /etc/grafana/provisioning/dashboards/parse-dmarc.yaml
# Restart Grafana or wait for it to pick up changes
systemctl restart grafana-server| Variable | Purpose |
|---|---|
datasource |
Prometheus datasource to query |
job |
Filter by Prometheus job label |
instance |
Filter by instance(s) |
domain |
Filter by monitored domain(s) |
| Section | What It Shows |
|---|---|
| Overview - Golden Signals | Compliance rate, total messages, reports count, time since last fetch |
| DMARC Authentication Results | SPF/DKIM pass rates, disposition breakdown, per-domain compliance |
| Report Sources & Organizations | Top reporting organizations (Google, Microsoft, etc.), messages by domain |
| IMAP & Fetch Operations | Connection health, fetch cycle monitoring, latency heatmaps |
| Error Tracking | Parse errors, storage errors, fetch failures |
| HTTP Server | Request rates, latency percentiles, error rates |
| Go Runtime | Goroutines, memory usage, GC stats, CPU usage |
Compliance Rate Gauge:
parse_dmarc_dmarc_compliance_rate
Messages Over Time:
rate(parse_dmarc_dmarc_messages_total[5m])
Compliance Rate by Domain:
parse_dmarc_dmarc_compliance_rate_by_domain
SPF/DKIM Pass Rate:
# SPF Pass Rate
parse_dmarc_dmarc_spf_results{result="pass"} / ignoring(result) sum(parse_dmarc_dmarc_spf_results) * 100
# DKIM Pass Rate
parse_dmarc_dmarc_dkim_results{result="pass"} / ignoring(result) sum(parse_dmarc_dmarc_dkim_results) * 100
Fetch Success Rate:
1 - (rate(parse_dmarc_reports_fetch_errors_total[1h]) / rate(parse_dmarc_reports_fetch_cycles_total[1h]))
IMAP Connection Health:
rate(parse_dmarc_imap_connections_total{status="success"}[5m]) /
(rate(parse_dmarc_imap_connections_total{status="success"}[5m]) + rate(parse_dmarc_imap_connections_total{status="error"}[5m]))
HTTP Request Latency (p95):
histogram_quantile(0.95, rate(parse_dmarc_http_request_duration_seconds_bucket[5m]))
Reports by Organization:
topk(10, parse_dmarc_dmarc_reports_by_org)
Example Prometheus alerting rules:
groups:
- name: parse-dmarc
rules:
- alert: DMARCComplianceLow
expr: parse_dmarc_dmarc_compliance_rate < 90
for: 1h
labels:
severity: warning
annotations:
summary: "DMARC compliance rate is below 90%"
description: "Current compliance rate: {{ $value }}%"
- alert: DMARCFetchFailures
expr: rate(parse_dmarc_reports_fetch_errors_total[15m]) > 0
for: 30m
labels:
severity: critical
annotations:
summary: "Parse DMARC fetch failures detected"
description: "IMAP fetch operations are failing"
- alert: IMAPConnectionErrors
expr: rate(parse_dmarc_imap_connections_total{status="error"}[5m]) > 0
for: 10m
labels:
severity: warning
annotations:
summary: "IMAP connection errors detected"
description: "Check IMAP credentials and server connectivity"
- alert: NoRecentFetch
expr: time() - parse_dmarc_reports_last_fetch_timestamp_seconds > 600
for: 5m
labels:
severity: warning
annotations:
summary: "No recent DMARC report fetch"
description: "Last fetch was {{ humanizeDuration $value }} ago"Complete monitoring stack:
version: "3.8"
services:
parse-dmarc:
image: ghcr.io/meysam81/parse-dmarc:latest
ports:
- "8080:8080"
volumes:
- ./config.json:/app/config.json
- ./data:/data
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- "--config.file=/etc/prometheus/prometheus.yml"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
volumes:
grafana-data:With prometheus.yml:
global:
scrape_interval: 15s
scrape_configs:
- job_name: "parse-dmarc"
static_configs:
- targets: ["parse-dmarc:8080"]Access:
- Parse DMARC Dashboard: http://localhost:8080
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000 (admin/admin)
This project is inspired by ParseDMARC but built for simplicity:
- Single 14MB binary vs Python + Elasticsearch + Kibana stack
- Built-in dashboard vs external visualization tools
- SQLite vs Elasticsearch (no JVM required)
- Zero dependencies vs complex setup
Issues and pull requests are welcome! Please check the issues page.
Apache-2.0 - see LICENSE for details.
Found this useful? Star the repo! ⭐
