# docker-dev/ — Mac-friendly four-node fleet for v2 development + manual UI exercise. # # Stack: # sql SQL Server 2022 (ConfigDb backing store) # ldap OpenLDAP with the dev users from C:\publish\glauth\auth.md mirrored in # admin-a OtOpcUa.Host with OTOPCUA_ROLES=admin (cluster seed) # admin-b OtOpcUa.Host with OTOPCUA_ROLES=admin (joins admin-a) # driver-a OtOpcUa.Host with OTOPCUA_ROLES=driver (joins via admin-a) # driver-b OtOpcUa.Host with OTOPCUA_ROLES=driver (joins via admin-a) # traefik Routes :80 to whichever admin-* currently passes /health/active # # Usage: # docker compose -f docker-dev/docker-compose.yml up -d --build # open http://localhost # Blazor admin UI via Traefik # open http://localhost:8080 # Traefik dashboard # # Tear-down: docker compose -f docker-dev/docker-compose.yml down -v name: otopcua-dev services: sql: image: mcr.microsoft.com/mssql/server:2022-latest environment: ACCEPT_EULA: "Y" SA_PASSWORD: "OtOpcUa!Dev123" MSSQL_PID: Developer ports: - "14330:1433" healthcheck: test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'OtOpcUa!Dev123' -No -Q 'SELECT 1' || exit 1"] interval: 10s timeout: 5s retries: 20 ldap: image: bitnami/openldap:2.6 environment: LDAP_ROOT: "dc=lmxopcua,dc=local" LDAP_ADMIN_USERNAME: "admin" LDAP_ADMIN_PASSWORD: "ldapadmin" LDAP_USERS: "alice,bob" LDAP_PASSWORDS: "alice123,bob123" LDAP_USER_DC: "ou=FleetAdmin" ports: - "3893:1389" admin-a: &otopcua-host build: context: .. dockerfile: docker-dev/Dockerfile image: otopcua-host:dev depends_on: sql: { condition: service_healthy } environment: OTOPCUA_ROLES: "admin" ASPNETCORE_URLS: "http://+:9000" ConnectionStrings__ConfigDb: "Server=sql,1433;Database=OtOpcUa;User Id=sa;Password=OtOpcUa!Dev123;TrustServerCertificate=True;" Cluster__Hostname: "0.0.0.0" Cluster__Port: "4053" Cluster__PublicHostname: "admin-a" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__Roles__0: "admin" Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__Server: "ldap" Authentication__Ldap__Port: "1389" Authentication__Ldap__AllowInsecureLdap: "true" admin-b: <<: *otopcua-host environment: OTOPCUA_ROLES: "admin" ASPNETCORE_URLS: "http://+:9000" ConnectionStrings__ConfigDb: "Server=sql,1433;Database=OtOpcUa;User Id=sa;Password=OtOpcUa!Dev123;TrustServerCertificate=True;" Cluster__Hostname: "0.0.0.0" Cluster__Port: "4053" Cluster__PublicHostname: "admin-b" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__Roles__0: "admin" Security__Jwt__SigningKey: "docker-dev-signing-key-with-at-least-32-bytes-of-utf8-content-12345" Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev" Authentication__Ldap__Server: "ldap" Authentication__Ldap__Port: "1389" Authentication__Ldap__AllowInsecureLdap: "true" driver-a: <<: *otopcua-host environment: OTOPCUA_ROLES: "driver" ConnectionStrings__ConfigDb: "Server=sql,1433;Database=OtOpcUa;User Id=sa;Password=OtOpcUa!Dev123;TrustServerCertificate=True;" Cluster__Hostname: "0.0.0.0" Cluster__Port: "4053" Cluster__PublicHostname: "driver-a" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__Roles__0: "driver" ports: - "4840:4840" driver-b: <<: *otopcua-host environment: OTOPCUA_ROLES: "driver" ConnectionStrings__ConfigDb: "Server=sql,1433;Database=OtOpcUa;User Id=sa;Password=OtOpcUa!Dev123;TrustServerCertificate=True;" Cluster__Hostname: "0.0.0.0" Cluster__Port: "4053" Cluster__PublicHostname: "driver-b" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__Roles__0: "driver" ports: - "4841:4840" traefik: image: traefik:v3.1 command: - --entrypoints.web.address=:80 - --providers.file.filename=/etc/traefik/dynamic.yml - --providers.file.watch=true - --api.insecure=true ports: - "80:80" - "8080:8080" volumes: - ./traefik-dynamic.yml:/etc/traefik/dynamic.yml:ro depends_on: - admin-a - admin-b