feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options (#12313)

* feat: Enhance config loading by applying GUNICORN_CMD_ARGS before programmatic options

* docs: Add Gunicorn configuration details to environment variables documentation

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/backend/base/langflow/server.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add unit test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com>
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
(cherry picked from commit 9d14d3b34e)
This commit is contained in:
Arek Mateusiak
2026-04-15 17:49:43 +02:00
committed by Eric Hare
parent 5d6a7a0f31
commit 89d1c60bed
3 changed files with 81 additions and 0 deletions

View File

@ -417,6 +417,20 @@ The following environment variables set base Langflow server configuration, such
| `LANGFLOW_CELERY_ENABLED` | Boolean | `False` | Enable Celery for distributed task processing. |
| `LANGFLOW_ALEMBIC_LOG_TO_STDOUT` | Boolean | `False` | Whether to log Alembic database migration output to stdout instead of a log file. If `true`, Alembic logs to `stdout` and the default log file is ignored. |
#### Gunicorn configuration
When running Langflow in a production environment using Gunicorn, you can configure Gunicorn workers using the `GUNICORN_CMD_ARGS` environment variable.
This is particularly useful to manage worker memory and prevent potential memory leaks over time. You can add it to your `.env` file like this:
```text
GUNICORN_CMD_ARGS="--max-requests 100 --max-requests-jitter 20"
```
Here is what these arguments do:
- `--max-requests 100`: Automatically restarts a worker after it has processed 100 requests. This helps prevent memory leaks from accumulating over time.
- `--max-requests-jitter 20`: Adds a random jitter of up to 20 requests to the `--max-requests` value. This ensures that all workers don't restart simultaneously, which could cause a brief downtime or spikes in latency.
For more information about deploying Langflow servers, see [Langflow deployment overview](/deployment-overview).
### Storage

View File

@ -71,6 +71,18 @@ class LangflowApplication(BaseApplication):
super().__init__()
def load_config(self) -> None:
# Apply options from GUNICORN_CMD_ARGS env var before programmatic options
parser = self.cfg.parser()
cmd_args = self.cfg.get_cmd_args_from_env()
if cmd_args:
env_args = parser.parse_args(cmd_args)
for k, v in vars(env_args).items():
# Skip unset/positional args and only apply known settings
if v is None or k == "args" or k not in self.cfg.settings:
continue
self.cfg.set(k.lower(), v)
# Programmatic options override env args
config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None}
for key, value in config.items():
self.cfg.set(key.lower(), value)

View File

@ -0,0 +1,55 @@
import pytest
from langflow.server import LangflowApplication
def _make_app(options=None, env_args=None, monkeypatch=None):
"""Create a LangflowApplication with a dummy WSGI app.
Args:
options: Programmatic options passed to LangflowApplication.
env_args: If provided, set GUNICORN_CMD_ARGS env var before construction.
monkeypatch: pytest monkeypatch fixture for env manipulation.
"""
if env_args is not None and monkeypatch is not None:
monkeypatch.setenv("GUNICORN_CMD_ARGS", env_args)
def dummy_app(environ, start_response):
pass
return LangflowApplication(dummy_app, options=options)
class TestGunicornEnvArgs:
def test_env_args_applied(self, monkeypatch):
"""GUNICORN_CMD_ARGS values should be reflected in the config."""
app = _make_app(env_args="--max-requests 100 --max-requests-jitter 20", monkeypatch=monkeypatch)
assert app.cfg.settings["max_requests"].get() == 100
assert app.cfg.settings["max_requests_jitter"].get() == 20
def test_programmatic_options_override_env(self, monkeypatch):
"""Programmatic options must take precedence over GUNICORN_CMD_ARGS."""
app = _make_app(
options={"workers": 2},
env_args="--workers 8",
monkeypatch=monkeypatch,
)
assert app.cfg.settings["workers"].get() == 2
def test_no_env_var_uses_defaults(self):
"""Without GUNICORN_CMD_ARGS, Gunicorn defaults should remain intact."""
app = _make_app()
# Gunicorn default for max_requests is 0 (disabled)
assert app.cfg.settings["max_requests"].get() == 0
def test_env_does_not_override_worker_class(self, monkeypatch):
"""worker_class is always set programmatically and must not be overridden by env."""
app = _make_app(
env_args="--worker-class sync",
monkeypatch=monkeypatch,
)
assert app.cfg.settings["worker_class"].get() == "langflow.server.LangflowUvicornWorker"