Skip to content

Commit 2289578

Browse files
committed
fix: deployment and Chrome path issues
- Add CHROME_PATH to Dockerfile to override user env var - Fix VNC password location in Dockerfile for proper connection - Update env_example CHROME_PATH handling: -- Exclude from browser_config if empty -- Override Chromium location if set - Add Playwright version condition in pyproject - Fix browser context in server: - Avoid loading Chromium via CLI instead of .app - Use CHROME_PATH if set, otherwise fallback to default
1 parent b93ceb9 commit 2289578

File tree

5 files changed

+39
-114
lines changed

5 files changed

+39
-114
lines changed

.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# Path to Chrome/Chromium executable
2-
/usr/bin/chromium
1+
# Path to Chrome/Chromium executable leave blank to use default playwright chromium
2+
CHROME_PATH=
33

44
# OpenAI API key for OpenAI model access
55
OPENAI_API_KEY=your-api-key-here

Dockerfile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
FROM ghcr.io/astral-sh/uv:bookworm-slim AS builder
22

3-
ARG VNC_PASSWORD=browser-use
4-
53
ENV UV_COMPILE_BYTECODE=1 \
64
UV_LINK_MODE=copy \
75
UV_PYTHON_INSTALL_DIR=/python \
@@ -26,6 +24,8 @@ RUN --mount=type=cache,target=/root/.cache/uv \
2624

2725
FROM debian:bookworm-slim AS runtime
2826

27+
ARG VNC_PASSWORD="browser-use"
28+
2929
# Install required packages including Chromium and clean up in the same layer
3030
RUN apt-get update && \
3131
apt-get install --no-install-recommends -y \
@@ -56,11 +56,12 @@ COPY --from=builder --chown=app:app /app /app
5656
ENV PATH="/app/.venv/bin:$PATH" \
5757
DISPLAY=:0 \
5858
CHROME_BIN=/usr/bin/chromium \
59-
CHROMIUM_FLAGS="--no-sandbox --headless --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage"
59+
CHROMIUM_FLAGS="--no-sandbox --headless --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage" \
60+
CHROME_PATH="/usr/bin/chromium"
6061

6162
# Combine VNC setup commands to reduce layers
6263
RUN mkdir -p ~/.vnc && \
63-
echo ${VNC_PASSWORD} | vncpasswd -f > /root/.vnc/passwd && \
64+
echo $VNC_PASSWORD | vncpasswd -f > /root/.vnc/passwd && \
6465
chmod 600 /root/.vnc/passwd && \
6566
printf '#!/bin/sh\nunset SESSION_MANAGER\nunset DBUS_SESSION_BUS_ADDRESS\nstartxfce4' > /root/.vnc/xstartup && \
6667
chmod +x /root/.vnc/xstartup && \

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies = [
2727
"python-dotenv",
2828
"starlette",
2929
"uvicorn",
30+
"playwright>=1.50.0",
3031
]
3132

3233
[project.optional-dependencies]

server/server.py

Lines changed: 29 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
# Import from the browser-use-mcp-server package
1010
from browser_use_mcp_server.server import (
11-
initialize_browser_context,
1211
create_mcp_server,
1312
)
1413
from langchain_openai import ChatOpenAI
@@ -64,97 +63,21 @@ def main(
6463
task_expiry_minutes: int,
6564
) -> int:
6665
"""Run the browser-use MCP server."""
67-
# If Chrome path is explicitly provided, use it
68-
if chrome_path and os.path.exists(chrome_path):
69-
chrome_executable_path = chrome_path
70-
logger.info(f"Using explicitly provided Chrome path: {chrome_executable_path}")
66+
# Use Chrome path from command line arg, environment variable, or None
67+
chrome_executable_path = chrome_path or os.environ.get("CHROME_PATH")
68+
if chrome_executable_path:
69+
logger.info(f"Using Chrome path: {chrome_executable_path}")
7170
else:
72-
# Try to find Playwright's installed Chromium first
73-
import os.path
74-
import platform
75-
from pathlib import Path
76-
import subprocess
77-
78-
# Try to get Playwright browsers directory
79-
home_dir = str(Path.home())
80-
system = platform.system()
81-
82-
if system == "Darwin": # macOS
83-
playwright_browsers_path = os.path.join(home_dir, "Library", "Caches", "ms-playwright")
84-
elif system == "Linux":
85-
playwright_browsers_path = os.path.join(home_dir, ".cache", "ms-playwright")
86-
elif system == "Windows":
87-
playwright_browsers_path = os.path.join(home_dir, "AppData", "Local", "ms-playwright")
88-
else:
89-
playwright_browsers_path = None
90-
91-
# Try to find the Chromium executable in the Playwright directory
92-
chromium_executable_path = None
93-
if playwright_browsers_path and os.path.exists(playwright_browsers_path):
94-
logger.info(f"Found Playwright browsers directory at {playwright_browsers_path}")
95-
# Look for chromium directories
96-
try:
97-
for root, dirs, files in os.walk(playwright_browsers_path):
98-
for dir in dirs:
99-
if "chromium" in dir.lower():
100-
# Check for executable in this directory
101-
if system == "Darwin": # macOS
102-
exec_path = os.path.join(root, dir, "chrome-mac", "Chromium.app", "Contents", "MacOS", "Chromium")
103-
elif system == "Linux":
104-
exec_path = os.path.join(root, dir, "chrome-linux", "chrome")
105-
elif system == "Windows":
106-
exec_path = os.path.join(root, dir, "chrome-win", "chrome.exe")
107-
else:
108-
continue
109-
110-
if os.path.exists(exec_path):
111-
chromium_executable_path = exec_path
112-
logger.info(f"Found Playwright Chromium at {chromium_executable_path}")
113-
break
114-
if chromium_executable_path:
115-
break
116-
except Exception as e:
117-
logger.warning(f"Error searching for Playwright Chromium: {str(e)}")
118-
119-
# If Playwright Chromium not found, try standard locations
120-
if not chromium_executable_path:
121-
# Try to find Chromium/Chrome in common locations
122-
potential_paths = [
123-
# Environment variable
124-
os.environ.get("CHROME_PATH"),
125-
126-
# Common macOS paths
127-
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
128-
"/Applications/Chromium.app/Contents/MacOS/Chromium",
129-
130-
# Common Linux paths
131-
"/usr/bin/chromium",
132-
"/usr/bin/chromium-browser",
133-
"/usr/bin/google-chrome",
134-
"/usr/bin/google-chrome-stable",
135-
]
136-
137-
for path in potential_paths:
138-
if path and os.path.exists(path):
139-
logger.info(f"Found browser at {path}")
140-
chromium_executable_path = path
141-
break
142-
143-
# Use the found path or try with Playwright's default
144-
if chromium_executable_path:
145-
chrome_executable_path = chromium_executable_path
146-
logger.info(f"Using browser executable at: {chrome_executable_path}")
147-
else:
148-
# If no specific path found, let Playwright find its own browser
149-
logger.warning("No Chrome/Chromium path found, will let Playwright use its default browser")
150-
chrome_executable_path = None
151-
71+
logger.info(
72+
"No Chrome path specified, letting Playwright use its default browser"
73+
)
74+
15275
# Initialize browser context
15376
try:
15477
# Using the approach from backup/server.py
15578
from browser_use.browser.context import BrowserContextConfig, BrowserContext
15679
from browser_use.browser.browser import Browser, BrowserConfig
157-
80+
15881
# Browser context configuration
15982
config = BrowserContextConfig(
16083
wait_for_network_idle_page_load_time=0.6,
@@ -166,7 +89,7 @@ def main(
16689
highlight_elements=True,
16790
viewport_expansion=0,
16891
)
169-
92+
17093
# Initialize browser and context directly
17194
browser_config = BrowserConfig(
17295
extra_chromium_args=[
@@ -177,36 +100,36 @@ def main(
177100
"--remote-debugging-port=9222",
178101
],
179102
)
180-
181-
# Only set chrome_instance_path if we actually found a path
103+
104+
# Only set chrome_instance_path if we actually set a path in the env file
182105
if chrome_executable_path:
183106
browser_config.chrome_instance_path = chrome_executable_path
184-
107+
185108
browser = Browser(config=browser_config)
186109
context = BrowserContext(browser=browser, config=config)
187110
logger.info("Browser context initialized successfully")
188111
except Exception as e:
189112
logger.error(f"Failed to initialize browser context: {str(e)}")
190113
return 1
191-
114+
192115
# Initialize LLM
193116
llm = ChatOpenAI(model="gpt-4o", temperature=0.0)
194-
117+
195118
# Create MCP server
196119
app = create_mcp_server(
197120
context=context,
198121
llm=llm,
199122
task_expiry_minutes=task_expiry_minutes,
200123
)
201-
124+
202125
if transport == "sse":
203126
from mcp.server.sse import SseServerTransport
204127
from starlette.applications import Starlette
205128
from starlette.routing import Mount, Route
206129
import uvicorn
207-
130+
208131
sse = SseServerTransport("/messages/")
209-
132+
210133
async def handle_sse(request):
211134
try:
212135
async with sse.connect_sse(
@@ -218,25 +141,25 @@ async def handle_sse(request):
218141
except Exception as e:
219142
logger.error(f"Error in handle_sse: {str(e)}")
220143
raise
221-
144+
222145
starlette_app = Starlette(
223146
debug=True,
224147
routes=[
225148
Route("/sse", endpoint=handle_sse),
226149
Mount("/messages/", app=sse.handle_post_message),
227150
],
228151
)
229-
152+
230153
# Add a startup event to initialize the browser and start task cleanup
231154
@starlette_app.on_event("startup")
232155
async def startup_event():
233156
logger.info("Starting server and scheduling cleanup...")
234-
157+
235158
# Start the cleanup task now that we have an event loop
236-
if hasattr(app, 'cleanup_old_tasks'):
159+
if hasattr(app, "cleanup_old_tasks"):
237160
asyncio.create_task(app.cleanup_old_tasks())
238161
logger.info("Task cleanup process scheduled")
239-
162+
240163
# Add a shutdown event to clean up browser resources
241164
@starlette_app.on_event("shutdown")
242165
async def shutdown_event():
@@ -246,18 +169,18 @@ async def shutdown_event():
246169
logger.info("Browser context closed successfully")
247170
except Exception as e:
248171
logger.error(f"Error closing browser: {str(e)}")
249-
172+
250173
uvicorn.run(starlette_app, host="0.0.0.0", port=port)
251174
else:
252175
from mcp.server.stdio import stdio_server
253-
176+
254177
async def arun():
255178
try:
256179
# Start the cleanup task now that we have an event loop
257-
if hasattr(app, 'cleanup_old_tasks'):
180+
if hasattr(app, "cleanup_old_tasks"):
258181
asyncio.create_task(app.cleanup_old_tasks())
259182
logger.info("Task cleanup process scheduled")
260-
183+
261184
async with stdio_server() as streams:
262185
await app.run(
263186
streams[0], streams[1], app.create_initialization_options()
@@ -270,9 +193,9 @@ async def arun():
270193
await context.browser.close()
271194
except Exception as e:
272195
logger.error(f"Error cleaning up resources: {str(e)}")
273-
196+
274197
anyio.run(arun)
275-
198+
276199
return 0
277200

278201

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)