Billionmail/BillionMail · error

failed to write DKIM signing config: %v

Error message

failed to write DKIM signing config: %v

What it means

RepairDKIMSigningConfig regenerates the Rspamd DKIM signing configuration block and overwrites the signing config file via public.WriteFile. This error wraps any failure from that write, meaning the new DKIM config content could not be persisted to disk.

Source

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

#%s_DKIM_END
`, d.Domain, d.Domain, d.Domain, d.Domain, d.Domain)
		allSignConfBlocks.WriteString(signConf)
	}

	// 3. Construct the final dkim_signing.conf content
	signConfPath := public.AbsPath(filepath.Join(consts.RSPAMD_LOCAL_D_PATH, "dkim_signing.conf"))
	finalSignContent := fmt.Sprintf(`sign_headers = "from:sender:reply-to:subject:date:message-id:to:cc:mime-version:content-type:content-transfer-encoding:content-language:resent-to:resent-cc:resent-from:resent-sender:resent-message-id:in-reply-to:references:list-id:list-help:list-owner:list-unsubscribe:list-subscribe:list-post:list-unsubscribe-post:disposition-notification-to:disposition-notification-options:original-recipient:openpgp:autocrypt";

domain {
#BT_DOMAIN_DKIM_BEGIN
%s
#BT_DOMAIN_DKIM_END
}`, allSignConfBlocks.String())

	// 4. Write the new content to the file, overwriting the old one
	_, err = public.WriteFile(signConfPath, finalSignContent)
	if err != nil {
		return fmt.Errorf("failed to write DKIM signing config: %v", err)
	}

	// 5. Ensure correct file permissions
	err = filepath.Walk(filepath.Join(public.AbsPath(consts.RSPAMD_LIB_PATH), "dkim"), func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if info.IsDir() {
			return os.Chmod(path, 0755)
		}
		if strings.HasSuffix(path, ".private") || strings.HasSuffix(path, ".pub") {
			return os.Chmod(path, 0644)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("failed to change DKIM file permissions: %v", err)
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Verify the signing config path (derived from RSPAMD_LIB_PATH / rspamd conf dir) exists and is writable by the process user
  2. Check disk space (df -h) and filesystem mount status (mount ro?)
  3. Ensure the rspamd conf volume is mounted and the process runs with sufficient privileges (or chown the conf dir)
  4. Inspect the wrapped %v error for the underlying OS cause (e.g. permission denied vs no such file)

Example fix

// before
_, err = public.WriteFile(signConfPath, finalSignContent)
if err != nil {
	return fmt.Errorf("failed to write DKIM signing config: %v", err)
}
// after
if err := os.MkdirAll(filepath.Dir(signConfPath), 0755); err != nil {
	return fmt.Errorf("failed to prepare dkim conf dir: %v", err)
}
_, err = public.WriteFile(signConfPath, finalSignContent)
if err != nil {
	return fmt.Errorf("failed to write DKIM signing config %s: %v", signConfPath, err)
}
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(filepath.Dir(signConfPath)); err != nil || !fi.IsDir() {
	return fmt.Errorf("dkim conf dir missing: %s", filepath.Dir(signConfPath))
}
if f, err := os.OpenFile(signConfPath, os.O_WRONLY, 0644); err != nil {
	return fmt.Errorf("signing config not writable: %v", err)
} else {
	f.Close()
}

Try / catch

err := RepairDKIMSigningConfig(ctx)
if err != nil && strings.HasPrefix(err.Error(), "failed to write DKIM signing config") {
	g.Log().Errorf(ctx, "check rspamd conf mount/permissions: %v", err)
}

Prevention

When it happens

Trigger: public.WriteFile(signConfPath, finalSignContent) returns non-nil: the signing config path is unwritable (permissions), the directory is missing, the filesystem is full/read-only, or the path resolves outside the container's mounted volume.

Common situations: Rspamd conf directory not bind-mounted into the app container; running the process as non-root without write access to /etc/rspamd or the RSPAMD_LIB_PATH volume; disk full on host; SELinux/AppArmor denying writes.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


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