From d452c2e413e3832f7bd9fd57435df3cdd4a50656 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 10 Dec 2025 12:09:38 +0100 Subject: [PATCH 1/2] Do away with Docker-specific case distinctions; instead, configure Docker properly --- DOCKER.md | 66 +++++++++---------- Dockerfile | 28 +++----- compose.yaml | 23 +------ docs/02-usage/020_running.md | 26 ++++---- src/serena/agent.py | 12 ++-- src/serena/config/serena_config.py | 48 ++++---------- src/serena/dashboard.py | 1 + .../resources/serena_config.template.yml | 6 +- 8 files changed, 75 insertions(+), 135 deletions(-) diff --git a/DOCKER.md b/DOCKER.md index 03432451..bb77040c 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -1,6 +1,6 @@ # Docker Setup for Serena (Experimental) -⚠️ **EXPERIMENTAL FEATURE**: The Docker setup for Serena is currently experimental and has several limitations. Please read this entire document before using Docker with Serena. +⚠️ **EXPERIMENTAL FEATURE**: The Docker setup for Serena is still experimental and has some limitations. Please read this entire document before using Docker with Serena. ## Overview @@ -12,27 +12,42 @@ Docker support allows you to run Serena in an isolated container environment, wh - **Consistent dependencies**: No need to manage language servers and dependencies on your host system - **Cross-platform support**: Works consistently across Windows, macOS, and Linux -## Important Limitations and Caveats +## Important Usage Pointers -### 1. Configuration File Conflicts +### Configuration -⚠️ **Critical**: Docker uses a separate configuration file (`serena_config.docker.yml`) to avoid path conflicts. When running in Docker: -- Container paths will be stored in the configuration (e.g., `/workspaces/serena/...`) -- These paths are incompatible with non-Docker usage -- After using Docker, you cannot directly switch back to non-Docker usage without manual configuration adjustment +Serena's configuration and log files are stored in the container in `/workspaces/serena/config/`. +Any local configuration you may have for Serena will not apply; the container uses its own separate configuration. -### 2. Project Activation Limitations +You can mount a local configuration/data directory to persist settings across container restarts +(which will also contain session log files). +Simply mount your local directory to `/workspaces/serena/config` in the container. +Initially, be sure to add a `serena_config.yml` file to the mounted directory which applies the following +special settings for Docker usage: +``` +# Disable the GUI log window since it's not supported in Docker +gui_log_window: False +# Listen on all interfaces for the web dashboard to be accessible from outside the container +web_dashboard_listen_address: 0.0.0.0 +# Disable opening the web dashboard on launch (not possible within the container) +web_dashboard_open_on_launch: False +``` +Set other configuration options as needed. + +### Project Activation Limitations - **Only mounted directories work**: Projects must be mounted as volumes to be accessible - Projects outside the mounted directories cannot be activated or accessed -- Default setup only mounts the current directory +- Since projects are not remembered across container restarts (unless you mount a local configuration as described above), + activate them using the full path (e.g. `/workspaces/projects/my-project`) when using dynamic project activation -### 3. GUI Window Disabled +### Language Support Limitations -- The GUI log window option is automatically disabled in Docker environments -- Use the web dashboard instead (see below) +The default Docker image does not include dependencies for languages that +require explicit system-level installations. +Only languages that install their requirements on the fly will work out of the box. -### 4. Dashboard Port Configuration +### Dashboard Port Configuration The web dashboard runs on port 24282 (0x5EDA) by default. You can configure this using environment variables: @@ -46,7 +61,7 @@ SERENA_DASHBOARD_PORT=8080 docker-compose up serena ⚠️ **Note**: If the local port is occupied, you'll need to specify a different port using the environment variable. -### 5. Line Ending Issues on Windows +### Line Ending Issues on Windows ⚠️ **Windows Users**: Be aware of potential line ending inconsistencies: - Files edited within the Docker container may use Unix line endings (LF) @@ -70,7 +85,7 @@ SERENA_DASHBOARD_PORT=8080 docker-compose up serena Note: Edit the `compose.yaml` file to customize volume mounts for your projects. -### Using Docker directly +### Building the Docker Image Manually ```bash # Build the image @@ -157,24 +172,3 @@ Ensure projects are properly mounted: - Check volume mounts in `docker-compose.yaml` - Use absolute paths for external projects - Verify permissions on mounted directories - -## Migration Path - -To switch between Docker and non-Docker usage: - -1. **Docker to Non-Docker**: - - Manually edit project paths in `serena_config.yml` - - Change container paths to host paths - - Or use separate config files for each environment - -2. **Non-Docker to Docker**: - - Projects will be re-registered with container paths - - Original config remains unchanged - -## Future Improvements - -We're working on: -- Automatic config migration between environments -- Better project path handling -- Dynamic port allocation -- Windows line-ending handling. diff --git a/Dockerfile b/Dockerfile index d84ed7d0..1dccaf40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -53,28 +53,21 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ # Set the working directory WORKDIR /workspaces/serena -# Development target -FROM base AS development # Copy all files for development COPY . /workspaces/serena/ -# Create virtual environment and install dependencies with dev extras -RUN uv venv -RUN . .venv/bin/activate -RUN uv pip install --all-extras -r pyproject.toml -e . -ENV PATH="/workspaces/serena/.venv/bin:${PATH}" +# Install sed +RUN apt-get update && apt-get install -y sed -# Entrypoint to ensure environment is activated -ENTRYPOINT ["/bin/bash", "-c", "source .venv/bin/activate && $0 $@"] +# Create Serena configuration +ENV SERENA_HOME=/workspaces/serena/config +RUN mkdir -p $SERENA_HOME +RUN cp src/serena/resources/serena_config.template.yml $SERENA_HOME/serena_config.yml +RUN sed -i 's/^gui_log_window: .*/gui_log_window: False/' $SERENA_HOME/serena_config.yml +RUN sed -i 's/^web_dashboard_listen_address: .*/web_dashboard_listen_address: 0.0.0.0/' $SERENA_HOME/serena_config.yml +RUN sed -i 's/^web_dashboard_open_on_launch: .*/web_dashboard_open_on_launch: False/' $SERENA_HOME/serena_config.yml -# Production target -FROM base AS production -# Copy only necessary files for production -COPY pyproject.toml /workspaces/serena/ -COPY README.md /workspaces/serena/ -COPY src/ /workspaces/serena/src/ - -# Create virtual environment and install dependencies (production only) +# Create virtual environment and install dependencies RUN uv venv RUN . .venv/bin/activate RUN uv pip install -r pyproject.toml -e . @@ -82,4 +75,3 @@ ENV PATH="/workspaces/serena/.venv/bin:${PATH}" # Entrypoint to ensure environment is activated ENTRYPOINT ["/bin/bash", "-c", "source .venv/bin/activate && $0 $@"] - diff --git a/compose.yaml b/compose.yaml index 48923e27..d7c8becd 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,7 +1,7 @@ services: serena: image: serena:latest - # To work with projects, you must mount them as volumes: + # To work with projects, you must mount them into /workspace/ in the container: # volumes: # - ./my-project:/workspace/my-project # - /path/to/another/project:/workspace/another-project @@ -16,23 +16,6 @@ services: - SERENA_DOCKER=1 command: - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0" - # Add the context for the IDE assistant - # - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0 --context ide-assistant" + # Alternatively add further arguments, e.g. a context + # - "uv run --directory . serena-mcp-server --transport sse --port 9121 --host 0.0.0.0 --context ide" - serena-dev: - image: serena:dev - build: - context: ./ - dockerfile: Dockerfile - target: development - tty: true - stdin_open: true - environment: - - SERENA_DOCKER=1 - volumes: - - .:/workspaces/serena - ports: - - "${SERENA_PORT:-9121}:9121" # MCP server port - - "${SERENA_DASHBOARD_PORT:-24282}:24282" # Dashboard port - command: - - "uv run --directory . serena-mcp-server" diff --git a/docs/02-usage/020_running.md b/docs/02-usage/020_running.md index ede78ec2..231dd70c 100644 --- a/docs/02-usage/020_running.md +++ b/docs/02-usage/020_running.md @@ -47,28 +47,30 @@ As a consequence, you will need to specify paths when using CLI commands that wo ::: (docker)= -### Using Docker (Experimental) +### Using Docker -:::{warning} -Docker support is currently experimental with several limitations. -::: +The Docker approach offers several advantages: -You can run the Serena MCP server directly via docker as follows, +* better security isolation for shell command execution +* no need to install language servers and dependencies locally +* consistent environment across different systems + +You can run the Serena MCP server directly via Docker as follows, assuming that the projects you want to work on are all located in `/path/to/your/projects`: ```shell docker run --rm -i --network host -v /path/to/your/projects:/workspaces/projects ghcr.io/oraios/serena:latest serena ``` -Replace `/path/to/your/projects` with the absolute path to your projects directory. The Docker approach provides: +This command mounts your projects into the container under `/workspaces/projects`, so when working with projects, +you need to refer to them using the respective path (e.g. `/workspaces/projects/my-project`). -* Better security isolation for shell command execution -* No need to install language servers and dependencies locally -* Consistent environment across different systems +Alternatively, you may use Docker compose with the `compose.yml` file provided in the repository. +See our [advanced Docker usage](https://github.com/oraios/serena/blob/main/DOCKER.md) documentation for more detailed instructions, configuration options, and limitations. -Alternatively, use docker compose with the `compose.yml` file provided in the repository. - -See our [Docker Setup](https://github.com/oraios/serena/blob/main/DOCKER.md) documentation for more detailed setup instructions, configuration options, and known limitations. +:::{note} +Docker usage is subject to limitations; see the [advanced Docker usage](https://github.com/oraios/serena/blob/main/DOCKER.md) documentation for details. +::: ### Using Nix diff --git a/src/serena/agent.py b/src/serena/agent.py index 8b198c5a..394c14bb 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -18,7 +18,7 @@ from interprompt.jinja_template import JinjaTemplate from serena import serena_version from serena.analytics import RegisteredTokenCountEstimator, ToolUsageStats from serena.config.context_mode import SerenaAgentContext, SerenaAgentMode -from serena.config.serena_config import SerenaConfig, ToolInclusionDefinition, is_running_in_docker +from serena.config.serena_config import SerenaConfig, ToolInclusionDefinition from serena.dashboard import SerenaDashboardAPI from serena.ls_manager import LanguageServerManager from serena.project import Project @@ -272,12 +272,10 @@ class SerenaAgent: self._dashboard_thread, port = SerenaDashboardAPI( get_memory_log_handler(), tool_names, agent=self, tool_usage_stats=self._tool_usage_stats ).run_in_thread(host=self.serena_config.web_dashboard_listen_address) - dashboard_listen_address = self.serena_config.web_dashboard_listen_address - if is_running_in_docker(): - # we use localhost for URL instead of the 0.0.0.0 that is configured for docker - # (the user has to port-forward then) - dashboard_listen_address = "127.0.0.1" - dashboard_url = f"http://{dashboard_listen_address}:{port}/dashboard/index.html" + dashboard_host = self.serena_config.web_dashboard_listen_address + if dashboard_host == "0.0.0.0": + dashboard_host = "localhost" + dashboard_url = f"http://{dashboard_host}:{port}/dashboard/index.html" log.info("Serena web dashboard started at %s", dashboard_url) if self.serena_config.web_dashboard_open_on_launch: # open the dashboard URL in the default web browser (using a separate process to control diff --git a/src/serena/config/serena_config.py b/src/serena/config/serena_config.py index 83d6bcb4..75f916b8 100644 --- a/src/serena/config/serena_config.py +++ b/src/serena/config/serena_config.py @@ -106,19 +106,6 @@ def get_serena_managed_in_project_dir(project_root: str | Path) -> str: return os.path.join(project_root, SERENA_MANAGED_DIR_NAME) -def is_running_in_docker() -> bool: - """Check if we're running inside a Docker container.""" - # Check for Docker-specific files - if os.path.exists("/.dockerenv"): - return True - # Check cgroup for docker references - try: - with open("/proc/self/cgroup") as f: - return "docker" in f.read() - except FileNotFoundError: - return False - - @dataclass(kw_only=True) class ProjectConfig(ToolInclusionDefinition, ToStringMixin): project_name: str @@ -368,7 +355,6 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin): web_dashboard: bool = True web_dashboard_open_on_launch: bool = True web_dashboard_listen_address: str = "127.0.0.1" - """When running in Docker, this will be automatically set to '0.0.0.0' ignoring any configuration by the user.""" tool_timeout: float = DEFAULT_TOOL_TIMEOUT loaded_commented_yaml: CommentedMap | None = None config_file_path: str | None = None @@ -398,11 +384,6 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin): """Advanced configuration option allowing to configure language server implementation specific options, see SolidLSPSettings for more info.""" CONFIG_FILE = "serena_config.yml" - CONFIG_FILE_DOCKER = "serena_config.docker.yml" # Docker-specific config file; auto-generated if missing, mounted via docker-compose for user customization - - def __post_init__(self) -> None: - if is_running_in_docker(): - self.web_dashboard_listen_address = "0.0.0.0" def _tostring_includes(self) -> list[str]: return ["config_file_path"] @@ -423,20 +404,17 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin): """ :return: the location where the Serena configuration file is stored/should be stored """ - if is_running_in_docker(): - return os.path.join(REPO_ROOT, cls.CONFIG_FILE_DOCKER) - else: - config_path = os.path.join(SerenaPaths().serena_user_home_dir, cls.CONFIG_FILE) + config_path = os.path.join(SerenaPaths().serena_user_home_dir, cls.CONFIG_FILE) - # if the config file does not exist, check if we can migrate it from the old location - if not os.path.exists(config_path): - old_config_path = os.path.join(REPO_ROOT, cls.CONFIG_FILE) - if os.path.exists(old_config_path): - log.info(f"Moving Serena configuration file from {old_config_path} to {config_path}") - os.makedirs(os.path.dirname(config_path), exist_ok=True) - shutil.move(old_config_path, config_path) + # if the config file does not exist, check if we can migrate it from the old location + if not os.path.exists(config_path): + old_config_path = os.path.join(REPO_ROOT, cls.CONFIG_FILE) + if os.path.exists(old_config_path): + log.info(f"Moving Serena configuration file from {old_config_path} to {config_path}") + os.makedirs(os.path.dirname(config_path), exist_ok=True) + shutil.move(old_config_path, config_path) - return config_path + return config_path @classmethod def from_config_file(cls, generate_if_missing: bool = True) -> "SerenaConfig": @@ -490,12 +468,8 @@ class SerenaConfig(ToolInclusionDefinition, ToStringMixin): def get_value_or_default(field_name: str) -> Any: return loaded_commented_yaml.get(field_name, get_dataclass_default(SerenaConfig, field_name)) - if is_running_in_docker(): - instance.gui_log_window_enabled = False # not supported in Docker - else: - instance.gui_log_window_enabled = get_value_or_default("gui_log_window_enabled") - # in docker the post_init takes care of setting the address to 0.0.0.0, we ignore the user's setting - instance.web_dashboard_listen_address = get_value_or_default("web_dashboard_listen_address") + instance.gui_log_window_enabled = get_value_or_default("gui_log_window_enabled") + instance.web_dashboard_listen_address = get_value_or_default("web_dashboard_listen_address") instance.log_level = loaded_commented_yaml.get("log_level", loaded_commented_yaml.get("gui_log_level", logging.INFO)) instance.web_dashboard = get_value_or_default("web_dashboard") instance.web_dashboard_open_on_launch = get_value_or_default("web_dashboard_open_on_launch") diff --git a/src/serena/dashboard.py b/src/serena/dashboard.py index 5b414e2d..9002c3bf 100644 --- a/src/serena/dashboard.py +++ b/src/serena/dashboard.py @@ -574,6 +574,7 @@ class SerenaDashboardAPI: def run_in_thread(self, host: str) -> tuple[threading.Thread, int]: port = self._find_first_free_port(0x5EDA) + log.info("Starting dashboard (listen_address=%s, port=%d)", host, port) thread = threading.Thread(target=lambda: self.run(host=host, port=port), daemon=True) thread.start() return thread, port diff --git a/src/serena/resources/serena_config.template.yml b/src/serena/resources/serena_config.template.yml index 75aa5cff..974fe7e5 100644 --- a/src/serena/resources/serena_config.template.yml +++ b/src/serena/resources/serena_config.template.yml @@ -21,10 +21,7 @@ web_dashboard: True # is supported on all platforms. web_dashboard_listen_address: 127.0.0.1 -# The address where the web dashboard will listen on (by default, localhost) -# Important: when running serena in docker, this configuration entry will be ignored and the -# address will be set to 0.0.0.0. (as it's running in a sandbox, this does not pose a safety issue) -# You can then port-forward it to a host of your choice, e.g., localhost +# The address where the web dashboard will listen on web_dashboard_open_on_launch: True # whether to open a browser window with the web dashboard when Serena starts (provided that web_dashboard @@ -32,7 +29,6 @@ web_dashboard_open_on_launch: True # http://localhost:24282/dashboard/ in your web browser (24282 = 0x5EDA, SErena DAshboard). # If you have multiple instances running, a higher port will be used; try port 24283, 24284, etc. - log_level: 20 # the minimum log level for the GUI log window and the dashboard (10 = debug, 20 = info, 30 = warning, 40 = error) From 7ad44edd18fe9e28d0c77c3e24893f0248a792ad Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Thu, 11 Dec 2025 00:06:46 +0100 Subject: [PATCH 2/2] Do not use listen_address 0.0.0.0 for Docker (has no desirable effect) --- DOCKER.md | 2 -- Dockerfile | 1 - 2 files changed, 3 deletions(-) diff --git a/DOCKER.md b/DOCKER.md index bb77040c..08348182 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -27,8 +27,6 @@ special settings for Docker usage: ``` # Disable the GUI log window since it's not supported in Docker gui_log_window: False -# Listen on all interfaces for the web dashboard to be accessible from outside the container -web_dashboard_listen_address: 0.0.0.0 # Disable opening the web dashboard on launch (not possible within the container) web_dashboard_open_on_launch: False ``` diff --git a/Dockerfile b/Dockerfile index 1dccaf40..e58d821b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,7 +64,6 @@ ENV SERENA_HOME=/workspaces/serena/config RUN mkdir -p $SERENA_HOME RUN cp src/serena/resources/serena_config.template.yml $SERENA_HOME/serena_config.yml RUN sed -i 's/^gui_log_window: .*/gui_log_window: False/' $SERENA_HOME/serena_config.yml -RUN sed -i 's/^web_dashboard_listen_address: .*/web_dashboard_listen_address: 0.0.0.0/' $SERENA_HOME/serena_config.yml RUN sed -i 's/^web_dashboard_open_on_launch: .*/web_dashboard_open_on_launch: False/' $SERENA_HOME/serena_config.yml # Create virtual environment and install dependencies