Billionmail/BillionMail · error
failed to write key file: %v
Error message
failed to write key file: %v
What it means
Same write path as the certificate error but for the private key file <SSL_PATH>/postfix.key. updatePostfixConfig wraps os.WriteFile failure for the TLS private key with the OS error embedded. Without this key Postfix cannot complete the TLS handshake, so SSL setup aborts.
Source
Thrown at core/internal/service/mail_service/certificate.go:244
// updatePostfixConfig updates Postfix configuration with new certificate
func (c *Certificate) updatePostfixConfig(csrPem, keyPem string) error {
mainCf := public.AbsPath(consts.POSTFIX_MAIN_CONF)
content, err := os.ReadFile(mainCf)
if err != nil {
return fmt.Errorf("failed to read postfix config: %v", err)
}
// 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 {View on GitHub (pinned to fc36c76c05)
Solutions
- Verify SSL_PATH exists and the process user can write to it (ls -ld, chown/chmod as needed).
- Check disk space and Docker volume mounts for the SSL path.
- Remove/rename any directory incorrectly sitting at the postfix.key path.
- Read the embedded OS error to distinguish permission vs space vs read-only causes.
- Re-run SetSSL and confirm postfix.key exists with correct contents.
Example fix
// before
if err := os.WriteFile(keyPath, []byte(keyPem), 0755); err != nil {
return fmt.Errorf("failed to write key file: %v", err)
}
// after
if err := os.WriteFile(keyPath, []byte(keyPem), 0600); err != nil {
return fmt.Errorf("failed to write key file %s: %w", keyPath, err)
} Defensive patterns
Strategy: validation
Validate before calling
keyPath := public.AbsPath(filepath.Join(consts.SSL_PATH, "postfix.key"))
if st, err := os.Stat(filepath.Dir(keyPath)); err != nil || !st.IsDir() {
return fmt.Errorf("ssl dir missing")
}
if err := unix.Access(filepath.Dir(keyPath), unix.W_OK); err != nil {
return fmt.Errorf("ssl dir not writable: %v", err)
} Try / catch
err := svc.SetSSL(ctx, domain, certPem, keyPem)
if err != nil && strings.Contains(err.Error(), "failed to write key file") {
log.Printf("verify SSL dir ownership and free space before retrying: %v", err)
} Prevention
- Check that postfix.key path is not shadowed by a directory after manual restores.
- Set key file mode to 0600 to satisfy strict permission checks by mail daemons.
- Ensure adequate free disk space before bulk SSL operations.
- Avoid running the service as a different UID than the one that created existing keys.
When it happens
Trigger: SetSSL or SetPostfixSSL invoked when SSL_PATH is missing/unwritable, the key write hits ENOSPC/EACCES/EROFS, or the key file path collides with a directory.
Common situations: Read-only or unmounted SSL volume in Docker; key file previously created by root and now written by a lower-privileged process; disk full; path exists as a directory.
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
- failed to write certificate file: %v
- Failed to save private key file: {}
- failed to write postfix config: %v
- Failed to save certificate file: {}
- failed to save certificate: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/78e3d93772cf984f.
Report an issue: GitHub.