mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-11-02 22:16:58 +08:00
313 lines
14 KiB
Markdown
313 lines
14 KiB
Markdown
# Docker Deployment
|
|
|
|
The only recommended production deployment outside the desktop app. One `docker compose up -d` brings up three containers: PostgreSQL, SearXNG, and mateclaw-server.
|
|
|
|
> **⚠️ Upgrading from the MySQL stack**: the Docker stack switched from MySQL to PostgreSQL 16. Pulling this change and running `up -d` on an existing host starts a **fresh, empty PostgreSQL volume** — the old `mysql_data` volume is not read (data is not lost, but the new stack cannot see it). To carry data across, `mysqldump` from the old stack first and import into PostgreSQL with a cross-engine tool such as pgloader; or stay on a pre-switch tag to keep running MySQL (the `mysql` Spring profile remains supported).
|
|
|
|
This page covers **requirements, steps, verification, and common gotchas**. For the full environment variable reference, see [Configuration](./config).
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
| Item | Minimum | Recommended | Notes |
|
|
|---|---|---|---|
|
|
| Docker Engine | 24.0+ | latest stable | `docker --version` |
|
|
| Docker Compose | v2.20+ | v2.30+ | `docker compose version` (the v2 plugin, not the legacy `docker-compose`) |
|
|
| Host RAM | 4 GB | 8 GB+ | Chromium consumes 1-2 GB when the browser tool is active |
|
|
| Disk | 6 GB | 20 GB+ | ~2 GB image + PostgreSQL data + workspace files |
|
|
| /dev/shm | default | compose sets 2 GB automatically | Chromium uses shared memory for rendering; the 64 MB default causes SIGBUS |
|
|
| Network | outbound | — | for pulling images and calling LLM APIs |
|
|
|
|
**Not required on the host**: Java, Node, Maven, Chrome, or Python — all live inside the image.
|
|
|
|
---
|
|
|
|
## The three containers
|
|
|
|
| Service | Image | Role | Exposed port |
|
|
|---|---|---|---|
|
|
| `postgres` | `postgres:16` | Business data | compose network only (no host port by default) |
|
|
| `searxng` | Built from `./docker/searxng/` | Keyless search fallback | `8088` |
|
|
| `mateclaw-server` | Built from `mateclaw-server/Dockerfile` | Spring Boot backend + embedded browser | `18080` |
|
|
|
|
---
|
|
|
|
## SearXNG search service
|
|
|
|
### Why we build a custom image
|
|
|
|
`docker/searxng/Dockerfile` derives from upstream `searxng/searxng:latest` and **bakes our own `settings.yml` into `/etc/searxng/settings.yml`**. This isn't polish — it's mandatory:
|
|
|
|
- **Upstream ships with only `html` output enabled**, while mateclaw calls `GET /search?q=...&format=json`. The default image responds to JSON requests with an HTML error page, `SearXNGSearchProvider` fails to parse it, returns empty results, and the UI shows "search temporarily unavailable".
|
|
- **Upstream enables the anti-bot Limiter plugin by default**, which rejects server-side calls (no JS, no cookies) with HTTP 429.
|
|
|
|
Our `docker/searxng/settings.yml` changes three things:
|
|
|
|
1. `search.formats: [html, json]` — enable JSON output
|
|
2. `server.limiter: false` — disable anti-bot rate limiting
|
|
3. Trim the engine list to a reliable subset (DuckDuckGo / Bing / Brave / Wikipedia / Google / Startpage), dropping the dozens of niche engines the upstream enables
|
|
|
|
**Do not** switch this to a host bind-mount. An earlier version did, and deploys where the host directory didn't exist got an auto-created empty directory that shadowed the file — SearXNG started with no config at all. To tweak settings.yml, edit `docker/searxng/settings.yml` then:
|
|
|
|
```sh
|
|
docker compose build searxng
|
|
docker compose up -d searxng
|
|
```
|
|
|
|
### Search provider fallback chain
|
|
|
|
The backend `SearchProviderRegistry` picks a provider in this order:
|
|
|
|
1. Whatever the user explicitly set under `Settings → Search` (the `searchProvider` setting)
|
|
2. Walk `autoDetectOrder`, **preferring paid providers whose API key is configured** (Serper order=1, Tavily order=2)
|
|
3. Fall back to keyless — SearXNG (order=50) wins over DuckDuckGo (order=100)
|
|
|
|
On a fresh container with no API keys configured at all, **SearXNG handles every search call**.
|
|
|
|
### Verifying the SearXNG path
|
|
|
|
```sh
|
|
# 1. Hit the container directly
|
|
curl -s 'http://localhost:8088/search?q=test&format=json' | head -5
|
|
# Expect: {"query": ..., "results": [...]}
|
|
# If you get HTML back, settings.yml didn't take effect.
|
|
|
|
# 2. Hit it from inside the mateclaw-server container
|
|
docker exec mateclaw-server wget -qO- 'http://searxng:8080/search?q=test&format=json' | head -5
|
|
# If this fails, compose networking is the problem.
|
|
|
|
# 3. Ask an agent to search and tail backend logs
|
|
docker compose logs -f mateclaw-server | grep "搜索 provider"
|
|
# Expect: 搜索 provider 解析: searxng (source=keyless-fallback)
|
|
```
|
|
|
|
### Using an external SearXNG instance
|
|
|
|
If you're already running SearXNG elsewhere, point mateclaw at it via `.env`:
|
|
|
|
```properties
|
|
SEARXNG_BASE_URL=https://your-searxng.example.com
|
|
```
|
|
|
|
Then comment out the `searxng` service block in `docker-compose.yml`. Make sure **your external instance has the same JSON + Limiter settings** — otherwise you'll hit the same silent failure mode.
|
|
|
|
---
|
|
|
|
## Browser automation
|
|
|
|
### What the image actually contains
|
|
|
|
The backend runtime stage (`mateclaw-server/Dockerfile` stage 3) is based on `mcr.microsoft.com/playwright:v1.62.0-noble` (Ubuntu Noble 24.04, glibc) and installs on top of it:
|
|
|
|
- `openjdk-21-jre-headless` — runs the Spring Boot JAR
|
|
- `fonts-noto-cjk` — Chinese/Japanese/Korean rendering in screenshots
|
|
- `fonts-noto-color-emoji` — emoji glyphs
|
|
- `tzdata` — `Asia/Shanghai` timezone
|
|
|
|
Microsoft's base image already ships all three browsers in `/ms-playwright/`:
|
|
|
|
- `chromium-XXXX/chrome-linux/chrome` — the primary
|
|
- `firefox-XXXX/firefox/firefox`
|
|
- `webkit-XXXX/pw_run.sh`
|
|
|
|
Plus every system library Chromium needs (`libnss3`, `libgbm1`, `libasound2`, `libx11-xcb1`, `libxkbcommon`, …). **No `playwright install` is required, and the Alpine-vs-musl incompatibility that blocks most Playwright deployments is sidestepped entirely.**
|
|
|
|
The Dockerfile sets one environment variable explicitly:
|
|
|
|
```dockerfile
|
|
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
|
|
```
|
|
|
|
This tells Playwright Java to use the pre-installed browsers and **not** try to download to `$HOME/.cache/ms-playwright` at runtime.
|
|
|
|
### BrowserLauncher's 7-strategy fallback
|
|
|
|
`vip.mate.tool.browser.BrowserLauncher` tries each strategy in order until one succeeds:
|
|
|
|
1. `CONFIG_CDP` — if `MATECLAW_BROWSER_CDP_URL` is set, attach to that running Chrome
|
|
2. `CONFIG_PATH` — if `MATECLAW_BROWSER_CHROME_PATH` or `CHROME_PATH` env is set, use that exe
|
|
3. `CONFIG_CHANNEL` — if `MATECLAW_BROWSER_CHANNEL=chrome|msedge`, use the Playwright channel
|
|
4. `AUTO_CHANNEL` — try `chrome` then `msedge` channel (this step always wins inside the Docker image)
|
|
5. `AUTO_PATH` — scan standard install paths (`/usr/bin/google-chrome`, `chromium-browser`, `/snap/bin/chromium`, `microsoft-edge`, `brave-browser`)
|
|
6. `BUNDLED` — Playwright bundled Chromium (also guaranteed to work inside the image)
|
|
7. `EXTERNAL_CDP` — last resort: fork a system Chrome with `--remote-debugging-port=0`, parse stderr for the DevTools URL, attach via `connectOverCDP` (the openfang pattern)
|
|
|
|
Inside the Docker image, **strategy 4 or 6 always hits** and no configuration is needed. If you need to attach to an external Chrome, use strategy 1. If you want a specific host-installed Chrome, use strategy 2.
|
|
|
|
### `/dev/shm` must be 2 GB
|
|
|
|
`docker-compose.yml` sets `shm_size: 2gb` for `mateclaw-server`. Docker defaults to 64 MB per container — Chromium uses shared memory for GPU compositing and page rendering, and three tabs is enough to SIGBUS the browser. Playwright surfaces this as `TargetClosedError: Target page, context or browser has been closed`. **Do not shrink this value.**
|
|
|
|
### SSRF protection
|
|
|
|
Before any `navigate` call, `BrowserUseTool` runs the URL through `UrlSafetyChecker`, which **hard-blocks** these hosts:
|
|
|
|
- `localhost`, `127.0.0.1`, `::1`, `0.0.0.0`
|
|
- `169.254.169.254` (AWS / GCP / Azure IMDS), `100.100.100.200` (Alibaba Cloud IMDS), `192.0.0.192` (Azure IMDS alternative)
|
|
- All link-local / private / multicast IP ranges
|
|
|
|
An LLM generating a malicious URL to dump cloud credentials is therefore a closed loop. If you genuinely need to scrape internal infrastructure from a specific host, either disable via `mateclaw.browser.ssrf-check-enabled` or edit the `UrlSafetyChecker` allowlist. **Think twice before doing this in production.**
|
|
|
|
### Verifying the browser path
|
|
|
|
```sh
|
|
# 1. Pre-flight diagnosis (doesn't actually launch a browser)
|
|
curl -s http://localhost:18080/api/v1/system/browser-health | jq .
|
|
# Expect: overall: "healthy", system.browsers found with chromium path
|
|
|
|
# 2. Drive it from an agent
|
|
# browser_use(action="diagnose") # returns the strategy-chain trace
|
|
# browser_use(action="start") # actually launches
|
|
# browser_use(action="open", url="https://example.com")
|
|
# browser_use(action="screenshot") # returns a base64 PNG
|
|
```
|
|
|
|
---
|
|
|
|
## First deployment
|
|
|
|
```sh
|
|
git clone https://github.com/mateaix/mateclaw.git
|
|
cd mateclaw
|
|
|
|
# 1. Fill in required values
|
|
cp .env.example .env
|
|
vi .env # see table below
|
|
```
|
|
|
|
**Required** (compose refuses to start without these, so you can't accidentally ship default passwords):
|
|
|
|
| Variable | Notes |
|
|
|---|---|
|
|
| `DB_PASSWORD` | App DB password — 16+ chars, mixed case, digits, symbols |
|
|
| `DB_ADMIN_PASSWORD` | PostgreSQL bootstrap superuser password (init + admin only) — **must differ from the above** |
|
|
|
|
**Strongly recommended** (not enforced, but startup logs WARN if missing):
|
|
|
|
| Variable | Notes |
|
|
|---|---|
|
|
| `JWT_SECRET` | JWT signing key — generate with `openssl rand -base64 48` |
|
|
| `MATECLAW_CORS_ALLOWED_ORIGINS` | Production allowlist, e.g. `https://mateclaw.example.com` |
|
|
|
|
Then bring the stack up:
|
|
|
|
```sh
|
|
docker compose up -d --build # first build takes 3-10 minutes
|
|
docker compose logs -f mateclaw-server
|
|
```
|
|
|
|
First boot runs Flyway migrations (~5 s) and seeds default data (~3 s), then binds `0.0.0.0:18080`.
|
|
|
|
Open `http://localhost:18080`, sign in as `admin / admin123`, and **change the password immediately** under `Settings → Security`.
|
|
|
|
---
|
|
|
|
## Build-time performance
|
|
|
|
### US / EU servers
|
|
|
|
**Already optimal.** `mateclaw-server/pom.xml` lists repositories in the order `Maven Central → Google CDN → Aliyun`; Central direct is fastest over US/EU backbones.
|
|
|
|
### China servers
|
|
|
|
Flip to Aliyun-first. Either edit the `mvn` lines in `mateclaw-server/Dockerfile` to add `-Paliyun-first`, or (easier) expose it as a build arg:
|
|
|
|
```dockerfile
|
|
# from
|
|
RUN mvn dependency:go-offline -q
|
|
RUN mvn package -DskipTests -q
|
|
|
|
# to
|
|
ARG MAVEN_PROFILE=
|
|
RUN mvn dependency:go-offline -q ${MAVEN_PROFILE:+-P${MAVEN_PROFILE}}
|
|
RUN mvn package -DskipTests -q ${MAVEN_PROFILE:+-P${MAVEN_PROFILE}}
|
|
```
|
|
|
|
Then:
|
|
|
|
```sh
|
|
docker compose build --build-arg MAVEN_PROFILE=aliyun-first mateclaw-server
|
|
```
|
|
|
|
Aliyun's public + Spring mirrors are promoted to the top of the lookup chain, keeping traffic inside China.
|
|
|
|
---
|
|
|
|
## Optional overrides
|
|
|
|
All can be set in `.env` and are read as environment variables. **Leave them empty to accept the container defaults.**
|
|
|
|
| Variable | Default | Purpose |
|
|
|---|---|---|
|
|
| `SERPER_API_KEY` | — | Google Serper search API (paid, best quality) |
|
|
| `SEARXNG_SECRET` | built-in dev secret | Only fill when exposing port 8088 to the public internet |
|
|
| `SEARXNG_BASE_URL` | `http://searxng:8080` | Point at an external SearXNG instance |
|
|
| `MATECLAW_BROWSER_CDP_URL` | — | Attach to an external Chrome CDP sidecar |
|
|
| `MATECLAW_BROWSER_CHROME_PATH` | — | Override the bundled Chromium with a host-installed browser |
|
|
| `MATECLAW_BROWSER_CHANNEL` | — | Force a Playwright channel (`chrome`, `msedge`, ...) |
|
|
|
|
**LLM API keys (DashScope, OpenAI, Anthropic, DeepSeek, Kimi, etc.) are not read from `.env`** — add them after startup in the UI under `Settings → Models → Add Provider`. Hot-reload supported. The container starts with **zero LLM keys configured**; just log in and add your first provider on the Models page.
|
|
|
|
---
|
|
|
|
## Verification
|
|
|
|
Run these in order after `docker compose up -d`:
|
|
|
|
```sh
|
|
# 1. All three containers healthy
|
|
docker compose ps
|
|
|
|
# 2. Base health check
|
|
curl -s http://localhost:18080/api/v1/system/health | jq .
|
|
|
|
# 3. Browser tool self-diagnosis (the most common failure point on Linux hosts)
|
|
curl -s http://localhost:18080/api/v1/system/browser-health | jq .
|
|
# Expect overall: "healthy"
|
|
|
|
# 4. SearXNG returns JSON (not an HTML error page)
|
|
curl -s 'http://localhost:8088/search?q=hello&format=json' | head -5
|
|
```
|
|
|
|
If any of these fail, jump to the next section.
|
|
|
|
---
|
|
|
|
## Common gotchas
|
|
|
|
**Build stage `mvn dependency:go-offline` hangs**
|
|
US servers pulling through Aliyun is slow. The default `pom.xml` puts Maven Central first, so it should be fast. If it's still slow, the container has no outbound access — check your egress firewall.
|
|
|
|
**`mateclaw-server` stays unhealthy at startup**
|
|
`docker compose logs mateclaw-server` and look for Flyway migration errors. Nine times out of ten, a special character in `DB_PASSWORD` got eaten by the shell — wrap the value in double quotes in `.env`.
|
|
|
|
**Browser tool reports "Target page closed" or SIGBUS**
|
|
`shm_size: 2gb` didn't take effect. Check the actual value with `docker inspect mateclaw-server | grep ShmSize`. Upgrade Docker Engine to 24.0+ if it's still showing 64 MB.
|
|
|
|
**Search returns "Search temporarily unavailable"**
|
|
SearXNG either isn't up or the image default settings disabled JSON output. Our own `./docker/searxng/` build patches this; if you're reusing an old named volume, reset it: `docker compose down -v searxng && docker compose up -d searxng`.
|
|
|
|
**LLM responses show tofu boxes (□) for Chinese**
|
|
The image already installs `fonts-noto-cjk` and `fonts-noto-color-emoji`, so this isn't a server-side font issue. Check your frontend browser's locale / font settings.
|
|
|
|
---
|
|
|
|
## Upgrading
|
|
|
|
```sh
|
|
git pull
|
|
docker compose build mateclaw-server # only rebuild the backend
|
|
docker compose up -d mateclaw-server
|
|
```
|
|
|
|
The `postgres_data` volume persists across rebuilds. Flyway runs incremental migrations automatically and self-heals checksum changes on restart. **Version is pinned in `mateclaw-server/pom.xml` and the git tag** — prefer pinning to a tag in production, not tracking `dev`.
|
|
|
|
---
|
|
|
|
## Next steps
|
|
|
|
- [Configuration](./config) — every environment variable and runtime toggle
|
|
- [Doctor Health Check](./doctor) — the in-app diagnostics page
|
|
- [Security & Approval](./security) — pre-production hardening checklist
|