    def start(
        self,
        blocking: bool = False,
        env: dict[str, str] | None = None,
        stdin: _FILE = None,
        stdout: _FILE = None,
        stderr: _FILE = None,
        text: bool | None = None,
    ) -> t.ContextManager[ClientType]:
        """Start the server programmatically.

        To get the client, use the context manager.

        .. note::
           ``blocking=True`` and using ``start()`` as a context manager is mutually exclusive.

        Args:
            blocking: If True, the server will block until it is stopped.
            env: A dictionary of environment variables to pass to the server. Default to ``None``.
            stdin: The stdin to pass to the server. Default to ``None``.
            stdout: The stdout to pass to the server. Default to ``None``.
            stderr: The stderr to pass to the server. Default to ``None``.
            text: Whether to output as text or bytes for stdout and stderr. Default to ``None``.
        """
        # NOTE: User shouldn't manually set this since this envvar will be managed by BentoML.
        os.environ[BENTOML_SERVE_FROM_SERVER_API] = "True"

        if env is None:
            env = {}
        env.update(os.environ.copy())

        class _Manager:
            def __init__(__inner_self):
                logger.debug(f"Starting server with arguments: {self.args}")
                default_io_descriptor = None if blocking else subprocess.PIPE
                if text is None:
                    warn(
                        "Setting text to True will be the default behavior for bentoml 2.x. Set it explicitly to avoid breaking changes.\n"
                        + "For Example: 'server.start(text=False, ...)'"
                    )
                self.process = subprocess.Popen(
                    self.args,
                    stdout=stdout if stdout is not None else default_io_descriptor,
                    stderr=stderr if stderr is not None else default_io_descriptor,
                    stdin=stdin if stdin is not None else default_io_descriptor,
                    env=env,
                    # TODO: Make this default to True in 2.x
                    text=text if text is not None else False,
                )
                self.wait_until_ready()

                if blocking:
                    try:
                        self.process.wait()
                    except KeyboardInterrupt:
                        pass

            def __enter__(__inner_self) -> ClientType:
                return self.get_client()

            def __exit__(
                __inner_self,
                _exc_type: type[BaseException] | None,
                _exc_value: BaseException | None,
                _traceback: TracebackType | None,
            ):
                self.stop()

        return _Manager()
Start the server programmatically.

To get the client, use the context manager.

.. note::
``blocking=True`` and using ``start()`` as a context manager is mutually exclusive.

Args:
blocking: If True, the server will block until it is stopped.
env: A dictionary of environment variables to pass to the server. Default to ``None``.
stdin: The stdin to pass to the server. Default to ``None``.
stdout: The stdout to pass to the server. Default to ``None``.
stderr: The stderr to pass to the server. Default to ``None``.
text: Whether to output as text or bytes for stdout and stderr. Default to ``None``.
src/bentoml/server.py