This directory contains Docker configuration files to run the Spec-Workflow MCP dashboard in a containerized environment. This setup provides isolation and easy deployment for the dashboard service.
- Prerequisites
- Quick Start
- Testing the Docker Image
- Building the Image
- Running the Dashboard
- Using Docker Compose
- Configuration Options
- Security Configuration
- Troubleshooting
- Docker (version 20.10 or later)
- Docker Compose (optional, for simplified management)
- A project directory where you want to use spec-workflow
The easiest way to get started is with Docker Compose:
# From the repository root
cd containers
docker-compose up --buildThe dashboard will be available at: http://localhost:5000
Build and run manually:
# From the repository root
docker build -f containers/Dockerfile -t spec-workflow-mcp .
docker run -p 5000:5000 -v "./workspace/.specflow:/workspace/.specflow:rw" spec-workflow-mcpA comprehensive test script is provided to validate the Docker image configurations:
# From the containers directory
./test-docker.shThe test script validates:
| Test | Description |
|---|---|
| Image Build | Verifies the Docker image builds successfully |
| Docker Default Config | Tests app binding to 0.0.0.0, Docker exposing to localhost |
| Security Check | Verifies app-level security block (when overriding Docker defaults) |
| Network Exposure | Tests full network port mapping |
| Rate Limiting | Verifies rate limiting configuration |
| Non-Root User | Confirms container runs as non-privileged user |
| Custom Port | Tests custom port configuration |
You can also manually test specific configurations:
# Test default Docker config (localhost-only access)
docker run --rm \
-p 127.0.0.1:5000:5000 \
spec-workflow-mcp
# Expected: Dashboard starts, accessible only from host machine
# Test network access (exposes to all interfaces)
docker run --rm \
-p 5000:5000 \
spec-workflow-mcp
# Expected: Dashboard starts with security warning
# Test app-level security check (override Docker defaults)
docker run --rm \
-e SPEC_WORKFLOW_ALLOW_EXTERNAL_ACCESS=false \
spec-workflow-mcp
# Expected: SECURITY ERROR (app blocks external binding)Important: The Dockerfile must be built from the repository root directory, not from the containers directory, because it needs access to the source code.
# From the repository root
docker build -f containers/Dockerfile -t spec-workflow-mcp .The image is built in two stages:
- Builder stage: Installs dependencies and builds the TypeScript application
- Runtime stage: Creates a minimal production image with only necessary files
Run the dashboard on the default port (5000):
docker run -p 5000:5000 \
-v "./workspace/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcpRun the dashboard on a custom port (e.g., 8080):
docker run -p 8080:8080 \
-e DASHBOARD_PORT=8080 \
-v "./workspace/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcpMount your project's .specflow directory:
docker run -p 5000:5000 \
-v "/path/to/your/project/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcpDocker Compose simplifies the management of the dashboard container.
Create a .env file (optional):
# .env file
DASHBOARD_PORT=5000
SPEC_WORKFLOW_PATH=./workspaceThen start the dashboard:
cd containers
docker-compose up -dOverride environment variables when starting:
DASHBOARD_PORT=8080 SPEC_WORKFLOW_PATH=/path/to/project docker-compose up -d# Start the dashboard
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the dashboard
docker-compose down
# Rebuild and restart
docker-compose up --build| Variable | Default (Docker) | Description |
|---|---|---|
DASHBOARD_PORT |
5000 |
Port on which the dashboard runs |
DASHBOARD_HOST |
127.0.0.1 |
Host IP for port binding (0.0.0.0 for network access) |
SPEC_WORKFLOW_PATH |
/workspace |
Path to the project directory (inside container) |
SPEC_WORKFLOW_BIND_ADDRESS |
0.0.0.0 |
IP address to bind to inside container (Docker requires 0.0.0.0 for port forwarding) |
SPEC_WORKFLOW_ALLOW_EXTERNAL_ACCESS |
true |
Set to true in Docker (external access controlled by port mapping) |
SPEC_WORKFLOW_RATE_LIMIT_ENABLED |
true |
Enable/disable rate limiting |
SPEC_WORKFLOW_CORS_ENABLED |
true |
Enable/disable CORS |
The dashboard requires access to the .specflow directory to function properly.
Example:
-v "/path/to/project/.specflow:/workspace/.specflow:rw"Important Notes:
- The volume mount must be read-write (
:rw) for the dashboard to function - Only the
.specflowdirectory needs to be mounted - The directory will be created automatically if it doesn't exist
Map the container port to a host port:
-p <host-port>:<container-port>Examples:
- Default:
-p 5000:5000 - Custom:
-p 8080:8080(remember to setDASHBOARD_PORT=8080)
The Docker image includes several security features that are enabled by default.
In Docker, the application binds to 0.0.0.0 inside the container (required for Docker's port forwarding to work). Security is controlled by Docker's port mapping, not by the application's bind address.
The default docker-compose.yml uses localhost-only port mapping:
ports:
- '127.0.0.1:5000:5000' # Only accessible from host machineOr with Docker CLI:
docker run -p 127.0.0.1:5000:5000 \
-v "./workspace/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcp✅ This is the recommended configuration. The dashboard is only accessible from your local machine.
To allow access from other machines on your network, use DASHBOARD_HOST=0.0.0.0:
With Docker Compose (recommended):
# Expose to all network interfaces
DASHBOARD_HOST=0.0.0.0 docker-compose up
# Or bind to a specific IP
DASHBOARD_HOST=192.168.1.100 docker-compose upWith Docker CLI:
docker run -p 0.0.0.0:5000:5000 \
-v "./workspace/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcp
# Or bind to a specific IP
docker run -p 192.168.1.100:5000:5000 \
-v "./workspace/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcpRate limiting protects against abuse and DoS attacks. It's enabled by default (120 requests/minute per client).
# Disable rate limiting (not recommended)
docker run -e SPEC_WORKFLOW_RATE_LIMIT_ENABLED=false spec-workflow-mcpThe provided docker-compose.yml includes additional security hardening:
# Read-only root filesystem
read_only: true
# Drop all Linux capabilities
cap_drop:
- ALL
# Prevent privilege escalation
security_opt:
- no-new-privileges:true
# Resource limits (prevent DoS)
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M- Use localhost port mapping when possible (
127.0.0.1:5000:5000) - Never expose to public internet without proper authentication/firewall
- Keep rate limiting enabled in production
- Use the provided docker-compose.yml for security hardening
- Run as non-root user (default in the image)
- Mount volumes read-write only when necessary
All API requests are logged to a structured JSON audit log for compliance and debugging.
Log Location: <project>/.specflow/audit.log
Log Format:
{
"timestamp": "2025-12-06T10:30:45.123Z",
"actor": "127.0.0.1",
"action": "GET /api/projects/list",
"resource": "/api/projects/list",
"result": "success",
"details": {
"statusCode": 200,
"duration": 45,
"userAgent": "Mozilla/5.0..."
}
}Viewing Logs:
# View recent logs
tail -f .specflow/audit.log
# Parse as JSON (requires jq)
cat .specflow/audit.log | jq '.'
# Filter by result
cat .specflow/audit.log | jq 'select(.result == "denied")'The dashboard does not include built-in authentication. For external access, use a reverse proxy:
Option 1: nginx with Basic Auth
server {
listen 443 ssl;
server_name dashboard.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
auth_basic "Dashboard Access";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Option 2: OAuth2 Proxy (for SSO)
oauth2-proxy \
--upstream=http://127.0.0.1:5000 \
--http-address=0.0.0.0:4180 \
--provider=google \
--client-id=YOUR_CLIENT_ID \
--client-secret=YOUR_CLIENT_SECRETThe dashboard runs independently of MCP servers. To connect MCP servers to the dashboard:
Add to your claude_desktop_config.json:
{
"mcpServers": {
"spec-workflow": {
"command": "npx",
"args": ["-y", "@pimzino/spec-workflow-mcp@latest", "/path/to/your/project"]
}
}
}Note: The MCP server runs on your host machine and connects to the Docker dashboard automatically via port 5000.
Use similar configuration with the appropriate MCP client settings. The MCP servers run independently and connect to the dashboard's WebSocket endpoint.
Error: Bind for 0.0.0.0:5000 failed: port is already allocated
Solution: Use a different port:
docker run -p 8080:8080 -e DASHBOARD_PORT=8080 ...
# or with docker-compose
DASHBOARD_PORT=8080 docker-compose upError: Permission issues with .specflow directory
Solutions:
- Ensure the directory has proper permissions:
chmod -R 755 .specflow - On SELinux systems, add
:zto the volume mount:-v "./workspace/.specflow:/workspace/.specflow:rw,z"
Check:
- Container is running:
docker ps - Port is properly mapped:
docker port <container-id> - Firewall allows connections on the port
- Access via:
http://localhost:5000(or your custom port)
Error: Build fails with COPY or dependency errors
Solutions:
- Ensure you're building from the repository root:
docker build -f containers/Dockerfile -t spec-workflow-mcp . - Check that all source files are present
- Verify
package.jsonandpackage-lock.jsonexist
docker logs <container-id>
docker logs -f <container-id> # Follow logsdocker-compose logs
docker-compose logs -f # Follow logs# View container details
docker inspect <container-id>
# Access container shell
docker exec -it <container-id> /bin/shdocker run -d \
--name spec-workflow-dashboard \
-p 5000:5000 \
-v "./workspace/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcpdocker run -d \
--name spec-workflow-dashboard \
--restart unless-stopped \
-p 5000:5000 \
-v "./workspace/.specflow:/workspace/.specflow:rw" \
spec-workflow-mcpThe dashboard doesn't currently include health checks, but you can test connectivity:
curl http://localhost:5000The Docker image implements enterprise-grade security controls:
| Feature | Status | Description |
|---|---|---|
| Non-root User | ✅ Enabled | Runs as node user (UID 1000) |
| Rate Limiting | ✅ Enabled | 120 req/min per client |
| Audit Logging | ✅ Enabled | JSON logs with 30-day retention |
| Security Headers | ✅ Enabled | XSS, clickjacking, MIME sniffing protection |
| CORS Protection | ✅ Enabled | Localhost origins only by default |
| Localhost Binding | ✅ Default | 127.0.0.1:5000:5000 in docker-compose |
| HTTPS/TLS | ❌ Not built-in | Use reverse proxy (nginx/Apache) |
| User Authentication | ❌ Not built-in | Use reverse proxy with Basic Auth or OAuth2 |
Best Practices:
- Keep the base image updated:
docker pull node:24-alpine - Use read-only volume mounts where possible (
:rwrequired for.specflow) - For network access, always use a reverse proxy with TLS and authentication
- Review audit logs regularly for security monitoring
- The container is optimized for production with:
- Multi-stage builds to minimize image size
- Only production dependencies in final image
- Alpine Linux for small footprint
- Monitor resource usage:
docker stats <container-id>
If you encounter issues:
- Check the Troubleshooting section
- Review logs:
docker logs <container-id> - Open an issue on GitHub
- Include:
- Docker version:
docker --version - Operating system
- Error messages
- Steps to reproduce
- Docker version: