Billionmail/BillionMail · error
failed to write dovecot config: %v
Error message
failed to write dovecot config: %v
What it means
After updating ssl_cert/ssl_key lines in memory, updateDovecotConfig writes the modified config back to c.DovecotSslConf with os.WriteFile (0755). A failure is wrapped as 'failed to write dovecot config: %v'. At this point the new cert/key files are already on disk, so the config file may not reference them until this succeeds. Thrown by SetSSL and SetDovecotSSL.
Source
Thrown at core/internal/service/mail_service/certificate.go:374
// 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 {
return fmt.Errorf("failed to write certificate file: %v", err)
}
if err := os.WriteFile(keyPath, []byte(keyPem), 0755); err != nil {
return fmt.Errorf("failed to write key file: %v", err)
}
// Update SSL certificate configuration
config := string(content)
config = c.updateConfigLine(config, "ssl_cert", "<"+certPath)
config = c.updateConfigLine(config, "ssl_key", "<"+keyPath)
if err := os.WriteFile(dovecotConf, []byte(config), 0755); err != nil {
return fmt.Errorf("failed to write dovecot config: %v", err)
}
return nil
}
// updateDovecotSNIConfig updates Dovecot SNI configuration
func (c *Certificate) updateDovecotSNIConfig(domain, certPem, keyPem string) error {
// Ensure domain directory exists
domainDir := filepath.Join(consts.SSL_PATH, domain)
if err := os.MkdirAll(domainDir, 0755); err != nil {
return fmt.Errorf("failed to create domain directory: %v", err)
}
sniCert := filepath.Join(domainDir, "fullchain.pem")
sniKey := filepath.Join(domainDir, "privkey.pem")
// Write certificate and key to files
if err := os.WriteFile(sniCert, []byte(certPem), 0755); err != nil {View on GitHub (pinned to fc36c76c05)
Solutions
- Make the dovecot conf file writable (chmod/chown, remove chattr +i with chattr -i)
- Remount the dovecot config volume read-write
- Free disk space
- After fixing, re-run SetSSL and restart the dovecot container so it picks up the new ssl_cert/ssl_key paths
Example fix
// before
if err := os.WriteFile(dovecotConf, []byte(config), 0755); err != nil {
return fmt.Errorf("failed to write dovecot config: %v", err)
}
// after
if err := os.WriteFile(dovecotConf, []byte(config), 0644); err != nil {
return fmt.Errorf("failed to write dovecot config %s: %w", dovecotConf, err)
}
return c.restartDovecot() Defensive patterns
Strategy: try-catch
Validate before calling
if info, err := os.Stat(dovecotConf); err != nil || info.IsDir() {
return fmt.Errorf("dovecot conf missing")
}
if f, err := os.OpenFile(dovecotConf, os.O_WRONLY, 0644); err != nil {
return fmt.Errorf("dovecot conf not writable: %w", err)
} else {
f.Close()
} Try / catch
if err := certSvc.SetSSL(ctx, domain); err != nil {
if strings.Contains(err.Error(), "failed to write dovecot config") {
// ensure conf volume is rw and not immutable, then retry
}
return err
} Prevention
- Mount dovecot config volumes read-write, not read-only
- Avoid chattr +i on generated config files
- After success, restart the dovecot container so new ssl paths load
When it happens
Trigger: SetSSL/SetDovecotSSL invoked when the dovecot ssl conf file is read-only, owned by another user, on a full disk, or the path became unwritable after the read step.
Common situations: Config volume mounted read-only (common with bind-mounted /etc/dovecot); file immutable bit set (chattr +i); SELinux/AppArmor denying write; disk full after writing certs.
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
- error saving default footer config file: %v
- error saving footer config file: %v
- error saving default prompt config file: %v
- error reading prompt config file: %v
- failed to read dovecot config: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/2e41d42f7f854631.
Report an issue: GitHub.