Billionmail/BillionMail · error
failed to hash postfix config: %v
Error message
failed to hash postfix config: %v
What it means
After persisting the SNI map, updatePostfixSNIMap execs 'postmap /etc/postfix/conf/vmail_ssl.map' inside the Postfix container to rebuild the .db lookup file, wrapping failure with this message. Without rehashing, Postfix keeps serving the stale SNI map.
Source
Thrown at core/internal/service/mail_service/certificate.go:342
for i, line := range lines {
if strings.HasPrefix(line, domain) {
lines[i] = fmt.Sprintf("%s %s %s", domain, keyPath, certPath)
addNewLine = false
break
}
}
if addNewLine {
lines = append(lines, fmt.Sprintf("%s %s %s", domain, keyPath, certPath))
}
if err := os.WriteFile(c.PostfixSNIPath, []byte(strings.Join(lines, "\n")), 0755); err != nil {
return fmt.Errorf("failed to write postfix config: %v", err)
}
// Rehash configuration
if _, err := c.dockerApiClient().ExecCommandByName(context.Background(), consts.SERVICES.Postfix, []string{"postmap", "/etc/postfix/conf/vmail_ssl.map"}, "root"); err != nil {
return fmt.Errorf("failed to hash postfix config: %v", err)
}
return nil
}
// updateDovecotConfig updates Dovecot configuration with new certificate
func (c *Certificate) updateDovecotConfig(csrPem, keyPem string) error {
dovecotConf := c.DovecotSslConf
content, err := os.ReadFile(dovecotConf)
if err != nil {
return fmt.Errorf("failed to read dovecot config: %v", err)
}
// Write certificate and key to files
certPath := public.AbsPath(filepath.Join(consts.SSL_PATH, "dovecot.crt"))
keyPath := public.AbsPath(filepath.Join(consts.SSL_PATH, "dovecot.key"))
if err := os.WriteFile(certPath, []byte(csrPem), 0755); err != nil {View on GitHub (pinned to fc36c76c05)
Solutions
- Verify the Postfix container is running (docker ps) and restart it if stopped.
- Confirm consts.SERVICES.Postfix matches the actual container/service name.
- Check the map file exists at /etc/postfix/conf/vmail_ssl.map inside the container (volume mount correct).
- Inspect the wrapped exec error/output from ExecCommandByName for the postmap stderr.
- Run postmap manually inside the container as a workaround, then retry SetSNI.
Example fix
// before
if _, err := c.dockerApiClient().ExecCommandByName(context.Background(), consts.SERVICES.Postfix, []string{"postmap", "/etc/postfix/conf/vmail_ssl.map"}, "root"); err != nil {
return fmt.Errorf("failed to hash postfix config: %v", err)
}
// after
if _, err := c.dockerApiClient().ExecCommandByName(context.Background(), consts.SERVICES.Postfix, []string{"postmap", "/etc/postfix/conf/vmail_ssl.map"}, "root"); err != nil {
return fmt.Errorf("failed to hash postfix config (postmap in %s): %w", consts.SERVICES.Postfix, err)
} Defensive patterns
Strategy: retry
Validate before calling
// before calling SetSNI, ensure the exec target is healthy:
if !dockerServiceRunning(consts.SERVICES.Postfix) {
return fmt.Errorf("postfix container %s not running", consts.SERVICES.Postfix)
}
// and that the map is visible in-container:
// docker exec <postfix> test -f /etc/postfix/conf/vmail_ssl.map Try / catch
err := svc.SetSNI(ctx, domain)
if err != nil && strings.Contains(err.Error(), "failed to hash postfix config") {
log.Printf("postmap exec failed — is the postfix container up? %v", err)
time.Sleep(5 * time.Second)
err = svc.SetSNI(ctx, domain) // retry once container healthy
} Prevention
- Add a healthcheck to the Postfix container and gate cert updates on it.
- Verify consts.SERVICES.Postfix matches the compose service name after renames.
- Confirm /etc/postfix/conf volume mapping includes vmail_ssl.map.
- Handle transient docker API errors with bounded retries/backoff.
- Run postmap after any out-of-band edits to vmail_ssl.map.
When it happens
Trigger: The docker exec of postmap fails because the Postfix container is stopped/restarting, the service name in consts.SERVICES.Postfix doesn't match a running container, or the map file isn't visible at /etc/postfix/conf/vmail_ssl.map inside the container.
Common situations: Postfix container crashed or mid-restart during cert update; docker API unreachable from the app container; volume mount path mismatch so the map file is absent in the container; wrong service name after a compose rename.
Related errors
- failed to update BILLIONMAIL_HOSTNAME in postfix container:
- failed to restart postfix container: %v
- failed to list containers: %w
- container with name %s not found
- failed to restart postfix container: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/04150f6aa217ccf1.
Report an issue: GitHub.