Billionmail/BillionMail · error
failed to update BILLIONMAIL_HOSTNAME in .env file: %v
Error message
failed to update BILLIONMAIL_HOSTNAME in .env file: %v
What it means
Add (public) sets BILLIONMAIL_HOSTNAME in the .env file to the MX hostname (via public.FormatMX) when it is empty or still the default mail.example.com. This error wraps a failure of public.SetDockerEnv, which writes the .env file. It means the deployment environment variable could not be persisted.
Source
Thrown at core/internal/service/domains/domains.go:119
Username: mailbox + "@" + domain.Domain,
Password: grand.S(16),
FullName: mailbox,
IsAdmin: 0,
Quota: 5242880,
LocalPart: mailbox,
Domain: domain.Domain,
Active: 1,
})
}
// attempt update hostname in .env file
hostname := public.MustGetDockerEnv("BILLIONMAIL_HOSTNAME", "")
if hostname == "" || hostname == "mail.example.com" {
err = public.SetDockerEnv("BILLIONMAIL_HOSTNAME", public.FormatMX(domain.Domain))
if err != nil {
return fmt.Errorf("failed to update BILLIONMAIL_HOSTNAME in .env file: %v", err)
}
// update postfix environment parameter
_, err = public.DockerApiFromCtx(ctx).ExecCommandByName(ctx, consts.SERVICES.Postfix, []string{"bash", "-c", fmt.Sprintf("sed -i '/^BILLIONMAIL_HOSTNAME=/d' /postfix.sh && sed -i '/^#!\\/bin\\/bash/a BILLIONMAIL_HOSTNAME=%s' /postfix.sh", public.FormatMX(domain.Domain))}, "root")
if err != nil {
return fmt.Errorf("failed to update BILLIONMAIL_HOSTNAME in postfix container: %v", err)
}
// restart postfix service
err = public.DockerApiFromCtx(ctx).RestartContainerByName(ctx, consts.SERVICES.Postfix)
if err != nil {
return fmt.Errorf("failed to restart postfix container: %v", err)
}
}
// catchallView on GitHub (pinned to fc36c76c05)
Solutions
- Check the wrapped error: permission denied vs file-not-found on the .env path
- Fix .env file permissions/ownership so the process can write it
- Ensure the .env path is on a writable volume and disk has space
- Set BILLIONMAIL_HOSTNAME manually in .env and restart the stack if automated writes keep failing
Example fix
// before
err = public.SetDockerEnv("BILLIONMAIL_HOSTNAME", public.FormatMX(domain.Domain))
// after
if err := public.SetDockerEnv("BILLIONMAIL_HOSTNAME", public.FormatMX(domain.Domain)); err != nil {
logger.Warnf(ctx, "could not persist BILLIONMAIL_HOSTNAME: %v — set it manually in .env", err)
// decide: abort or continue
} Defensive patterns
Strategy: validation
Validate before calling
envPath := ".env"
fi, err := os.Stat(envPath)
if err != nil || fi.IsDir() { return errors.New(".env file missing — cannot persist BILLIONMAIL_HOSTNAME") }
if unix.Access(envPath, unix.W_OK) != nil { return errors.New(".env not writable by this process") } Try / catch
if err := public.SetDockerEnv("BILLIONMAIL_HOSTNAME", mx); err != nil {
if errors.Is(err, fs.ErrPermission) { return fmt.Errorf("fix .env ownership/permissions: %v", err) }
return fmt.Errorf("failed to update BILLIONMAIL_HOSTNAME in .env file: %v", err)
} Prevention
- Ensure the .env volume is writable by the app container user at deploy time
- Check disk space in health checks
- Keep BILLIONMAIL_HOSTNAME set correctly in .env so the auto-set path is skipped
- Mount .env as a regular volume, not read-only
When it happens
Trigger: Calling domains.Add/AddDomain for the first (or default-hostname) domain when SetDockerEnv fails — .env file missing, read-only filesystem, permission denied, or the file is malformed/unparseable.
Common situations: Docker Compose .env owned by root while app runs unprivileged; container running with read-only rootfs; .env mounted read-only volume; disk full on host.
Related errors
- environment file not found:
- failed to create Docker client: %v
- alert settings file not found
- alert settings file is empty
- dovecot conf dir not found: %s
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/bc2b53181c5f940c.
Report an issue: GitHub.