Billionmail/BillionMail · error

Cannot read DKIM public key: %v

Error message

Cannot read DKIM public key: %v

What it means

Thrown when the DKIM public key file (dkimPubPath inside/on the rspamd container) cannot be read. The library needs the key's base64 body to assemble the DNS TXT record, so failure here aborts DKIM record generation.

Source

Thrown at core/internal/service/domains/domains.go:645

			if err != nil {
				err = fmt.Errorf("Failed to write DKIM sign config: %v", err)
				return
			}

			// Restart rspamd service
			err = dk.RestartContainerByName(context.Background(), consts.SERVICES.Rspamd)
			if err != nil {
				err = fmt.Errorf("Failed to restart rspamd container: %v", err)
				return
			}
		}
	}

	// DKIM public key is typically stored in a specific location in the container or host
	// Assuming we use docker exec to read the DKIM public key from the rspamd container
	dkimPub, err := public.ReadFile(dkimPubPath)
	if err != nil {
		err = fmt.Errorf("Cannot read DKIM public key: %v", err)
		return
	}

	// Format DKIM record
	// Expected format is a pre-formatted TXT record value like "v=DKIM1; k=rsa; p=MIIBIjANBg..."
	dkimRecord := strings.TrimSpace(dkimPub)

	// If the raw public key is read, format it into DNS TXT record format
	if !strings.Contains(dkimRecord, "v=DKIM1") && !strings.Contains(dkimRecord, "k=rsa") && !strings.Contains(dkimRecord, "p=") {
		// Remove possible header/footer markers and newlines
		dkimRecord = strings.ReplaceAll(dkimRecord, "-----BEGIN PUBLIC KEY-----", "")
		dkimRecord = strings.ReplaceAll(dkimRecord, "-----END PUBLIC KEY-----", "")
		dkimRecord = strings.ReplaceAll(dkimRecord, "\n", "")
		dkimRecord = strings.TrimSpace(dkimRecord)
		dkimRecord = fmt.Sprintf("v=DKIM1; k=rsa; p=%s", dkimRecord)
	} else {
		var ms [][]string
		ms, err = gregex.MatchAllString(`"([^"\r\n]+)"`, dkimRecord)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Confirm the DKIM key file exists at dkimPubPath (ls) and regenerate it via RepairDKIMSigningConfig if missing
  2. Fix read permissions/ownership on the key file
  3. Verify the rspamd data volume is mounted so the key path resolves on the host
  4. Check the rspamd version/config for a changed dkim key path and update dkimPubPath accordingly

Example fix

// before: read fails silently for caller
if _, err := os.Stat(dkimPubPath); os.IsNotExist(err) {
    // regenerate keys first via RepairDKIMSigningConfig
}
dkimPub, err := public.ReadFile(dkimPubPath)
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(dkimPubPath); err != nil || fi.IsDir() || fi.Size() == 0 {
    return fmt.Errorf("DKIM public key missing or empty at %s — run RepairDKIMSigningConfig", dkimPubPath)
}

Type guard

func dkimKeyExists(path string) bool {
    fi, err := os.Stat(path)
    return err == nil && fi.Mode().IsRegular() && fi.Size() > 0
}

Try / catch

dkimPub, err := public.ReadFile(dkimPubPath)
if err != nil {
    log.Printf("DKIM public key unreadable at %s: %v — regenerate via RepairDKIMSigningConfig", dkimPubPath, err)
    return err
}

Prevention

When it happens

Trigger: public.ReadFile(dkimPubPath) errors — the DKIM key file was never generated, path changed between rspamd versions, permissions deny read, or the file lives inside the container while the code reads a host path (or vice versa).

Common situations: Fresh domain where the keypair generation step was skipped or failed earlier, rspamd image update moving the key location, volume not mounted so the key file is absent on the host, wrong permissions after manual key regeneration.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/bf70a0c1a0d14c9b. Report an issue: GitHub.