Billionmail/BillionMail · error

failed to write postfix config: %v

Error message

failed to write postfix config: %v

What it means

After updating the smtpd_tls_key_file / smtpd_tls_cert_file lines, updatePostfixConfig fails to persist main.cf via os.WriteFile and wraps the OS error. The certificate files may already be written, leaving main.cf pointing at old cert paths until retried.

Source

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

	// Write certificate and key to files
	certPath := public.AbsPath(filepath.Join(consts.SSL_PATH, "postfix.crt"))
	keyPath := public.AbsPath(filepath.Join(consts.SSL_PATH, "postfix.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, "smtpd_tls_key_file", keyPath)
	config = c.updateConfigLine(config, "smtpd_tls_cert_file", certPath)

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

	return nil
}

// SetPostfixVMailCert configures SSL certificate for Postfix virtual mail
func (c *Certificate) SetPostfixVMailCert(domain, csrPem, keyPem string) error {
	// Validate certificate data
	if err := c.verifyCertificate(csrPem, keyPem); err != nil {
		return err
	}

	// Update Postfix Master configuration
	if err := c.SetPostfixMasterSSL(); err != nil {
		return err
	}

	// Update Postfix virtual mail configuration

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check that the resolved main.cf path exists and is writable by the service user (chown/chmod or fix the volume).
  2. Confirm the Postfix configuration volume is not mounted read-only in Docker Compose.
  3. Free disk space if ENOSPC is the wrapped error.
  4. Read the embedded %v error to pinpoint the errno before changing anything.
  5. Retry SetSSL; then reload/restart Postfix so main.cf changes take effect.

Example fix

// before
if err := os.WriteFile(mainCf, []byte(config), 0755); err != nil {
    return fmt.Errorf("failed to write postfix config: %v", err)
}
// after
if err := os.WriteFile(mainCf, []byte(config), 0644); err != nil {
    return fmt.Errorf("failed to write postfix config %s: %w", mainCf, err)
}
Defensive patterns

Strategy: validation

Validate before calling

mainCf := public.AbsPath(filepath.Join(consts.POSTFIX_CONF_PATH, "main.cf"))
if _, err := os.Stat(mainCf); err != nil {
    return fmt.Errorf("main.cf missing: %v", err)
}
if err := unix.Access(mainCf, unix.W_OK); err != nil {
    return fmt.Errorf("main.cf not writable: %v", err)
}

Try / catch

err := svc.SetPostfixSSL(ctx, domain)
if err != nil && strings.Contains(err.Error(), "failed to write postfix config") {
    log.Printf("postfix main.cf unwritable — check volume mounts and ownership: %v", err)
}

Prevention

When it happens

Trigger: SetSSL or SetPostfixSSL runs while the Postfix main.cf path is missing, read-only, permission-denied, or the disk is full — typically a container mount or ownership problem.

Common situations: Postfix conf volume mounted read-only; main.cf owned by root while service runs unprivileged; container filesystem in read-only mode; out-of-space host.

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


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