Billionmail/BillionMail · error

failed to read dovecot config: %v

Error message

failed to read dovecot config: %v

What it means

updateDovecotConfig reads the Dovecot SSL config file (c.DovecotSslConf) with os.ReadFile before rewriting ssl_cert/ssl_key lines. When that read fails (missing file, bad path, permission denied), the underlying OS error is wrapped as 'failed to read dovecot config: %v'. It is thrown by SetSSL and SetDovecotSSL when applying a new certificate to the mail server.

Source

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

	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 {
		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)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Verify the file at c.DovecotSslConf exists (ls -l) and is a regular readable file
  2. Fix the DOVECOT ssl conf path constant / volume mount so the file is present inside the container
  3. Run the process with sufficient permissions (root or the file's owning group)
  4. Reinstall/restore the default dovecot ssl conf from the image or conf/ directory

Example fix

// before
dovecotConf := c.DovecotSslConf
content, err := os.ReadFile(dovecotConf)
// after
dovecotConf := c.DovecotSslConf
if _, statErr := os.Stat(dovecotConf); statErr != nil {
    return fmt.Errorf("dovecot config %s missing: %w", dovecotConf, statErr)
}
content, err := os.ReadFile(dovecotConf)
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(c.DovecotSslConf); err != nil || info.IsDir() {
    return fmt.Errorf("dovecot ssl conf not readable at %s", c.DovecotSslConf)
}

Try / catch

if err := certSvc.SetSSL(ctx, domain); err != nil {
    if strings.Contains(err.Error(), "failed to read dovecot config") {
        // check mount/permissions on the conf path before retrying
    }
    return err
}

Prevention

When it happens

Trigger: Calling SetSSL or SetDovecotSSL when the Dovecot ssl conf path (c.DovecotSslConf) does not exist, points to a directory, or the process lacks read permission on it.

Common situations: Running inside a container where /etc/dovecot/conf.d/ssl.conf was never mounted; Docker volume remapped after an upgrade; wrong DOVECOT conf path constant after reorganization; running BillionMail as non-root without read access.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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