Docker Deployment
Deploy ERA Agent in Docker containers for easy portability, consistent environments, and simplified deployment across different platforms.
Quick Start
Section titled “Quick Start”Build the Image
Section titled “Build the Image”cd era-agentdocker build -t era-agent .Run the Container
Section titled “Run the Container”docker run -d \ --name era-agent \ -p 8787:8787 \ --privileged \ era-agentTest It
Section titled “Test It”curl http://localhost:8787/healthDockerfile
Section titled “Dockerfile”Create Dockerfile in the era-agent directory:
FROM golang:1.21-alpine AS builder
WORKDIR /build
# Install build dependenciesRUN apk add --no-cache git make
# Copy go mod filesCOPY go.mod go.sum ./RUN go mod download
# Copy source codeCOPY . .
# Build the binaryRUN CGO_ENABLED=0 GOOS=linux go build -o agent
FROM alpine:latest
# Install runtime dependenciesRUN apk add --no-cache \ ca-certificates \ docker-cli
# Create non-root userRUN addgroup -g 1000 era-agent && \ adduser -D -u 1000 -G era-agent era-agent
WORKDIR /app
# Copy binary from builderCOPY --from=builder /build/agent /app/agent
# Create state directoryRUN mkdir -p /var/lib/era-agent && \ chown era-agent:era-agent /var/lib/era-agent
# Switch to non-root userUSER era-agent
# Expose HTTP portEXPOSE 8787
# Environment variablesENV PORT=8787 \ AGENT_MODE=http \ AGENT_LOG_LEVEL=info \ AGENT_STATE_DIR=/var/lib/era-agent
# Health checkHEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:8787/health || exit 1
# Start the agentCMD ["/app/agent", "serve"]Docker Compose
Section titled “Docker Compose”Basic Setup
Section titled “Basic Setup”Create docker-compose.yml:
version: '3.8'
services: era-agent: image: era-agent:latest build: . container_name: era-agent ports: - "8787:8787" environment: - PORT=8787 - AGENT_LOG_LEVEL=info - AGENT_STATE_DIR=/var/lib/era-agent volumes: - era-agent-state:/var/lib/era-agent - /var/run/docker.sock:/var/run/docker.sock restart: unless-stopped privileged: true
volumes: era-agent-state: driver: localStart services:
docker-compose up -dWith Nginx Reverse Proxy
Section titled “With Nginx Reverse Proxy”version: '3.8'
services: era-agent: image: era-agent:latest build: . environment: - PORT=8787 - AGENT_LOG_LEVEL=info volumes: - era-agent-state:/var/lib/era-agent - /var/run/docker.sock:/var/run/docker.sock restart: unless-stopped privileged: true networks: - internal
nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./certs:/etc/nginx/certs:ro depends_on: - era-agent restart: unless-stopped networks: - internal
volumes: era-agent-state:
networks: internal:Nginx configuration (nginx.conf):
events { worker_connections 1024;}
http { upstream era_agent { server era-agent:8787; }
server { listen 80; server_name era-agent.example.com;
location / { proxy_pass http://era_agent; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_read_timeout 300s; } }}With Monitoring
Section titled “With Monitoring”version: '3.8'
services: era-agent: image: era-agent:latest build: . ports: - "8787:8787" volumes: - era-agent-state:/var/lib/era-agent - /var/run/docker.sock:/var/run/docker.sock restart: unless-stopped privileged: true logging: driver: "json-file" options: max-size: "10m" max-file: "3"
prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' restart: unless-stopped
grafana: image: grafana/grafana:latest ports: - "3000:3000" volumes: - grafana-data:/var/lib/grafana environment: - GF_SECURITY_ADMIN_PASSWORD=admin restart: unless-stopped
volumes: era-agent-state: prometheus-data: grafana-data:Configuration
Section titled “Configuration”Environment Variables
Section titled “Environment Variables”# Run with custom configurationdocker run -d \ --name era-agent \ -p 8787:8787 \ -e PORT=8787 \ -e AGENT_LOG_LEVEL=debug \ -e AGENT_STATE_DIR=/var/lib/era-agent \ --privileged \ era-agentVolume Mounts
Section titled “Volume Mounts”Persist state across container restarts:
docker run -d \ --name era-agent \ -p 8787:8787 \ -v era-agent-state:/var/lib/era-agent \ -v /var/run/docker.sock:/var/run/docker.sock \ --privileged \ era-agentResource Limits
Section titled “Resource Limits”Limit container resources:
docker run -d \ --name era-agent \ -p 8787:8787 \ --cpus="2.0" \ --memory="2g" \ --privileged \ era-agentDocker Compose:
services: era-agent: image: era-agent:latest deploy: resources: limits: cpus: '2.0' memory: 2G reservations: cpus: '1.0' memory: 512MNetworking
Section titled “Networking”Host Network Mode
Section titled “Host Network Mode”Use host networking for better performance:
docker run -d \ --name era-agent \ --network host \ --privileged \ era-agentCustom Network
Section titled “Custom Network”Create isolated network:
# Create networkdocker network create era-network
# Run containerdocker run -d \ --name era-agent \ --network era-network \ -p 8787:8787 \ --privileged \ era-agentSecurity
Section titled “Security”Read-Only Root Filesystem
Section titled “Read-Only Root Filesystem”docker run -d \ --name era-agent \ -p 8787:8787 \ --read-only \ --tmpfs /tmp:rw,noexec,nosuid,size=100m \ -v era-agent-state:/var/lib/era-agent:rw \ --privileged \ era-agentDrop Capabilities
Section titled “Drop Capabilities”docker run -d \ --name era-agent \ -p 8787:8787 \ --cap-drop=ALL \ --cap-add=NET_BIND_SERVICE \ --privileged \ era-agentUser Namespaces
Section titled “User Namespaces”Enable user namespace remapping in Docker daemon (/etc/docker/daemon.json):
{ "userns-remap": "default"}Then restart Docker:
sudo systemctl restart dockerMulti-Stage Builds
Section titled “Multi-Stage Builds”Optimize image size with multi-stage builds:
# Build stageFROM golang:1.21-alpine AS builderWORKDIR /buildCOPY . .RUN go build -o agent -ldflags="-s -w" .
# Runtime stageFROM alpine:latestRUN apk add --no-cache ca-certificates docker-cliCOPY --from=builder /build/agent /app/agentWORKDIR /appEXPOSE 8787CMD ["/app/agent", "serve"]Benefits:
- Smaller final image (no build tools)
- Faster deployments
- Reduced attack surface
Registry and Distribution
Section titled “Registry and Distribution”Push to Docker Hub
Section titled “Push to Docker Hub”# Tag imagedocker tag era-agent:latest yourusername/era-agent:latestdocker tag era-agent:latest yourusername/era-agent:v1.0.0
# Push to Docker Hubdocker logindocker push yourusername/era-agent:latestdocker push yourusername/era-agent:v1.0.0Private Registry
Section titled “Private Registry”# Tag for private registrydocker tag era-agent:latest registry.example.com/era-agent:latest
# Push to private registrydocker push registry.example.com/era-agent:latest
# Pull on other machinesdocker pull registry.example.com/era-agent:latestGitHub Container Registry
Section titled “GitHub Container Registry”# Login to GitHubecho $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Tag and pushdocker tag era-agent:latest ghcr.io/username/era-agent:latestdocker push ghcr.io/username/era-agent:latestCloud Deployment
Section titled “Cloud Deployment”AWS ECS
Section titled “AWS ECS”Create task definition:
{ "family": "era-agent", "containerDefinitions": [ { "name": "era-agent", "image": "yourusername/era-agent:latest", "portMappings": [ { "containerPort": 8787, "protocol": "tcp" } ], "environment": [ { "name": "PORT", "value": "8787" }, { "name": "AGENT_LOG_LEVEL", "value": "info" } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/era-agent", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "ecs" } } } ], "requiresCompatibilities": ["FARGATE"], "networkMode": "awsvpc", "cpu": "256", "memory": "512"}Google Cloud Run
Section titled “Google Cloud Run”# Build and push to Google Container Registrygcloud builds submit --tag gcr.io/PROJECT_ID/era-agent
# Deploy to Cloud Rungcloud run deploy era-agent \ --image gcr.io/PROJECT_ID/era-agent \ --platform managed \ --region us-central1 \ --allow-unauthenticated \ --port 8787Azure Container Instances
Section titled “Azure Container Instances”# Create resource groupaz group create --name era-agent-rg --location eastus
# Deploy containeraz container create \ --resource-group era-agent-rg \ --name era-agent \ --image yourusername/era-agent:latest \ --dns-name-label era-agent \ --ports 8787DigitalOcean App Platform
Section titled “DigitalOcean App Platform”Create app.yaml:
name: era-agentservices: - name: era-agent image: registry_type: DOCKER_HUB registry: yourusername repository: era-agent tag: latest http_port: 8787 instance_count: 1 instance_size_slug: basic-xs envs: - key: AGENT_LOG_LEVEL value: "info"Deploy:
doctl apps create --spec app.yamlKubernetes
Section titled “Kubernetes”Deployment
Section titled “Deployment”Create kubernetes/deployment.yaml:
apiVersion: apps/v1kind: Deploymentmetadata: name: era-agentspec: replicas: 3 selector: matchLabels: app: era-agent template: metadata: labels: app: era-agent spec: containers: - name: era-agent image: yourusername/era-agent:latest ports: - containerPort: 8787 env: - name: PORT value: "8787" - name: AGENT_LOG_LEVEL value: "info" resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 8787 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /health port: 8787 initialDelaySeconds: 5 periodSeconds: 10---apiVersion: v1kind: Servicemetadata: name: era-agentspec: selector: app: era-agent ports: - protocol: TCP port: 80 targetPort: 8787 type: LoadBalancerDeploy:
kubectl apply -f kubernetes/deployment.yamlMonitoring and Logging
Section titled “Monitoring and Logging”View Logs
Section titled “View Logs”# Follow logsdocker logs -f era-agent
# Last 100 linesdocker logs --tail 100 era-agent
# Logs since 1 hour agodocker logs --since 1h era-agentContainer Stats
Section titled “Container Stats”# Real-time statsdocker stats era-agent
# Format outputdocker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"Health Checks
Section titled “Health Checks”# Manual health checkdocker exec era-agent wget -q -O- http://localhost:8787/health
# Check container healthdocker inspect --format='{{.State.Health.Status}}' era-agentTroubleshooting
Section titled “Troubleshooting”Container Won’t Start
Section titled “Container Won’t Start”# Check logsdocker logs era-agent
# Check eventsdocker events --filter container=era-agent
# Run interactivelydocker run -it --rm era-agent /bin/shPermission Issues
Section titled “Permission Issues”# Run as root (not recommended for production)docker run -d --user root era-agent
# Fix volume permissionsdocker run --rm -v era-agent-state:/data alpine chmod -R 777 /dataNetworking Issues
Section titled “Networking Issues”# Check exposed portsdocker port era-agent
# Inspect networkdocker network inspect bridge
# Test connectivitydocker exec era-agent ping -c 3 google.comDocker Socket Access
Section titled “Docker Socket Access”If ERA Agent needs Docker access:
# Mount Docker socketdocker run -d \ -v /var/run/docker.sock:/var/run/docker.sock \ --group-add $(getent group docker | cut -d: -f3) \ era-agentBest Practices
Section titled “Best Practices”- Use specific tags: Don’t rely on
latest - Multi-stage builds: Minimize image size
- Health checks: Always define healthchecks
- Resource limits: Set CPU and memory limits
- Secrets management: Use Docker secrets or environment files
- Logging: Configure proper log drivers
- Updates: Regularly update base images
- Scanning: Scan images for vulnerabilities
Next Steps
Section titled “Next Steps”- HTTP Server Mode - API integration
- CLI Usage - Command-line interface
- MCP Server - Claude Desktop integration
- API Reference - Complete API documentation