Docker Development
β±οΈ Time: 15 minutes | π― Goal: Run complete containerized stack locally
Run all 5 services (UI, API, 3 MCP servers) in Docker containers with Azure authentication.
Prerequisites
Required:
- Docker Desktop installed and running
- Azure CLI installed (az --version)
- Azure logged in (az login)
- Azure AI Foundry project with GPT-4 model deployed
- Contributor or Owner role on Azure subscription (to create service principal)
Check before starting:
# Verify Docker
docker --version
docker ps # Should not error
# Verify Azure CLI
az --version
az login
az account show
Quick Start
1. Create Service Principal
This project uses service principal authentication for Docker containers (not Azure CLI credentials):
Why service principal instead of Azure CLI?
- Smaller images (no +500MB Azure CLI binary needed)
- Least-privilege security (not your broad Owner role)
- Production-aligned (same pattern as Managed Identity)
- Cross-platform (no .azure folder mounts)
See: ADR-038: Service Principal Least Privilege for full rationale.
What this does:
- Creates service principal: loan-defenders-docker-dev
- Assigns "Azure AI User" role (includes inference + agent creation in AI Foundry)
- Adds credentials to .env automatically
Expected output:
Note: Service principal credentials are automatically added to your .env file.
2. Configure Environment
# Clone repository (if not already done)
git clone https://github.com/niksacdev/loan-defenders.git
cd loan-defenders
# Create .env file (if not already done)
cp .env.example .env
# Edit .env - Verify these were added by script:
# AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# AZURE_CLIENT_SECRET=xxxxx~...
# AZURE_AI_PROJECT_ENDPOINT=https://your-resource.services.ai.azure.com/api/projects/your-project
# AZURE_AI_MODEL_DEPLOYMENT_NAME=your-deployment-name
3. Configure MCP Server URLs
Important: Update MCP server URLs for Docker networking:
# In .env, use Docker container names (NOT localhost)
MCP_APPLICATION_VERIFICATION_URL=http://loan-defenders-mcp-app-verification:8010/mcp
MCP_DOCUMENT_PROCESSING_URL=http://loan-defenders-mcp-doc-processing:8011/mcp
MCP_FINANCIAL_CALCULATIONS_URL=http://loan-defenders-mcp-financial-calc:8012/mcp
Why? Inside Docker containers, localhost refers to the container itself. Containers communicate via Docker network using service names.
4. Start Services
# Build and start all containers
./scripts/docker-dev.sh up
# Or manually:
docker-compose build
docker-compose up -d
β±οΈ First time: 5-8 minutes (image builds)
β±οΈ Subsequent: 30-40 seconds (health checks)
Services started:
- loan-defenders-ui (port 8080)
- loan-defenders-api (port 8000)
- loan-defenders-mcp-app-verification (port 8010)
- loan-defenders-mcp-doc-processing (port 8011)
- loan-defenders-mcp-financial-calc (port 8012)
5. Verify Deployment
# Check container status
docker-compose ps
# Should see all 5 containers "Up" and "healthy"
# Open in browser
open http://localhost:8080 # macOS
# or navigate to http://localhost:8080
Test it: 1. Click "Start Your Loan Application" 2. Fill in test data 3. Submit and watch agents process 4. Verify loan decision (30-60 seconds)
Managing Containers
# View logs (all services)
docker-compose logs -f
# View logs (specific service)
docker-compose logs -f api
docker-compose logs -f ui
# Check container health
docker-compose ps
# Restart single service
docker-compose restart api
# Stop all services
docker-compose down
# Stop and remove volumes
docker-compose down -v
Helper script commands:
# Start
./scripts/docker-dev.sh up
# View logs
./scripts/docker-dev.sh logs
./scripts/docker-dev.sh logs api # Specific service
# Stop
./scripts/docker-dev.sh down
# Rebuild after code changes
./scripts/docker-dev.sh rebuild
# Complete reset
./scripts/docker-dev.sh clean
Development Workflow
Code Changes
After changing code:
# Stop containers
docker-compose down
# Rebuild specific service
docker-compose build api
# Or rebuild all
docker-compose build
# Restart
docker-compose up -d
Faster rebuild:
Testing Container Connectivity
# Test API from host
curl http://localhost:8000/health
# Test MCP servers from host
curl http://localhost:8010/health
curl http://localhost:8011/health
curl http://localhost:8012/health
# Test MCP connectivity from API container
docker exec loan-defenders-api curl http://loan-defenders-mcp-app-verification:8010/health
Authentication
Service principal authentication for containers:
This project uses service principal authentication for Docker containers (environment variables from .env).
How it works:
1. You run ./scripts/create-service-principal.sh
2. Script creates service principal with least-privilege role
3. Credentials added to .env file
4. Docker containers read from .env:
- AZURE_TENANT_ID
- AZURE_CLIENT_ID
- AZURE_CLIENT_SECRET
5. DefaultAzureCredential uses these environment variables
Why service principal?
- Smaller images (no Azure CLI binary needed)
- Least-privilege role (inference only)
- Same pattern as production (environment variables)
- Script automates creation and .env setup
Alternative: Azure CLI mount is possible (requires .azure folder mount and CLI binary in containers, +500MB per image). This project chose service principal for simplicity and security. See ADR-038.
Verify authentication:
# Check API logs for successful auth
docker-compose logs api | grep -i "authentication"
# Should see: "Using EnvironmentCredential for authentication"
Troubleshooting
Container Exits Immediately
Check logs:
Common causes:
- Missing .env file β Copy from .env.example
- Invalid Azure credentials β Check .env values
- Missing environment variables β Compare with .env.example
- Build failed β Run docker-compose build --no-cache api
Agent Processing Hangs
Error: Stuck at "Intake Agent" or agents not progressing
Cause: MCP server URLs pointing to localhost instead of Docker container names
Fix:
# Check .env uses container names (NOT localhost)
grep MCP_ .env
# Should show:
# MCP_APPLICATION_VERIFICATION_URL=http://loan-defenders-mcp-app-verification:8010/mcp
# NOT: http://localhost:8010/mcp
# After fixing .env, restart:
docker-compose restart api
Port Already in Use
Error: port is already allocated
# Find what's using the port
lsof -i :8000 # or :8080, :8010, etc.
# Kill the process
kill -9 <PID>
# Or change port in docker-compose.yml:
ports:
- "8001:8000" # External:Internal
Authentication Fails
Error: DefaultAzureCredential failed to retrieve a token
Cause: Service principal credentials missing or incorrect in .env
Fix:
# Verify .env has service principal credentials
cat .env | grep AZURE_
# Should show:
# AZURE_TENANT_ID=xxxxxxxx-...
# AZURE_CLIENT_ID=xxxxxxxx-...
# AZURE_CLIENT_SECRET=xxxxx~...
# AZURE_AI_PROJECT_ENDPOINT=https://...
# AZURE_AI_MODEL_DEPLOYMENT_NAME=...
# If missing, re-run service principal script:
./scripts/create-service-principal.sh
# Restart containers
docker-compose restart
Connection Refused
Error: UI can't connect to API
Check:
# 1. Verify API is running
docker-compose ps api
# 2. Test API endpoint
curl http://localhost:8000/health
# 3. Check API logs
docker-compose logs api
# 4. Verify CORS in .env
grep CORS .env
# Should include: http://localhost:8080
Clean Up
Stop Services
Complete Reset
# Remove containers, images, volumes
docker-compose down -v
docker system prune -af
# Rebuild from scratch
docker-compose build --no-cache
docker-compose up -d
What's Deployed
βββββββββββββββββββββββββββββββββββββββββββ
β Docker Container Network β
β β
β ββββββββββββ ββββββββββββ β
β β UI ββββΆβ API β β
β β :8080 β β :8000 β β
β ββββββββββββ ββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββ β
β β MCP Servers β β
β β :8010-8012 β β
β ββββββββββββββββ β
β β β
β βΌ β
β Azure AI Foundry β
β (via Service Principal) β
βββββββββββββββββββββββββββββββββββββββββββ
Container networking:
- Containers use Docker's internal network
- Internal communication: service names (e.g., loan-defenders-api)
- External access: localhost (ports mapped by docker-compose)
Why Docker?
Use Docker when: - Testing full system integration - Validating production-like environment - Need reproducible builds - Testing deployment before Azure
Use local dev when: - Daily coding (faster iteration) - Debugging with IDE - Hot-reload development
What's Next?
Deploy to Azure: - Azure Deployment - Production deployment
Learn the System: - System Architecture - How it works - Docker Architecture - Container design
Development: - Local Development - Faster iteration
Need Help? - Troubleshooting Guide - Common issues - GitHub Discussions - Ask questions
π Docker environment ready! All services containerized and tested.