Billionmail/BillionMail · error
failed to create domain directory: %v
Error message
failed to create domain directory: %v
What it means
updatePostfixVMailConfig creates a per-domain directory under SSL_PATH (os.MkdirAll) before writing fullchain.pem/privkey.pem. If MkdirAll fails, the OS error is wrapped with this message. Per-domain SNI certs cannot be stored without this directory.
Source
Thrown at core/internal/service/mail_service/certificate.go:289
// Update Postfix virtual mail configuration
if err := c.updatePostfixVMailConfig(domain, csrPem, keyPem); err != nil {
return err
}
// Restart Postfix service
if err := c.restartPostfix(); err != nil {
return err
}
return nil
}
// updatePostfixVMailConfig updates Postfix virtual mail configuration
func (c *Certificate) updatePostfixVMailConfig(domain, csrPem, 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)
}
vmailCert := filepath.Join(domainDir, "fullchain.pem")
vmailKey := filepath.Join(domainDir, "privkey.pem")
// Write certificate and key to files
if err := os.WriteFile(vmailCert, []byte(csrPem), 0755); err != nil {
return fmt.Errorf("failed to write certificate file: %v", err)
}
if err := os.WriteFile(vmailKey, []byte(keyPem), 0755); err != nil {
return fmt.Errorf("failed to write key file: %v", err)
}
// Create SNI mapping table
if err := c.updatePostfixSNIMap(public.FormatMX(domain), vmailCert, vmailKey); err != nil {
return fmt.Errorf("failed to update SNI map: %v", err)
}View on GitHub (pinned to fc36c76c05)
Solutions
- Sanitize/validate the domain before using it as a directory name (reject '/', '..', empty).
- Ensure SSL_PATH exists and is writable by the process user.
- Check the wrapped OS error for the exact errno and fix the mount/permission accordingly.
- Verify the Docker volume backing SSL_PATH is present and writable.
- Retry SetSNI and confirm the domain directory now exists.
Example fix
// before
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)
}
// after
if strings.ContainsAny(domain, "/\\") || strings.Contains(domain, "..") {
return fmt.Errorf("invalid domain for ssl directory: %q", domain)
}
domainDir := filepath.Join(consts.SSL_PATH, domain)
if err := os.MkdirAll(domainDir, 0755); err != nil {
return fmt.Errorf("failed to create domain directory %s: %w", domainDir, err)
} Defensive patterns
Strategy: validation
Validate before calling
func validDomainForPath(d string) bool {
if d == "" || strings.ContainsAny(d, "/\\") || strings.Contains(d, "..") {
return false
}
return true
}
// before calling:
if !validDomainForPath(domain) { return fmt.Errorf("invalid domain: %q", domain) }
if err := os.MkdirAll(consts.SSL_PATH, 0755); err != nil { return err } Try / catch
err := svc.SetSNI(ctx, domain)
if err != nil && strings.Contains(err.Error(), "failed to create domain directory") {
log.Printf("validate domain characters and SSL_PATH writability: %v", err)
} Prevention
- Validate domains against a strict regex before using them as path segments.
- Pre-create and chown SSL_PATH at deployment time.
- Ensure the SSL volume is mounted before the app starts (depends_on/healthchecks).
- Reject wildcard names ('*.example.com') or sanitize them before path use.
When it happens
Trigger: SetSNI or SetPostfixVMailCert called with a domain whose directory path cannot be created — SSL_PATH missing/unwritable, invalid characters in domain used as path segment, or read-only filesystem.
Common situations: Wildcard names or user-supplied domains containing '/' or '..' causing bad path segments; SSL volume unmounted; parent directory owned by root; read-only container FS.
Related errors
- failed to write certificate file: %v
- failed to write key file: %v
- failed to update SNI map: %v
- unable to determine domain for noreply email
- failed to create email sender: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/973cc93700b22baf.
Report an issue: GitHub.