Billionmail/BillionMail · error

failed to update SNI map: %v

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.

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)

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.