A complete, production-ready implementation of a hub-and-spoke distributed architecture using Docker Compose. This architecture separates the central coordination layer (Hub) from the execution services (Spokes), enabling scalability, flexibility, and fault isolation.
The Hub acts as the central nervous system, coordinating all communication and orchestration:
- Hub Gateway (NGINX): Entry point for all external traffic, load balancing, and SSL termination
- Redis Broker: High-speed message broker for real-time communication and caching
- RabbitMQ Broker: Message queue for asynchronous task distribution and event streaming
- Hub Orchestrator: Central brain that routes requests, manages state, and coordinates spoke services
The Spokes are independent services that perform specific tasks:
- Authentication Service (Node.js): User authentication, JWT token management
- User Service (Node.js): User profile management, CRUD operations
- Notification Service (Node.js): Email, SMS, and push notification delivery
- Analytics Service (Python): Data processing, metrics aggregation, reporting
- Storage Service (MinIO): Object storage for files, images, and documents
- Search Service (Elasticsearch): Full-text search and indexing
- PostgreSQL: Relational database for structured data
- Prometheus: Metrics collection and monitoring
- Grafana: Visualization dashboards and alerting
✅ Centralized Control: All routing and orchestration logic in one place
✅ Independent Scaling: Scale spokes independently based on load
✅ Fault Isolation: Spoke failures don't cascade to other services
✅ Flexible Communication: Synchronous (HTTP) and asynchronous (message queue) patterns
✅ Easy Monitoring: Centralized metrics and logging
✅ Service Discovery: Hub maintains registry of all active spokes
- Separation of Concerns: Hub (brain) vs. Spokes (hands)
- Loose Coupling: Services communicate only through the hub
- High Availability: Health checks and automatic restarts
- Security: Network isolation, environment-based secrets
- Observability: Prometheus metrics + Grafana dashboards
- Docker Engine 20.10+
- Docker Compose 2.0+
- At least 4GB RAM available
- Ports 80, 443, 3000-3010, 5432, 6379, 9000-9001, 9090, 9200, 15672 available
# Create project directory
mkdir hub-spoke-architecture && cd hub-spoke-architecture
# Copy the docker-compose.yml file to this directory
# Create environment file
cp .env.example .env
# Edit .env with your secure passwords
nano .env# Hub configuration
mkdir -p hub-config/ssl
mkdir -p hub-orchestrator
# Spoke services
mkdir -p spokes/auth-service
mkdir -p spokes/user-service
mkdir -p spokes/notification-service
mkdir -p spokes/analytics-service
# Monitoring configuration
mkdir -p monitoring/grafana/{dashboards,datasources}
# Database initialization scripts
mkdir -p init-scriptsCreate hub-config/nginx.conf:
events {
worker_connections 1024;
}
http {
upstream hub_orchestrator {
server hub-orchestrator:3000;
}
upstream spoke_auth {
server spoke-auth:3000;
}
upstream spoke_user {
server spoke-user:3000;
}
upstream spoke_notification {
server spoke-notification:3000;
}
server {
listen 80;
server_name localhost;
# Health check endpoint
location /health {
return 200 "OK\n";
add_header Content-Type text/plain;
}
# Hub orchestrator
location /api/hub/ {
proxy_pass http://hub_orchestrator/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Authentication service
location /api/auth/ {
proxy_pass http://spoke_auth/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# User service
location /api/users/ {
proxy_pass http://spoke_user/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Notification service
location /api/notifications/ {
proxy_pass http://spoke_notification/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}Create init-scripts/create-multiple-databases.sh:
#!/bin/bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
CREATE DATABASE auth_db;
CREATE DATABASE user_db;
CREATE DATABASE analytics_db;
EOSQLMake it executable:
chmod +x init-scripts/create-multiple-databases.shCreate monitoring/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'hub-orchestrator'
static_configs:
- targets: ['hub-orchestrator:3000']
- job_name: 'spoke-auth'
static_configs:
- targets: ['spoke-auth:3000']
- job_name: 'spoke-user'
static_configs:
- targets: ['spoke-user:3000']
- job_name: 'spoke-notification'
static_configs:
- targets: ['spoke-notification:3000']
- job_name: 'spoke-analytics'
static_configs:
- targets: ['spoke-analytics:8000']# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Check service status
docker-compose ps# Check hub gateway
curl http://localhost/health
# Check Redis
docker exec redis-broker redis-cli -a changeme ping
# Check RabbitMQ management UI
open http://localhost:15672 # admin/changeme
# Check Grafana
open http://localhost:3010 # admin/admin
# Check MinIO console
open http://localhost:9001 # minioadmin/minioadmin| Service | Port | Endpoint | Description |
|---|---|---|---|
| Hub Gateway | 80 | http://localhost | Main entry point |
| Hub Orchestrator | 3000 | http://localhost:3000 | Central coordinator |
| Auth Service | 3001 | http://localhost:3001 | Authentication |
| User Service | 3002 | http://localhost:3002 | User management |
| Notification Service | 3003 | http://localhost:3003 | Notifications |
| Analytics Service | 3004 | http://localhost:3004 | Analytics |
| PostgreSQL | 5432 | localhost:5432 | Database |
| Redis | 6379 | localhost:6379 | Cache/Broker |
| RabbitMQ | 5672 | localhost:5672 | Message Queue |
| RabbitMQ UI | 15672 | http://localhost:15672 | Queue Management |
| MinIO | 9000 | http://localhost:9000 | Object Storage |
| MinIO Console | 9001 | http://localhost:9001 | Storage UI |
| Elasticsearch | 9200 | http://localhost:9200 | Search Engine |
| Prometheus | 9090 | http://localhost:9090 | Metrics |
| Grafana | 3010 | http://localhost:3010 | Dashboards |
Client Request
↓
Hub Gateway (NGINX)
↓
Hub Orchestrator (Brain)
↓
Message Broker (Redis/RabbitMQ)
↓
Spoke Services (Hands)
↓
Database/Storage
- Request Routing: Determine which spoke should handle each request
- Load Balancing: Distribute work across multiple spoke instances
- State Management: Maintain session state and context
- Service Discovery: Track available spokes and their health
- Error Handling: Retry logic, circuit breaking, fallback strategies
- Authentication: Validate tokens before routing to spokes
- Rate Limiting: Protect spokes from overload
- Logging & Metrics: Centralized observability
- Task Execution: Perform specific business logic
- Data Processing: CRUD operations, transformations
- External Integration: Call third-party APIs
- Event Publishing: Notify hub of state changes
- Health Reporting: Regular health checks to hub
- Stateless Operation: No direct spoke-to-spoke communication
# Scale notification service to 3 instances
docker-compose up -d --scale spoke-notification=3
# Scale analytics service to 2 instances
docker-compose up -d --scale spoke-analytics=2- Define new service in
docker-compose.yml - Connect to
hub-network - Configure to communicate with
hub-orchestrator - Add route in
hub-config/nginx.conf - Restart gateway:
docker-compose restart hub-gateway
open http://localhost:9090Example queries:
up{role="spoke"}- Check spoke healthhttp_requests_total- Total requestshttp_request_duration_seconds- Request latency
open http://localhost:3010- Login with admin/admin
- Add Prometheus datasource: http://prometheus:9090
- Import dashboards for each service
# All services
docker-compose logs -f
# Specific service
docker-compose logs -f hub-orchestrator
# Filter by role
docker-compose logs -f $(docker-compose ps -q | xargs docker inspect -f '{{if eq (index .Config.Labels "role") "spoke"}}{{.Name}}{{end}}')- Change Default Passwords: Update all passwords in
.env - Use SSL/TLS: Configure SSL certificates in
hub-config/ssl/ - Network Isolation: Spokes can't communicate directly
- Secret Management: Use Docker secrets in production
- Least Privilege: Each service has minimal permissions
- Regular Updates: Keep base images updated
# Check logs
docker-compose logs <service-name>
# Verify network connectivity
docker-compose exec hub-orchestrator ping spoke-auth
# Check environment variables
docker-compose config# Test PostgreSQL connection
docker-compose exec postgres-db psql -U postgres -c "SELECT 1"
# Check database exists
docker-compose exec postgres-db psql -U postgres -l# Test Redis connection
docker-compose exec redis-broker redis-cli -a changeme ping
# Check Redis info
docker-compose exec redis-broker redis-cli -a changeme info- Use Docker Swarm or Kubernetes for orchestration
- External Load Balancer instead of NGINX container
- Managed Databases (AWS RDS, Google Cloud SQL)
- Secrets Management (HashiCorp Vault, AWS Secrets Manager)
- Centralized Logging (ELK Stack, Splunk)
- Auto-scaling based on metrics
- Backup Strategy for databases and volumes
# Development
docker-compose -f docker-compose.yml up
# Staging
docker-compose -f docker-compose.yml -f docker-compose.staging.yml up
# Production
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up# Backup PostgreSQL
docker-compose exec postgres-db pg_dumpall -U postgres > backup.sql
# Backup Redis
docker-compose exec redis-broker redis-cli -a changeme --rdb /data/dump.rdb# Pull latest images
docker-compose pull
# Recreate services
docker-compose up -d --force-recreate# Stop all services
docker-compose down
# Remove volumes (WARNING: deletes all data)
docker-compose down -v
# Remove images
docker-compose down --rmi allTo add a new spoke service:
- Create service directory in
spokes/ - Add service definition to
docker-compose.yml - Connect to
hub-networkandspoke-network - Configure environment variables
- Add route to
hub-config/nginx.conf - Update this README
MIT License - feel free to use and modify for your projects.
For issues and questions:
- Check logs:
docker-compose logs -f - Verify configuration:
docker-compose config - Review health checks:
docker-compose ps