Billionmail/BillionMail · error

failed to update docker-compose.yml: %v

Error message

failed to update docker-compose.yml: %v

What it means

applyConfigs is step 1 of ApplyConfigsWithRollback: update the docker-compose topology by generating docker-compose_addnetwork.yml. If updateDockerCompose fails for any reason (dir creation, docker exec, read/modify/write), the error is wrapped with this prefix and triggers rollback of the Postfix configs. It is a wrapper — the root cause is inside updateDockerCompose.

Source

Thrown at core/internal/service/multi_ip_domain/config_manager.go:144

	return nil
}

// cleanupBackups Clean up backup files
func (m *ConfigManager) cleanupBackups(ctx context.Context, backups map[string]string) {
	for _, backup := range backups {
		if gfile.Exists(backup) {
			g.Log().Debugf(ctx, "Cleaning up backup file: %s", backup)
			_ = os.Remove(backup)
		}
	}
}

// applyConfigs Apply configurations
func (m *ConfigManager) applyConfigs(ctx context.Context, configs []map[string]interface{}) error {
	// 1. Update docker-compose.yml
	if err := m.updateDockerCompose(ctx, configs); err != nil {

		return fmt.Errorf("failed to update docker-compose.yml: %v", err)
	}

	// 2. Update Postfix configurations
	if err := m.updatePostfixConfigs(ctx, configs); err != nil {
		return fmt.Errorf("failed to update Postfix configurations: %v", err)
	}

	return nil
}

// updateDockerCompose Generate docker-compose_addnetwork.yml
func (m *ConfigManager) updateDockerCompose(ctx context.Context, configs []map[string]interface{}) error {
	originalPath := filepath.Join(public.HostWorkDir, "docker-compose.yml")
	outputPath := filepath.Join(public.HostWorkDir, "docker-compose_addnetwork.yml")

	// 设置临时文件路径
	// 容器内路径:/opt/billionmail/core/data (通过 public.AbsPath 获取)
	// 宿主机路径:./core-data (相对于 docker-compose.yml 所在目录)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Unwrap the inner error to find the failing step (temp dir, copy, read, modify, or output copy).
  2. Verify docker-compose.yml exists at HostWorkDir and the core-data host volume is mounted and writable.
  3. Confirm the container can run chroot /host_root commands and the Docker API is reachable (docker ps from inside).
  4. After fixing, re-run ApplyConfigsWithRollback; it already restored Postfix configs via rollback, so retry is safe.
Defensive patterns

Strategy: validation

Validate before calling

// preflight before apply
if _, err := os.Stat(filepath.Join(public.HostWorkDir, "docker-compose.yml")); err != nil {
	return fmt.Errorf("docker-compose.yml missing: %v", err)
}
if _, err := os.Stat(filepath.Join(public.HostWorkDir, "core-data")); err != nil {
	return fmt.Errorf("core-data volume not mounted: %v", err)
}

Try / catch

if err := mgr.ApplyConfigsWithRollback(ctx, configs); err != nil {
	if strings.Contains(err.Error(), "failed to update docker-compose.yml") {
		// inner cause is in the wrapped text; check docker socket, chroot, mounts
	}
}

Prevention

When it happens

Trigger: Any updateDockerCompose failure: cannot create the temp data dir, chroot cp of docker-compose.yml fails, temp file empty, content modification error, or copy of the output file back to the host fails.

Common situations: Missing /host_root chroot capability or docker API socket inaccessible; docker-compose.yml not at public.HostWorkDir; core-data volume not mounted so the container↔host temp path mapping is broken; malformed compose content.

Related errors


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