Skip to main content

Docker Infrastructure

Complete Docker Compose setup for the Easy AppServer platform.

Service Overview

Required Services (Core Platform)

ServiceImagePortsPurpose
postgresregistry.eacore6.de/de.easy-m.pgsql:example5432Primary database
redisredis:8.26379Cache, sessions, rate limiting
rabbitmqrabbitmq:4.2-management5672, 15672Event bus for apps
kratosoryd/kratos:v1.3.04433, 4434Identity & authentication
openfgaopenfga/openfga:v1.8.68090, 8091Fine-grained authorization
otel-collectorotel/opentelemetry-collector-contrib:0.97.04317, 4318Telemetry backend
appserverCustom build8080, 9090Backend API
orchestratorCustom build-Docker container management
shellCustom build3000Frontend UI

Optional Services

ServiceImagePortsWhen Needed
hydraoryd/hydra:v2.2.04444, 4445OAuth2/OIDC flows
oathkeeperoryd/oathkeeper:v0.40.74455, 4456API gateway proxy

Observability Dashboards (Optional)

ServiceImagePortsPurpose
prometheusprom/prometheus:v2.51.09090Metrics storage
lokigrafana/loki:2.9.63100Log aggregation
tempografana/tempo:2.4.13200Distributed tracing
grafanagrafana/grafana:10.4.13000Visualization

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:

  1. Waits for OpenFGA health check
  2. Creates store named "appserver-platform"
  3. Updates docker/.env with OPENFGA_STORE_ID
  4. Uploads authorization model from docker/openfga/authorization-model.json
  5. Updates docker/.env with OPENFGA_MODEL_ID
  6. Assigns default platform admin role
  7. 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:

VolumeServicePurpose
postgres-dataPostgreSQLDatabase files
redis-dataRedisCache persistence
rabbitmq-dataRabbitMQMessage queue
kratos-dataKratosSession data
hydra-dataHydraOAuth data
openfga-dataOpenFGAAuth data
prometheus-dataPrometheusMetrics storage
loki-dataLokiLog storage
tempo-dataTempoTrace storage
grafana-dataGrafanaDashboards

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:5432
  • redis:6379
  • kratos:4433 / kratos:4434
  • hydra:4444 / hydra:4445
  • openfga:8089 (internal) / 8090 (external)
  • appserver:8080 / appserver:9090
  • shell:3000
  • rabbitmq: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.

See AppServer Guide for build instructions and configuration.

Note: The partner database contains the appserver schema with all AppServer tables.

Health Checks

All services include health checks:

ServiceHealth Endpoint
PostgreSQLpg_isready
Redisredis-cli ping
Kratoshttp://localhost:4433/health/ready
Hydrahttp://localhost:4444/health/ready
OpenFGAgrpc_health_probe -addr=:8081
Oathkeeperhttp://localhost:4456/health/ready
Prometheushttp://localhost:9090/-/healthy
Lokihttp://localhost:3100/ready
Tempohttp://localhost:3200/ready
Grafanahttp://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

PortServiceProtocolDescription
5432PostgreSQLTCPDatabase
6379RedisTCPCache
5672RabbitMQAMQPMessage queue
15672RabbitMQHTTPManagement UI
4433KratosHTTPPublic API
4434KratosHTTPAdmin API
4444HydraHTTPPublic API
4445HydraHTTPAdmin API
4455OathkeeperHTTPProxy
4456OathkeeperHTTPAPI
8080AppServerHTTPREST/GraphQL
9090AppServergRPCApp services
8090OpenFGAHTTPAPI
8091OpenFGAgRPCAPI
8092OpenFGAHTTPPlayground
3000ShellHTTPFrontend
3000GrafanaHTTPDashboards
4317OTEL CollectorgRPCTelemetry
4318OTEL CollectorHTTPTelemetry
9090PrometheusHTTPMetrics
3100LokiHTTPLogs
3200TempoHTTPTraces

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