{"record":{"id":"83aa5f345a818e20","repo":"Billionmail/BillionMail","slug":"failed-to-write-certificate-file-v","errorCode":null,"errorMessage":"failed to write certificate file: %v","messagePattern":"failed to write certificate file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/certificate.go","lineNumber":240,"sourceCode":"\t}\n\n\treturn nil\n}\n\n// updatePostfixConfig updates Postfix configuration with new certificate\nfunc (c *Certificate) updatePostfixConfig(csrPem, keyPem string) error {\n\tmainCf := public.AbsPath(consts.POSTFIX_MAIN_CONF)\n\tcontent, err := os.ReadFile(mainCf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read postfix config: %v\", err)\n\t}\n\n\t// Write certificate and key to files\n\tcertPath := public.AbsPath(filepath.Join(consts.SSL_PATH, \"postfix.crt\"))\n\tkeyPath := public.AbsPath(filepath.Join(consts.SSL_PATH, \"postfix.key\"))\n\n\tif err := os.WriteFile(certPath, []byte(csrPem), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to write certificate file: %v\", err)\n\t}\n\n\tif err := os.WriteFile(keyPath, []byte(keyPem), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to write key file: %v\", err)\n\t}\n\n\t// Update SSL certificate configuration\n\tconfig := string(content)\n\tconfig = c.updateConfigLine(config, \"smtpd_tls_key_file\", keyPath)\n\tconfig = c.updateConfigLine(config, \"smtpd_tls_cert_file\", certPath)\n\n\tif err := os.WriteFile(mainCf, []byte(config), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to write postfix config: %v\", err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/certificate.go#L222-L258","documentation":"updatePostfixConfig in the BillionMail mail_service Certificate module wraps the failure of os.WriteFile when saving the TLS public certificate to <SSL_PATH>/postfix.crt. The underlying OS error (permissions, missing directory, read-only mount, disk full) is embedded in the message. It is thrown while applying a newly issued/updated certificate so Postfix can serve TLS.","triggerScenarios":"Calling SetSSL or SetPostfixSSL when consts.SSL_PATH does not exist, the process lacks write permission to it, or the filesystem is full/read-only.","commonSituations":"Docker volume for the SSL path not mounted or mounted read-only; running container as non-root; SSL_PATH directory deleted by cleanup scripts; disk quota exhausted after bulk cert issuance.","solutions":["Ensure the SSL_PATH directory exists and is writable (mkdir -p and chown the path for the process user, or fix the Docker volume mount).","Check disk space (df -h) and inode usage on the SSL volume.","Inspect the wrapped %v error in logs to identify the exact errno (EACCES vs ENOSPC vs EROFS).","Run the container/service with sufficient privileges (root) since Postfix config paths are system locations.","Retry SetSSL after fixing storage; verify postfix.crt was written."],"exampleFix":"// before\nif err := os.WriteFile(certPath, []byte(csrPem), 0755); err != nil {\n    return fmt.Errorf(\"failed to write certificate file: %v\", err)\n}\n// after\nif err := os.MkdirAll(filepath.Dir(certPath), 0755); err != nil {\n    return fmt.Errorf(\"failed to create ssl directory: %v\", err)\n}\nif err := os.WriteFile(certPath, []byte(csrPem), 0644); err != nil {\n    return fmt.Errorf(\"failed to write certificate file %s: %w\", certPath, err)\n}","handlingStrategy":"validation","validationCode":"certPath := public.AbsPath(filepath.Join(consts.SSL_PATH, \"postfix.crt\"))\nif err := os.MkdirAll(filepath.Dir(certPath), 0755); err != nil {\n    return err\n}\nif fi, err := os.Stat(filepath.Dir(certPath)); err != nil || !fi.IsDir() {\n    return fmt.Errorf(\"ssl dir not usable: %v\", err)\n}\nif err := syscall.Access(filepath.Dir(certPath), unix.W_OK); err != nil {\n    return fmt.Errorf(\"ssl dir not writable: %v\", err)\n}","typeGuard":null,"tryCatchPattern":"cert, err := svc.SetSSL(ctx, domain, certPem, keyPem)\nif err != nil && strings.Contains(err.Error(), \"failed to write certificate file\") {\n    log.Printf(\"check SSL volume mount/permissions/disk space: %v\", err)\n}","preventionTips":["Mount the SSL path as a dedicated writable Docker volume and never ':ro'.","Run the app container with a user that owns the SSL directory.","Pre-create SSL_PATH at container startup in the entrypoint script.","Monitor disk space on the mail host (alert at 85%).","Keep cert/key files at 0644/0600, not 0755."],"tags":["filesystem","ssl-certificate","postfix","file-write"],"backgroundTag":"file-write-permission-denied","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}