Docker Infrastructure
Complete Docker Compose setup for the Easy AppServer platform.
Service Overview
Required Services (Core Platform)
| Service | Image | Ports | Purpose |
|---|---|---|---|
| postgres | registry.eacore6.de/de.easy-m.pgsql:example | 5432 | Primary database |
| redis | redis:8.2 | 6379 | Cache, sessions, rate limiting |
| rabbitmq | rabbitmq:4.2-management | 5672, 15672 | Event bus for apps |
| kratos | oryd/kratos:v1.3.0 | 4433, 4434 | Identity & authentication |
| openfga | openfga/openfga:v1.8.6 | 8090, 8091 | Fine-grained authorization |
| otel-collector | otel/opentelemetry-collector-contrib:0.97.0 | 4317, 4318 | Telemetry backend |
| appserver | Custom build | 8080, 9090 | Backend API |
| orchestrator | Custom build | - | Docker container management |
| shell | Custom build | 3000 | Frontend UI |
Optional Services
| Service | Image | Ports | When Needed |
|---|---|---|---|
| hydra | oryd/hydra:v2.2.0 | 4444, 4445 | OAuth2/OIDC flows |
| oathkeeper | oryd/oathkeeper:v0.40.7 | 4455, 4456 | API gateway proxy |
Observability Dashboards (Optional)
| Service | Image | Ports | Purpose |
|---|---|---|---|
| prometheus | prom/prometheus:v2.51.0 | 9090 | Metrics storage |
| loki | grafana/loki:2.9.6 | 3100 | Log aggregation |
| tempo | grafana/tempo:2.4.1 | 3200 | Distributed tracing |
| grafana | grafana/grafana:10.4.1 | 3000 | Visualization |
Service Dependencies
┌─────────────┐
│ PostgreSQL │
│ (5432) │
└──────┬──────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ db-migrate │ │kratos-migrate│ │hydra-migrate│
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ OpenFGA │◀───│ Kratos │ │ Hydra │
│ (8090) │ │ (4433) │ │ (4444) │
└──────┬──────┘ └──────┬──────┘ └─────────────┘
│ │
└────────┬─────────┘
│
▼
┌─────────────┐ ┌─────────────┐
│ AppServer │◀────│ Redis │
│ (8080) │ │ (6379) │
└──────┬──────┘ └─────────────┘
│
▼
┌─────────────┐
│ Shell │
│ (3000) │
└─────────────┘
Startup Order
Services must start in the correct order:
# Phase 1: Database
1. postgres # Must be healthy first
# Phase 2: Database Migrations (run once, exit)
2. db-migrate # Creates databases: kratos, hydra, openfga, appserver
# Phase 3: Auth Service Migrations (run once, exit)
3. kratos-migrate # Runs Kratos schema migrations
4. hydra-migrate # Runs Hydra schema migrations (if using OAuth)
5. openfga-migrate # Runs OpenFGA schema migrations
# Phase 4: Infrastructure Services
6. redis # Cache service
7. rabbitmq # Event bus
8. otel-collector # Telemetry backend
# Phase 5: Auth Services
9. kratos # Identity service
10. hydra # OAuth service (optional)
11. openfga # Authorization service
# Phase 6: Setup Scripts (Docker-exclusive)
12. setup-openfga-roles # Creates OpenFGA store and model (see Manual Setup below for non-Docker)
# Phase 7: Application Services
13. appserver # Backend API
14. orchestrator # Container orchestration
15. shell # Frontend UI
# Phase 8: Dashboards (optional)
16. prometheus # Metrics storage
17. loki # Log storage
18. tempo # Trace storage
19. grafana # Dashboards
Configuration Files
docker/
├── docker-compose.yml # Main compose file
├── .env.example # Environment template
├── .env # Your environment (create from example)
│
├── kratos/
│ └── kratos.yml # Kratos configuration
│
├── hydra/
│ └── hydra.yml # Hydra configuration
│
├── oathkeeper/
│ └── oathkeeper.yml # Oathkeeper rules
│
├── openfga/
│ └── nginx.conf # Playground proxy config
│
├── observability/
│ ├── otel-collector/
│ │ └── otel-config.yml # Collector pipelines
│ ├── prometheus/
│ │ └── prometheus.yml # Scrape targets
│ ├── loki/
│ │ └── loki-config.yml # Log storage config
│ ├── tempo/
│ │ └── tempo-config.yml # Trace storage config
│ └── grafana/
│ └── provisioning/ # Datasources & dashboards
│
├── migrations/
│ └── Dockerfile # Database migration image
│
└── scripts/
├── setup-openfga-roles.sh # OpenFGA initialization (Docker-exclusive)
└── upload-openfga-model.sh # Authorization model upload
OpenFGA Setup
The setup script initializes OpenFGA with the authorization model and default roles.
Automated Setup (Docker)
Script location: docker/scripts/setup-openfga-roles.sh
This script is Docker-exclusive and runs automatically in docker-compose:
# Runs automatically during docker-compose up
# Or run manually:
./docker/scripts/setup-openfga-roles.sh
The script performs:
- Waits for OpenFGA health check
- Creates store named "appserver-platform"
- Updates
docker/.envwithOPENFGA_STORE_ID - Uploads authorization model from
docker/openfga/authorization-model.json - Updates
docker/.envwithOPENFGA_MODEL_ID - Assigns default platform admin role
- Grants permission scopes to roles
Manual Setup (Non-Docker)
If you cannot run the setup script (non-Docker deployment or partial failure), perform these steps manually:
Step 1: Wait for OpenFGA
curl -f ${OPENFGA_API_URL}/healthz
Step 2: Create Store
curl -X POST ${OPENFGA_API_URL}/stores \
-H "Content-Type: application/json" \
-d '{"name": "appserver-platform"}'
# Response: {"id": "01HQXYZ...", "name": "appserver-platform", ...}
# Save the "id" value as OPENFGA_STORE_ID
Step 3: Upload Authorization Model
STORE_ID="<your-store-id>"
curl -X POST ${OPENFGA_API_URL}/stores/${STORE_ID}/authorization-models \
-H "Content-Type: application/json" \
-d @docker/openfga/authorization-model.json
# Response: {"authorization_model_id": "01HQABC..."}
# Save as OPENFGA_MODEL_ID
Step 4: Assign Platform Admin Role
curl -X POST ${OPENFGA_API_URL}/stores/${STORE_ID}/write \
-H "Content-Type: application/json" \
-d '{
"writes": {
"tuple_keys": [{
"user": "role:easy",
"relation": "assignee",
"object": "platform_role:admin"
}]
}
}'
Step 5: Grant Permission Scopes
curl -X POST ${OPENFGA_API_URL}/stores/${STORE_ID}/write \
-H "Content-Type: application/json" \
-d '{
"writes": {
"tuple_keys": [
{"user": "platform_role:admin", "relation": "can_grant", "object": "permission_scope:install_app"},
{"user": "platform_role:admin", "relation": "can_grant", "object": "permission_scope:uninstall_app"}
]
}
}'
Step 6: Update Environment
Add to your environment configuration:
OPENFGA_STORE_ID=<store-id-from-step-2>
OPENFGA_MODEL_ID=<model-id-from-step-3>
Volumes
Persistent data volumes:
| Volume | Service | Purpose |
|---|---|---|
postgres-data | PostgreSQL | Database files |
redis-data | Redis | Cache persistence |
rabbitmq-data | RabbitMQ | Message queue |
kratos-data | Kratos | Session data |
hydra-data | Hydra | OAuth data |
openfga-data | OpenFGA | Auth data |
prometheus-data | Prometheus | Metrics storage |
loki-data | Loki | Log storage |
tempo-data | Tempo | Trace storage |
grafana-data | Grafana | Dashboards |
Backup Critical Volumes
# Backup database
docker exec appserver-postgres pg_dump -U partner partner > backup.sql
# Backup all volumes
docker run --rm -v postgres-data:/data -v $(pwd):/backup \
alpine tar cvf /backup/postgres-data.tar /data
Networks
All services connect to appserver-network:
networks:
appserver-network:
driver: bridge
Service DNS Names
Inside the network, services are reachable by container name:
postgres:5432redis:6379kratos:4433/kratos:4434hydra:4444/hydra:4445openfga:8089(internal) /8090(external)appserver:8080/appserver:9090shell:3000rabbitmq:5672
Starting the Stack
Full Stack
cd docker
# Copy and configure environment
cp .env.example .env
# Edit .env with your values
# Start all services
docker-compose up -d
# Wait for services to be healthy
docker-compose ps
# View logs
docker-compose logs -f
Standard Stack (Required Services)
# Start required services
docker-compose up -d \
postgres \
redis \
rabbitmq \
kratos \
openfga \
otel-collector \
appserver \
orchestrator \
shell
With Dashboards
# Start with dashboards
docker-compose up -d \
postgres redis rabbitmq kratos openfga otel-collector \
appserver orchestrator shell \
prometheus loki tempo grafana
Service Details
PostgreSQL
postgres:
image: registry.eacore6.de/de.easy-m.pgsql:example
ports:
- "5432:5432"
environment:
POSTGRES_USER: partner
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: partner
POSTGRES_HOST_AUTH_METHOD: trust
healthcheck:
test: ["CMD-SHELL", "pg_isready -U partner"]
Creates databases: partner, kratos, hydra, openfga, appserver
Redis
redis:
image: redis:8.2
ports:
- "6379:6379"
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
Kratos (Identity)
kratos:
image: oryd/kratos:v1.3.0
ports:
- "4433:4433" # Public API
- "4434:4434" # Admin API
environment:
DSN: postgres://partner:${POSTGRES_PASSWORD}@postgres:5432/kratos?sslmode=disable
SECRETS_COOKIE: ${KRATOS_SECRETS_COOKIE}
SECRETS_CIPHER: ${KRATOS_SECRETS_CIPHER}
depends_on:
kratos-migrate:
condition: service_completed_successfully
OpenFGA (Authorization)
openfga:
image: openfga/openfga:v1.8.6
ports:
- "8090:8089" # HTTP API (note: internal 8089, external 8090)
- "8091:8081" # gRPC
- "8092:3000" # Playground
environment:
OPENFGA_DATASTORE_ENGINE: postgres
OPENFGA_DATASTORE_URI: postgres://partner:${POSTGRES_PASSWORD}@postgres:5432/openfga?sslmode=disable
OPENFGA_PLAYGROUND_ENABLED: "true"
Note: Ports changed from 8080/8081 to 8090/8091 to avoid conflict with AppServer.
Hydra (OAuth2) - Optional
hydra:
image: oryd/hydra:v2.2.0
ports:
- "4444:4444" # Public API
- "4445:4445" # Admin API
environment:
DSN: postgres://partner:${POSTGRES_PASSWORD}@postgres:5432/hydra?sslmode=disable
URLS_SELF_ISSUER: ${HYDRA_ISSUER_URL}
URLS_CONSENT: ${HYDRA_CONSENT_URL}
URLS_LOGIN: ${HYDRA_LOGIN_URL}
SECRETS_SYSTEM: ${HYDRA_SYSTEM_SECRET}
depends_on:
hydra-migrate:
condition: service_completed_successfully
RabbitMQ (Event Bus) - Optional
rabbitmq:
image: rabbitmq:4.2-management
ports:
- "5672:5672" # AMQP
- "15672:15672" # Management UI
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
Management UI: http://localhost:15672 (guest/guest by default)
AppServer (Backend API)
The AppServer is built from source using a multi-stage Dockerfile.
- Dockerfile:
docker/appserver/Dockerfile - Ports: 8080 (HTTP/GraphQL), 9090 (gRPC)
- Health check:
GET /health
See AppServer Guide for build instructions and configuration.
Note: The
partnerdatabase contains theappserverschema with all AppServer tables.
Health Checks
All services include health checks:
| Service | Health Endpoint |
|---|---|
| PostgreSQL | pg_isready |
| Redis | redis-cli ping |
| Kratos | http://localhost:4433/health/ready |
| Hydra | http://localhost:4444/health/ready |
| OpenFGA | grpc_health_probe -addr=:8081 |
| Oathkeeper | http://localhost:4456/health/ready |
| Prometheus | http://localhost:9090/-/healthy |
| Loki | http://localhost:3100/ready |
| Tempo | http://localhost:3200/ready |
| Grafana | http://localhost:3000/api/health |
Check all services:
docker-compose ps
Common Operations
View Logs
# All services
docker-compose logs -f
# Specific service
docker-compose logs -f appserver
# Last 100 lines
docker-compose logs --tail=100 appserver
Restart Service
docker-compose restart appserver
Stop Stack
# Stop all
docker-compose down
# Stop and remove volumes (DELETES DATA!)
docker-compose down -v
Rebuild Service
docker-compose build appserver
docker-compose up -d appserver
Shell into Container
docker-compose exec postgres psql -U partner
docker-compose exec redis redis-cli
docker-compose exec appserver sh
Port Reference
| Port | Service | Protocol | Description |
|---|---|---|---|
| 5432 | PostgreSQL | TCP | Database |
| 6379 | Redis | TCP | Cache |
| 5672 | RabbitMQ | AMQP | Message queue |
| 15672 | RabbitMQ | HTTP | Management UI |
| 4433 | Kratos | HTTP | Public API |
| 4434 | Kratos | HTTP | Admin API |
| 4444 | Hydra | HTTP | Public API |
| 4445 | Hydra | HTTP | Admin API |
| 4455 | Oathkeeper | HTTP | Proxy |
| 4456 | Oathkeeper | HTTP | API |
| 8080 | AppServer | HTTP | REST/GraphQL |
| 9090 | AppServer | gRPC | App services |
| 8090 | OpenFGA | HTTP | API |
| 8091 | OpenFGA | gRPC | API |
| 8092 | OpenFGA | HTTP | Playground |
| 3000 | Shell | HTTP | Frontend |
| 3000 | Grafana | HTTP | Dashboards |
| 4317 | OTEL Collector | gRPC | Telemetry |
| 4318 | OTEL Collector | HTTP | Telemetry |
| 9090 | Prometheus | HTTP | Metrics |
| 3100 | Loki | HTTP | Logs |
| 3200 | Tempo | HTTP | Traces |
Port Conflicts:
- Shell (3000) and Grafana (3000) - Use different ports in production
- AppServer gRPC (9090) and Prometheus (9090) - Different services, no conflict
Troubleshooting
Service Won't Start
# Check logs
docker-compose logs service-name
# Check health
docker-compose ps
# Restart failed service
docker-compose restart service-name
Database Connection Issues
# Check PostgreSQL is running
docker-compose exec postgres pg_isready
# Check database exists
docker-compose exec postgres psql -U partner -l
# Check connectivity from another service
docker-compose exec appserver nc -zv postgres 5432
Network Issues
# Check network exists
docker network ls | grep appserver
# Inspect network
docker network inspect docker_appserver-network
# Check DNS resolution
docker-compose exec shell nslookup appserver
Volume Issues
# List volumes
docker volume ls | grep docker_
# Inspect volume
docker volume inspect docker_postgres-data
# Remove all unused volumes (CAREFUL!)
docker volume prune
Related Topics
- AppServer Guide - Backend deployment
- Orchestrator Guide - Container management
- Shell Guide - Frontend deployment
- Environment Reference - All environment variables
- Production Deployment - Production setup
- Observability Stack - Monitoring configuration