Merge pull request #830 from oraios/improve-docker-config

Improve docker configuration
This commit is contained in:
Dominik Jain authored and GitHub committed 2025-12-11 12:14:03 +01:00
commit 61c4b5ea5b
8 files changed
+72 -135

No files matched your search

+28 -36
View File
@@ -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,40 @@ 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
# 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 +59,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 +83,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 +170,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.
+9 -18
View File
@@ -53,28 +53,20 @@ 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_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 +74,3 @@ ENV PATH="/workspaces/serena/.venv/bin:${PATH}"
# Entrypoint to ensure environment is activated
ENTRYPOINT ["/bin/bash", "-c", "source .venv/bin/activate && $0 $@"]
+3 -20
View File
@@ -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"
+14 -12
View File
@@ -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
+5 -7
View File
@@ -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
+11 -37
View File
@@ -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")
+1
View File
@@ -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
@@ -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)