diff --git a/docs/docs/cloud/reference/cli.md b/docs/docs/cloud/reference/cli.md
index b20073451..c6eda1431 100644
--- a/docs/docs/cloud/reference/cli.md
+++ b/docs/docs/cloud/reference/cli.md
@@ -42,8 +42,10 @@ The LangGraph CLI requires a JSON configuration file that follows this [schema](
| `dependencies` | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following:
- A single period (`"."`), which will look for local Python packages.
- The directory path where `pyproject.toml`, `setup.py` or `requirements.txt` is located.For example, if `requirements.txt` is located in the root of the project directory, specify `"./"`. If it's located in a subdirectory called `local_package`, specify `"./local_package"`. Do not specify the string `"requirements.txt"` itself.
- A Python package name.
|
| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: - `./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`
- `./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and creates an instance of `langgraph.graph.state.StateGraph` / `langgraph.graph.state.CompiledStateGraph`.
|
| `auth` | _(Added in v0.0.11)_ Auth configuration containing the path to your authentication handler. Example: `./your_package/auth.py:auth`, where `auth` is an instance of `langgraph_sdk.Auth`. See [authentication guide](../../concepts/auth.md) for details. |
+ | `base_image` | Optional. Base image to use for the LangGraph API server. Defaults to `langchain/langgraph-api` or `langchain/langgraphjs-api`. Use this to pin your builds to a particular version of the langgraph API, such as `"langchain/langgraph-server:0.2"`. See https://hub.docker.com/r/langchain/langgraph-server/tags for more details. (added in `langgraph-cli==0.2.8`) |
| `env` | Path to `.env` file or a mapping from environment variable to its value. |
| `store` | Configuration for adding semantic search and/or time-to-live (TTL) to the BaseStore. Contains the following fields: - `index` (optional): Configuration for semantic search indexing with fields `embed`, `dims`, and optional `fields`.
- `ttl` (optional): Configuration for item expiration. An object with optional fields: `refresh_on_read` (boolean, defaults to `true`), `default_ttl` (float, lifespan in **minutes**, defaults to no expiration), and `sweep_interval_minutes` (integer, how often to check for expired items, defaults to no sweeping).
|
+ | `ui` | Optional. Named definitions of UI components emitted by the agent, each pointing to a JS/TS file. (added in `langgraph-cli==0.1.84`) |
| `python_version` | `3.11`, `3.12`, or `3.13`. Defaults to `3.11`. |
| `node_version` | Specify `node_version: 20` to use LangGraph.js. |
| `pip_config_file` | Path to `pip` config file. |
diff --git a/libs/cli/langgraph_cli/cli.py b/libs/cli/langgraph_cli/cli.py
index e2953b1ee..a2f98a02a 100644
--- a/libs/cli/langgraph_cli/cli.py
+++ b/libs/cli/langgraph_cli/cli.py
@@ -438,8 +438,17 @@ tests
),
is_flag=True,
)
+@click.option(
+ "--base-image",
+ help="Base image to use for the LangGraph API server. Defaults to langchain/langgraph-api or langchain/langgraphjs-api",
+)
@log_command
-def dockerfile(save_path: str, config: pathlib.Path, add_docker_compose: bool) -> None:
+def dockerfile(
+ save_path: str,
+ config: pathlib.Path,
+ add_docker_compose: bool,
+ base_image: Optional[str] = None,
+) -> None:
save_path = pathlib.Path(save_path).absolute()
secho(f"🔍 Validating configuration at path: {config}", fg="yellow")
config_json = langgraph_cli.config.validate_config_file(config)
@@ -449,7 +458,7 @@ def dockerfile(save_path: str, config: pathlib.Path, add_docker_compose: bool) -
dockerfile, additional_contexts = langgraph_cli.config.config_to_docker(
config,
config_json,
- None,
+ base_image=base_image,
)
with open(str(save_path), "w", encoding="utf-8") as f:
f.write(dockerfile)
@@ -709,7 +718,10 @@ def prepare_args_and_stdin(
debugger_port: Optional[int] = None,
debugger_base_url: Optional[str] = None,
postgres_uri: Optional[str] = None,
+ # Like "my-tag" (if you already built it locally)
image: Optional[str] = None,
+ # Like "langchain/langgraphjs-api" or "langchain/langgraph-api
+ base_image: Optional[str] = None,
) -> Tuple[List[str], str]:
assert config_path.exists(), f"Config file not found: {config_path}"
# prepare args
diff --git a/libs/cli/langgraph_cli/config.py b/libs/cli/langgraph_cli/config.py
index 3ebe3fda4..5ef67b673 100644
--- a/libs/cli/langgraph_cli/config.py
+++ b/libs/cli/langgraph_cli/config.py
@@ -362,6 +362,11 @@ class Config(TypedDict, total=False):
"""Optional. Internal use only.
"""
+ base_image: Optional[str]
+ """Optional. Base image to use for the LangGraph API server.
+
+ Defaults to langchain/langgraph-api or langchain/langgraphjs-api."""
+
pip_config_file: Optional[str]
"""Optional. Path to a pip config file (e.g., "/etc/pip.conf" or "pip.ini") for controlling
package installation (custom indices, credentials, etc.).
@@ -517,6 +522,7 @@ def validate_config(config: Config) -> Config:
"python_version": python_version,
"pip_config_file": config.get("pip_config_file"),
"_INTERNAL_docker_tag": config.get("_INTERNAL_docker_tag"),
+ "base_image": config.get("base_image"),
"dependencies": config.get("dependencies", []),
"dockerfile_lines": config.get("dockerfile_lines", []),
"graphs": config.get("graphs", {}),
@@ -1199,9 +1205,12 @@ ADD {relpath} /deps/{name}
"# -- End of JS dependencies install --",
]
)
-
+ if "/langgraph-server" in base_image:
+ image_str = f"{base_image}-py{docker_tag}"
+ else:
+ image_str = f"{base_image}:{docker_tag}"
docker_file_contents = [
- f"FROM {base_image}:{docker_tag}",
+ f"FROM {image_str}",
"",
os.linesep.join(config["dockerfile_lines"]),
"",
@@ -1285,6 +1294,8 @@ def node_config_to_docker(
def default_base_image(config: Config) -> str:
+ if config.get("base_image"):
+ return config["base_image"]
if config.get("node_version") and not config.get("python_version"):
return "langchain/langgraphjs-api"
return "langchain/langgraph-api"
@@ -1298,6 +1309,9 @@ def docker_tag(
if config.get("_INTERNAL_docker_tag"):
return f"{base_image}:{config['_INTERNAL_docker_tag']}"
+ if "/langgraph-server" in base_image:
+ return f"{base_image}-py{config['python_version']}"
+
if config.get("node_version") and not config.get("python_version"):
return f"{base_image}:{config['node_version']}"
return f"{base_image}:{config['python_version']}"
diff --git a/libs/cli/pyproject.toml b/libs/cli/pyproject.toml
index 320dcd56b..c5c9f6f65 100644
--- a/libs/cli/pyproject.toml
+++ b/libs/cli/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "langgraph-cli"
-version = "0.2.7"
+version = "0.2.8"
description = "CLI for interacting with LangGraph API"
authors = []
license = "MIT"
diff --git a/libs/cli/schemas/schema.json b/libs/cli/schemas/schema.json
index 1421e249e..a8972fb0c 100644
--- a/libs/cli/schemas/schema.json
+++ b/libs/cli/schemas/schema.json
@@ -51,6 +51,17 @@
],
"description": "Optional. Custom authentication config, including the path to your Python auth logic and\nthe OpenAPI security definitions it uses.\n"
},
+ "base_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
+ },
"checkpointer": {
"anyOf": [
{
@@ -178,6 +189,17 @@
],
"description": "Optional. Custom authentication config, including the path to your Python auth logic and\nthe OpenAPI security definitions it uses.\n"
},
+ "base_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
+ },
"checkpointer": {
"anyOf": [
{
diff --git a/libs/cli/schemas/schema.v0.json b/libs/cli/schemas/schema.v0.json
index 1421e249e..a8972fb0c 100644
--- a/libs/cli/schemas/schema.v0.json
+++ b/libs/cli/schemas/schema.v0.json
@@ -51,6 +51,17 @@
],
"description": "Optional. Custom authentication config, including the path to your Python auth logic and\nthe OpenAPI security definitions it uses.\n"
},
+ "base_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
+ },
"checkpointer": {
"anyOf": [
{
@@ -178,6 +189,17 @@
],
"description": "Optional. Custom authentication config, including the path to your Python auth logic and\nthe OpenAPI security definitions it uses.\n"
},
+ "base_image": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Optional. Base image to use for the LangGraph API server.\n\nDefaults to langchain/langgraph-api or langchain/langgraphjs-api.\n"
+ },
"checkpointer": {
"anyOf": [
{
diff --git a/libs/cli/tests/unit_tests/cli/test_cli.py b/libs/cli/tests/unit_tests/cli/test_cli.py
index e81b9e291..02eec1560 100644
--- a/libs/cli/tests/unit_tests/cli/test_cli.py
+++ b/libs/cli/tests/unit_tests/cli/test_cli.py
@@ -1,5 +1,6 @@
import json
import pathlib
+import re
import shutil
import tempfile
import textwrap
@@ -347,6 +348,35 @@ def test_dockerfile_command_new_style_config() -> None:
assert save_path.exists()
+def test_dockerfile_command_with_base_image() -> None:
+ """Test the 'dockerfile' command with a base image."""
+ runner = CliRunner()
+ config_content = {
+ "python_version": "3.11",
+ "graphs": {"agent": "agent.py:graph"},
+ "dependencies": ["."],
+ "base_image": "langchain/langgraph-server:0.2",
+ }
+ with temporary_config_folder(config_content) as temp_dir:
+ save_path = temp_dir / "Dockerfile"
+ agent_path = temp_dir / "agent.py"
+ agent_path.parent.mkdir(parents=True, exist_ok=True)
+ agent_path.touch()
+
+ result = runner.invoke(
+ cli,
+ ["dockerfile", str(save_path), "--config", str(temp_dir / "config.json")],
+ )
+
+ assert result.exit_code == 0, result.output
+ assert "✅ Created: Dockerfile" in result.output
+
+ assert save_path.exists()
+ with open(save_path) as f:
+ dockerfile = f.read()
+ assert re.match("FROM langchain/langgraph-server:0.2-py3.*", dockerfile)
+
+
def test_dockerfile_command_with_docker_compose() -> None:
"""Test the 'dockerfile' command with Docker Compose configuration."""
runner = CliRunner()
diff --git a/libs/cli/tests/unit_tests/test_config.py b/libs/cli/tests/unit_tests/test_config.py
index cb25eee30..ca29a9876 100644
--- a/libs/cli/tests/unit_tests/test_config.py
+++ b/libs/cli/tests/unit_tests/test_config.py
@@ -30,6 +30,7 @@ def test_validate_config():
actual_config = validate_config(expected_config)
expected_config = {
"_INTERNAL_docker_tag": None,
+ "base_image": None,
"python_version": "3.11",
"node_version": None,
"pip_config_file": None,
@@ -49,6 +50,7 @@ def test_validate_config():
env = ".env"
expected_config = {
"_INTERNAL_docker_tag": None,
+ "base_image": None,
"python_version": "3.12",
"node_version": None,
"pip_config_file": "pipconfig.txt",