Docker Deployment Guide#
This guide explains how to deploy the EngiAI chatbot using Docker on Windows Server or any other platform.
Prerequisites#
Docker installed on your system
Docker Compose (optional, but recommended)
Credentials for the selected LLM provider, or a local Ollama model
External Services#
MMORE RAG Service - Multimodal document retrieval system
Runs from the official upstream image, pinned in
docker-compose.yml, through a small local layer (services/mmore/Dockerfile) that carries four fixes v2.0.0 needs before it will start. Nothing to clone and no patches to apply:./docker-compose-wrapper.sh up -dpulls the base and builds it.Default URL:
http://localhost:8000; override withMMORE_RAG_URLin.envOverride the image with
MMORE_IMAGE. Seeservices/mmore/README.mdbefore bumping the pin.After the first start, build the index with
make mmore-index.
Prusa Connect MCP Server (Optional - only if using 3D printer integration)
Installed as a pip dependency from git (
prusa-mcp @ git+https://github.com/gioelemo/prusa-mcp.git), declared inservices/prusa_mcp_server/requirements-mcp.txtBuilt automatically into the
prusa-mcp-serverDocker image byservices/prusa_mcp_server/DockerfileSee
services/prusa_mcp_server/README.mdfor configurationSet
SKIP_MCP=falseto enable
These services run as separate containers/processes and the EngiAI connects to them via network.
Quick Start#
Option 1: Using Docker Compose (Recommended)#
Create a
.envfile from the example:cp .env.example .env
Edit the
.envfile and configure the LLM and search providers:OPENAI_API_KEY=your_actual_openai_key SEARCH_PROVIDER=duckduckgo
Build and run with Docker Compose:
./docker-compose-wrapper.sh up -d # or: make docker-up
Always go through the wrapper (or
make docker-up, which calls it) rather thandocker composedirectly: the wrapper creates thedata/mmore/bind-mount targets and touches the Milvus Lite database files, without which Docker substitutes a directory for the missingdata/mmore/index/mmore.dband the MMORE service — a health-gated dependency of the chatbot — cannot start. (make docker-upalso starts the host helper service on port 9999.)Access the application: Open your browser and navigate to
http://localhost:8501View logs:
./docker-compose-wrapper.sh logs -f chatbot
Stop the application:
./docker-compose-wrapper.sh down
Option 2: Using Docker CLI#
Build the Docker image:
docker build -t engiai-chatbot .
Run the container with environment variables:
docker run -d \ --name engiai \ -p 8501:8501 \ -e OPENAI_API_KEY=your_openai_key \ -e SEARCH_PROVIDER=duckduckgo \ -v $(pwd)/data:/app/data \ engiai-chatbot
Access the application: Open your browser and navigate to
http://localhost:8501
Windows Server Deployment#
Method 1: Build on Windows Server#
Install Docker Desktop for Windows or Docker Engine
Clone your repository or copy the project files to the server
Follow the “Quick Start” instructions above
Method 2: Transfer Image from Another Machine#
If you build the image on your local machine and want to deploy on Windows Server:
On your build machine (Mac/Linux):
# Build the image docker build -t engiai-chatbot . # Save the image to a tar file docker save -o engiai-chatbot.tar engiai-chatbot
Transfer the tar file to Windows Server (via SCP, FTP, USB, etc.)
On Windows Server:
# Load the image docker load -i engiai-chatbot.tar # Create a .env file with your API keys # Then run with Docker Compose or Docker CLI docker run -d ` --name engiai ` -p 8501:8501 ` --env-file .env ` -v ${PWD}/data:/app/data ` engiai-chatbot
Method 3: Using a Container Registry#
Push to Docker Hub (or another registry):
# Tag the image docker tag engiai-chatbot your-username/engiai-chatbot:latest # Login to Docker Hub docker login # Push the image docker push your-username/engiai-chatbot:latest
On Windows Server, pull and run:
docker pull your-username/engiai-chatbot:latest docker run -d ` --name engiai ` -p 8501:8501 ` --env-file .env ` -v ${PWD}/data:/app/data ` your-username/engiai-chatbot:latest
Environment Variables#
Provider Variables#
OPENAI_API_KEYorGOOGLE_API_KEY: Credential for the selected remote LLMSEARCH_PROVIDER:duckduckgoby default; set totavilyonly when desiredTAVILY_API_KEY: Required only whenSEARCH_PROVIDER=tavily
Important Variables#
SKIP_MCP: Skip Prusa MCP server initialization (default:falsein Docker)Set to
trueto disable 3D printer integrationSet to
false(default in docker-compose) to enable Prusa MCP
Optional Variables#
LLM_MODEL: Model to use (default:openai:gpt-4.1)DATABASE_URL: Database connection string (default:sqlite:///data/conversations.db)LANGCHAIN_TRACING: Enable LangSmith tracing (default:false)
See .env.example in the project root for all available configuration options.
About MCP Server (Prusa 3D Printer Integration)#
The application includes optional integration with Prusa Connect via an MCP (Model Context Protocol) server for 3D printer management. For most deployments, this is not needed and should be disabled by setting SKIP_MCP=true.
To enable Prusa integration (advanced):
Run the one-time OAuth2 login on the host with
make prusa-login(writes./data/prusa_tokens.json, which the container mounts asPRUSA_TOKEN_FILE=/app/data/prusa_tokens.json)Set
SKIP_MCP=false(docker-compose already forces this for the chatbot service)Point
PRUSA_MCP_URLat the server — docker-compose setshttp://prusa-mcp-server:8765automatically; usehttp://localhost:8765when running the server outside Docker viamake run-mcp
SKIP_MCP gates the whiteboard canvas as well as the printer, so disabling it turns off both — use SKIP_CANVAS to disable only the whiteboard.
In Docker the .env value is overridden: docker-compose.yml sets SKIP_MCP=false in the chatbot service’s environment: block, and the chatbot additionally waits for prusa-mcp-server to become healthy. To run without the printer integration you must override both — e.g. a docker-compose.override.yml that sets SKIP_MCP=true on the chatbot and removes the prusa-mcp-server service together with its depends_on entry.
About HPC Cluster Integration (SSH Access)#
For a shared workstation, use the managed HPC broker rather than the direct SSH mechanism below. The broker keeps the technical account’s private key outside Docker under a dedicated macOS service identity and adds resource limits plus audit logging.
For personal/development deployments, the Docker configuration mounts the host
directory selected by SSH_HOST_DIR (default: ~/.ssh) into the container. This
enables:
Connection to HPC clusters (like ETH’s Euler cluster)
SLURM job submission and monitoring
Remote file operations
How it works:
Your
~/.sshdirectory is mounted read-only to/root/.ssh-hostin the containerThe
docker-entrypoint.shscript copies the SSH configuration and sets proper permissions (600 for keys, 644 for known_hosts)SSH client is pre-installed in the container
What you need:
Your
~/.ssh/configfile with host aliases (e.g., “euler”)SSH private keys in
~/.ssh/The remote host’s entry in
~/.ssh/known_hosts(add it by connecting once from your host machine)
For passphrase-protected SSH keys: If your SSH key is protected with a passphrase, you need to use SSH agent forwarding:
Start SSH agent and add your key (on your host machine):
# Start the SSH agent eval "$(ssh-agent -s)" # Add your SSH key (you'll be prompted for the passphrase once) ssh-add ~/.ssh/id_rsa # or id_ed25519, or whatever your key is named # Verify the key is loaded ssh-add -l
Start the container (the SSH agent socket is automatically mounted):
./docker-compose-wrapper.sh up -d
The container will use your host’s SSH agent - no passphrase required!
Alternative: Use an unencrypted key (less secure): If you prefer not to use SSH agent, you can create a separate SSH key without a passphrase specifically for the container:
ssh-keygen -t ed25519 -f ~/.ssh/id_docker_euler -N ""
# Then add this key to your Euler account's authorized_keys
Testing SSH connection: Once the container is running, you can test the SSH connection through the chatbot UI by asking it to connect to the HPC cluster.
Security notes:
SSH keys are mounted read-only from your host
Keys are copied into the container (not shared directly)
Container is isolated from your host system
For production, consider using SSH agent forwarding or secrets management
Data Persistence#
The application stores conversation data in the /app/data directory. To persist this data:
Using Docker Compose: The
docker-compose.ymlalready mounts./data:/app/dataUsing Docker CLI: Add the volume mount:
-v $(pwd)/data:/app/data
Troubleshooting#
Container won’t start - Configuration Error#
Error: ValueError in config validation
Solution: Ensure OPENAI_API_KEY and TAVILY_API_KEY are set in your .env file or passed as environment variables.
Port 8501 already in use#
Solution: Either stop the service using port 8501 or map to a different port:
docker run -p 8080:8501 ... # Access on http://localhost:8080
Database connection issues#
Solution: If using PostgreSQL, ensure the database is accessible from the container. For SQLite (default), ensure the data directory is mounted.
Viewing container logs#
# Using Docker Compose
./docker-compose-wrapper.sh logs -f chatbot
# Using Docker CLI
docker logs -f engiai
Restarting the container#
# Using Docker Compose
./docker-compose-wrapper.sh restart
# Using Docker CLI
docker restart engiai
Health Check#
The application includes a health check endpoint. You can verify the application is running:
curl http://localhost:8501/_stcore/health
Updating the Application#
Pull latest code from your repository
Rebuild and restart everything:
make docker-rebuild
To rebuild a single service instead of the whole stack, leaving the others running:
make docker-rebuild-chatbot # just the Streamlit app
make docker-rebuild-prusa # just the Prusa MCP server
make mmore-rebuild # the derived MMORE image, re-pulling its base
Security Best Practices#
Never commit
.envfiles with real API keys to version controlUse secrets management in production (Docker secrets, Kubernetes secrets, etc.)
Restrict network access to the container if needed
Regularly update the base image and dependencies
Production Considerations#
Using Docker Swarm or Kubernetes#
For production deployments, consider:
Using orchestration platforms (Docker Swarm, Kubernetes)
Implementing load balancing
Setting up automated backups for the data directory
Using managed databases instead of SQLite
Implementing monitoring and alerting
Resource Limits#
Add resource limits to prevent excessive resource usage:
# In docker-compose.yml
services:
chatbot:
# ... other configuration ...
deploy:
resources:
limits:
cpus: '2'
memory: 4G
reservations:
cpus: '1'
memory: 2G
MMORE RAG Evaluation Service#
For running agent evaluations with MMORE RAG enabled, a separate Docker Compose file is provided that runs on a different port to avoid conflicts with the main application.
Why a Separate Service?#
Port isolation: Eval service runs on port 8001, main service on port 8000
Independent volumes: Separate cache and upload volumes prevent data conflicts
Parallel execution: Run evaluations while the main application is running
Starting the Evaluation Service#
# Start MMORE for evaluations (port 8001)
make mmore-eval-up
# Check status
make mmore-eval-status
# View logs
make mmore-eval-logs
# Stop when done
make mmore-eval-down
Running Evaluations with MMORE#
# Use the dedicated make command
make mmore-eval-run ARGS="--problem beams2d --samples 5 --scorers all --seed 1"
# Or set the environment variable manually
MMORE_RAG_URL=http://localhost:8001 python benchmarks/evaluations/evaluate_agent.py \
--problem beams2d --samples 5 --mmore
Service Comparison#
Service |
Started by |
Container |
Host Port |
Index |
|---|---|---|---|---|
Main MMORE |
|
|
8000 |
|
Eval MMORE |
|
|
8001 |
|
Both are defined in docker-compose.yml from a shared YAML anchor, so they
always run the same pinned image and config. The eval service sits behind the
eval profile and starts under its own compose project
(engiai-mmore-eval), which means make docker-down cannot terminate a
running evaluation. They have separate index directories — earlier versions
bind-mounted a single Milvus Lite file into both containers with unguarded
concurrent writes.
They share the model cache at data/mmore/cache/. Start the main service
first and let it warm the cache, rather than having both race the same
multi-gigabyte download.
See benchmarks/evaluations/README.md for detailed evaluation documentation.
Support#
For issues and questions:
Check the main README.md in the project root
Review the Database Setup Guide for database configuration
Check Docker logs for error messages