Billionmail/BillionMail · error

failed to update SNI map

Error message

failed to update SNI map: %v

What it means

After writing the domain cert/key, updatePostfixVMailConfig calls updatePostfixSNIMap to register the domain (via public.FormatMX) in Postfix's vmail_ssl.map, and wraps any failure with this message. The SNI map is what lets Postfix select the right cert per hostname at TLS handshake.

Solutions

  1. Verify the Postfix SNI map file exists and is writable (ls -l the path in c.PostfixSNIPath).
  2. Ensure the Postfix container (consts.SERVICES.Postfix) is up so postmap can run.
  3. Inspect the wrapped inner error — it distinguishes read/write failures from postmap exec failures.
  4. Recreate vmail_ssl.map with correct ownership if it is missing or corrupt.
  5. Retry SetSNI and confirm the MX-hostname entry appears in the map.

Example fix

// before
if err := c.updatePostfixSNIMap(public.FormatMX(domain), vmailCert, vmailKey); err != nil {
    return fmt.Errorf("failed to update SNI map: %v", err)
}
// after
if err := c.updatePostfixSNIMap(public.FormatMX(domain), vmailCert, vmailKey); err != nil {
    return fmt.Errorf("failed to update SNI map for %s: %w", domain, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling SetSNI / SetPostfixVMailCert:
if _, err := os.Stat(c.PostfixSNIPath); err != nil {
    return fmt.Errorf("sni map missing at %s: %v", c.PostfixSNIPath, err)
}
if !isContainerRunning(consts.SERVICES.Postfix) {
    return fmt.Errorf("postfix container not running")
}

Try / catch

err := svc.SetPostfixVMailCert(ctx, domain, cert, key)
if err != nil && strings.Contains(err.Error(), "failed to update SNI map") {
    log.Printf("SNI files written but map update failed — check vmail_ssl.map perms and postfix container: %v", err)
    // retry once after ensuring postfix is up
}

Prevention

When it happens

Trigger: SetSNI or SetPostfixVMailCert completes file writes but updatePostfixSNIMap fails — typically because the SNI map file (c.PostfixSNIPath) cannot be read/written or the postmap exec in the Postfix container fails.

Common situations: vmail_ssl.map missing from the Postfix conf volume; map owned by root; Postfix container not running so postmap exec fails; path mismatch between host and container mounts.

Related errors


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

Appendix: source

Thrown at core/internal/service/mail_service/certificate.go:306

	if err := os.MkdirAll(domainDir, 0755); err != nil {
		return fmt.Errorf("failed to create domain directory: %v", err)
	}

	vmailCert := filepath.Join(domainDir, "fullchain.pem")
	vmailKey := filepath.Join(domainDir, "privkey.pem")

	// Write certificate and key to files
	if err := os.WriteFile(vmailCert, []byte(csrPem), 0755); err != nil {
		return fmt.Errorf("failed to write certificate file: %v", err)
	}

	if err := os.WriteFile(vmailKey, []byte(keyPem), 0755); err != nil {
		return fmt.Errorf("failed to write key file: %v", err)
	}

	// Create SNI mapping table
	if err := c.updatePostfixSNIMap(public.FormatMX(domain), vmailCert, vmailKey); err != nil {
		return fmt.Errorf("failed to update SNI map: %v", err)
	}

	return nil
}

// updatePostfixSNIMap updates Postfix SNI mapping table
func (c *Certificate) updatePostfixSNIMap(domain, certPath, keyPath string) error {
	// Read Postfix configuration file
	content, err := os.ReadFile(c.PostfixSNIPath)
	if err != nil {
		return fmt.Errorf("failed to read postfix config: %v", err)
	}

	// Update SNI mapping table
	lines := strings.Split(string(content), "\n")
	addNewLine := true

	for i, line := range lines {

View on GitHub (pinned to fc36c76c05)