ChatDev Docker Deployment and Containerization: Multi-Stage Setup Guide

Deploy ChatDev locally using Docker Compose with multi-stage builds that separate Python and Node.js services, exposing ports 6400 and 5173 for immediate development access.

ChatDev by OpenBMB is packaged as a containerized multi-service application designed for both rapid development and production deployment. This guide explains how to deploy and containerize ChatDev using Docker's multi-stage build pattern to create lean runtime images while maintaining fast rebuilds during iterative development.

Multi-Stage Backend Containerization

The backend service is defined in the repository root Dockerfile and uses Python 3.12 with the uv package manager for deterministic dependency resolution. The build process splits compilation from runtime to minimize the final image size.

Builder Stage

The builder stage handles dependency resolution and compilation. It starts from python:3.12-slim and installs system compilers including pkg-config, build-essential, python3-dev, and libcairo2-dev. The uv tool is installed via pip install uv, then configured to create the virtual environment at /opt/venv using UV_PROJECT_ENVIRONMENT=/opt/venv. The stage copies pyproject.toml and uv.lock before running uv sync --no-cache --frozen to populate the virtual environment without network calls.

Runtime Stage

The runtime stage starts from a fresh python:3.12-slim image to exclude build tools. It installs only the runtime library libcairo2, copies the pre-built virtual environment from the builder, and sets PATH="/opt/venv/bin:${PATH}". The application source is copied into the image, Python output is set to unbuffered mode, and a non-root user named appuser is created for security. Port 6400 is exposed, and the container executes python server_main.py to start the FastAPI backend.


# Example: Build only the runtime stage

docker build \
  --target runtime \
  -t chatdev-backend:latest \
  -f Dockerfile .

Frontend Container Architecture

The frontend service defined in frontend/Dockerfile uses Node.js 24 Alpine for minimal footprint and separates dependency installation from source code to maximize cache efficiency.

Dependency Caching Stage

The deps stage uses node:24-alpine as the base image. It copies package*.json files and executes npm ci (falling back to npm install) to generate the node_modules directory. This layer is cached independently of application code changes, ensuring dependencies only reinstall when package definitions change.

Development Runtime Stage

The dev stage copies the cached node_modules and remaining source code, sets NODE_ENV=development, and exposes port 5173. It runs npm run dev -- --host to start the Vite development server with hot-reload enabled and network binding.

Docker Compose Orchestration

The compose.yml file orchestrates both services with volume mounts that enable live editing without image rebuilds.

  • backend: Builds targeting the runtime stage, mounts the repository root (.:/app), publishes 6400:6400, and loads environment variables from .env and .env.docker.
  • frontend: Builds targeting the dev stage, mounts ./frontend:/app, publishes ${FRONTEND_PORT:-5173}:5173, and declares a service dependency on the backend.

This configuration allows the frontend Vite server to communicate with the backend via the internal Docker network using the service name backend as the hostname.

Environment Configuration

Docker-specific defaults are stored in .env.docker and automatically loaded by Compose:

BACKEND_BIND=0.0.0.0
FRONTEND_HOST=0.0.0.0
FRONTEND_PORT=5173
VITE_API_BASE_URL=http://backend:6400
CORS_ALLOW_ORIGINS=http://localhost:5173,http://127.0.0.1:5173

For local development, copy the example file to .env and adjust project-specific values:

cp .env.example .env

The VITE_API_BASE_URL variable ensures the frontend browser requests proxy correctly to the backend container named backend on port 6400.

Quick Start Deployment

Run the complete ChatDev stack with these commands:


# 1. Copy environment configuration

cp .env.example .env

# 2. Build and launch both services

docker compose up --build

# 3. Access the application

#    Frontend: http://localhost:5173

#    Backend API: http://localhost:6400

Volume mounts (.:/app for backend and ./frontend:/app for frontend) enable real-time code changes to reflect immediately in running containers, ideal for rapid iteration on the OpenBMB/ChatDev codebase.

Production Build Strategies

For continuous integration pipelines or standalone testing, build specific components without Compose:

Run the backend image independently for integration testing:

docker run -d \
  --name chatdev-backend \
  -p 6400:6400 \
  --env-file .env.docker \
  chatdev-backend:latest

Force a frontend dependency refresh after modifying package.json:

docker compose build frontend
docker compose up -d frontend

Summary

  • Multi-stage builds separate compilation dependencies from runtime libraries, reducing the backend image to approximately 30 MB.
  • Backend security uses a dedicated appuser account with dropped privileges rather than running as root.
  • Frontend caching isolates node_modules in a dedicated stage, preventing unnecessary reinstalls during source code edits.
  • Service networking relies on Docker Compose DNS, with the frontend connecting to http://backend:6400 via VITE_API_BASE_URL.
  • Live development is supported through bind mounts that map host directories directly into containers without rebuilds.

Frequently Asked Questions

What ports does ChatDev expose in Docker?

The backend exposes port 6400 for the Python FastAPI server, while the frontend development server exposes port 5173 (configurable via FRONTEND_PORT environment variable). These are mapped to the host in compose.yml using 6400:6400 and ${FRONTEND_PORT:-5173}:5173 respectively.

How do I rebuild the frontend after updating package.json?

Run docker compose build frontend to invalidate the cached deps stage and reinstall Node modules, then execute docker compose up -d frontend to restart the service with the updated dependencies. This ensures node_modules reflects changes to package*.json files.

Why does the backend use a non-root user?

The Dockerfile creates an appuser account and switches to it with USER appuser before executing server_main.py as a security best practice. This limits potential damage from container escapes or compromised processes by removing root filesystem privileges from the running Python application.

Can I run the ChatDev backend without the frontend container?

Yes. Build the backend image targeting the runtime stage, then run it standalone using docker run with port mapping -p 6400:6400 and an environment file --env-file .env.docker. This is useful for API testing, backend development, or when serving the frontend through a separate reverse proxy or static hosting solution.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →