Skip to content

bug: Service.inject_config() mutates global BentoMLContainer.config instead of using isolated configuration #5678

Description

@atharv0o

Describe the bug

Describe the bug

Service.inject_config() currently retrieves the process-wide configuration from BentoMLContainer.config, merges service-specific settings into it, and writes the modified configuration back to the container.

Simplified implementation:

existing = t.cast(dict[str, t.Any], BentoMLContainer.config.get())
deep_merge(existing, {
    "api_server": api_server_config,
    **rest_config,
})
BentoMLContainer.config.set(existing)

Because inject_config() updates the shared container configuration, every invocation operates on the configuration produced by previous calls rather than an isolated base configuration.

This raises concerns about configuration isolation during service initialization.

Observed behavior

  • inject_config() modifies the process-wide BentoMLContainer.config.
  • Service-specific configuration becomes part of the shared global state.
  • Later calls to inject_config() inherit modifications made by previous services.
  • The current behavior makes it difficult to determine whether service configuration is intended to be isolated or globally persistent.

This report is not about concurrent execution or race conditions. The behavior can be observed with sequential calls within the same Python process.

Steps to reproduce

service_a = Service(...)
service_a.inject_config()

config_after_a = BentoMLContainer.config.get()

service_b = Service(...)
service_b.inject_config()

config_after_b = BentoMLContainer.config.get()

Observe that the global container configuration has been modified by both service injections.

Expected behavior

The expected behavior depends on the intended design of BentoMLContainer.config.

Either:

  • inject_config() is expected to mutate the global configuration, in which case this behavior should be documented.

or

  • Each service should be configured using an isolated configuration object without polluting the process-wide container state.

Clarifying the intended lifecycle of BentoMLContainer.config would help determine the correct implementation.

Additional context

Before implementing a fix, it would be helpful to clarify whether BentoMLContainer.config is intended to represent:

  • Process-wide mutable configuration
  • Build-local configuration
  • Service-local configuration

The appropriate solution depends on the intended ownership and lifetime of this configuration object.

To reproduce

from _bentoml_sdk import service
from bentoml._internal.configuration.containers import BentoMLContainer

@service(name="service_a", workers=2)
class ServiceA:
    pass

@service(name="service_b", workers=3)
class ServiceB:
    pass

ServiceA.inject_config()
config_after_a = BentoMLContainer.config.get()

ServiceB.inject_config()
config_after_b = BentoMLContainer.config.get()

print(config_after_a)
print(config_after_b)

Observe that BentoMLContainer.config is modified after each call to
inject_config(), meaning subsequent service configuration starts from
the previously mutated global configuration.

Expected behavior

Service.inject_config() should have clearly defined configuration
ownership.

Either:

  • The mutation of BentoMLContainer.config is intentional and should be
    documented as process-wide state.

or

  • Each service should configure itself using an isolated configuration
    object so that service initialization does not depend on mutations
    introduced by previous services.

The expected lifecycle of BentoMLContainer.config is currently
unclear.

Environment

python -m bentoml env

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions