# ── Stage 1: Build React frontend ──────────────────────────────────────────── FROM node:20-alpine AS frontend-build WORKDIR /app/ClientApp COPY OTSSignsOrchestrator/ClientApp/package.json OTSSignsOrchestrator/ClientApp/package-lock.json* ./ RUN npm ci COPY OTSSignsOrchestrator/ClientApp/ ./ # Build outputs to ../wwwroot (relative to ClientApp) RUN npm run build # ── Stage 2: Publish .NET app ───────────────────────────────────────────────── FROM mcr.microsoft.com/dotnet/sdk:9.0 AS dotnet-build WORKDIR /src # Restore dependencies (layer-cached separately for fast rebuilds) COPY OTSSignsOrchestrator/OTSSignsOrchestrator.csproj OTSSignsOrchestrator/ RUN dotnet restore OTSSignsOrchestrator/OTSSignsOrchestrator.csproj # Copy source (excluding ClientApp — frontend handled in stage 1) COPY OTSSignsOrchestrator/ OTSSignsOrchestrator/ # Copy built frontend assets from stage 1 COPY --from=frontend-build /app/wwwroot OTSSignsOrchestrator/wwwroot/ RUN dotnet publish OTSSignsOrchestrator/OTSSignsOrchestrator.csproj \ -c Release \ -o /app/publish \ --no-restore # ── Stage 3: Runtime image ──────────────────────────────────────────────────── FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime WORKDIR /app # LibGit2Sharp requires git native libraries (libgit2 is bundled in the NuGet package, # but git2-ssh requires libssh2 on Linux) RUN apt-get update && apt-get install -y --no-install-recommends \ libssl3 \ ca-certificates \ nfs-common \ default-mysql-client \ && rm -rf /var/lib/apt/lists/* # Docker CLI — used for local swarm operations when running on the same manager node COPY --from=docker:27-cli /usr/local/bin/docker /usr/local/bin/docker COPY --from=dotnet-build /app/publish . # Data Protection keys must survive restarts — mount a volume here VOLUME ["/app/dataprotection-keys"] # Expose HTTP only — use a reverse proxy (nginx/Caddy/Traefik) for TLS termination EXPOSE 8080 ENV ASPNETCORE_URLS=http://+:8080 ENV ASPNETCORE_ENVIRONMENT=Production ENTRYPOINT ["dotnet", "OTSSignsOrchestrator.dll"]