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)
			}
		}

		// catchall

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check the wrapped error: permission denied vs file-not-found on the .env path
  2. Fix .env file permissions/ownership so the process can write it
  3. Ensure the .env path is on a writable volume and disk has space
  4. 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

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


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