# syntax=docker/dockerfile:1 # Die Oberfläche zuerst, in einem eigenen Abschnitt: sie braucht Node, der # Server nicht. So landet Node nie im fertigen Image. FROM --platform=$BUILDPLATFORM node:22-alpine AS web WORKDIR /web COPY web/app/package.json web/app/package-lock.json ./ RUN npm ci COPY web/app/ ./ RUN npm run build # Gebaut wird auf der Architektur des Bauwirts, übersetzt wird für die des # Ziels — `dotnet publish -a` kann das. Ohne diese Trennung liefe der Bau für # arm64 unter Emulation und dauerte ein Vielfaches. FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS build ARG TARGETARCH WORKDIR /src # Erst die Projektdateien, dann wiederherstellen: so bleibt die # Paket-Wiederherstellung in der Ebenenzwischenablage, solange sich keine # Abhängigkeit ändert. Ohne diesen Schritt lädt jeder Bau alles neu. # `--parents` behält die Verzeichnisse bei. **Ein Muster statt einer Liste**, # weil eine Liste bei jedem neuen Projekt vergessen wird — und dann scheitert # erst der Bau im Container, nicht der auf dem Arbeitsrechner. COPY --parents server/src/*/*.csproj ./ RUN dotnet restore server/src/Flaro.Api/Flaro.Api.csproj -a $TARGETARCH COPY server/ ./server/ RUN dotnet publish server/src/Flaro.Api/Flaro.Api.csproj -a $TARGETARCH -c Release -o /app --no-restore FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime # ffmpeg und die Treiber für Hardware-Beschleunigung. # # **Als eigenes Programm, nicht als Bibliothek.** Flaro ruft es als Prozess # auf; damit bleiben die Lizenzen getrennt (ffmpeg steht unter GPL) und ein # Absturz dort nimmt den Server nicht mit. # # `mesa-va-drivers` bedient AMD und ältere Intel, `intel-media-va-driver` die # neueren Intel — beide über VAAPI an `/dev/dri`. Welcher greift, entscheidet # sich zur Laufzeit danach, was durchgereicht wurde. # # **Die Intel-Treiber nur auf amd64.** Es gibt sie für arm64 nicht, und ein # unbedingtes `apt-get install` liess den Bau dort mit Code 100 abbrechen — # eine Meldung, die nicht verrät, welches der sechs Pakete fehlt. ARG TARGETARCH RUN apt-get update \ && apt-get install -y --no-install-recommends \ ffmpeg libva2 libva-drm2 mesa-va-drivers \ && if [ "$TARGETARCH" = "amd64" ]; then \ apt-get install -y --no-install-recommends intel-media-va-driver i965-va-driver; \ fi \ && rm -rf /var/lib/apt/lists/* # **Die Verzeichnisse müssen hier angelegt werden, nicht erst beim Start.** # Docker legt den Einhängepunkt eines frischen benannten Volumes sonst als # `root` an und übernimmt für ein leeres Volume die Rechte aus dem Image — # gibt es das Verzeichnis im Image nicht, erbt es `root:root`, und der erste # Start scheitert für jeden, der kein Bind Mount benutzt. RUN mkdir -p /config /cache /transcode && chown -R 1000:1000 /config /cache /transcode # **Kein root.** Die Kennung 1000 ist die des ersten Benutzers auf fast jedem # Linux und auf den üblichen NAS-Geräten — wessen Dateien anders gehören, setzt # in der Compose-Datei `user: ":"`. Das ist Docker-Bordmittel und # braucht weder ein Startskript noch root im Container. USER 1000:1000 ENV FLARO_DATA=/config \ FLARO_CACHE=/cache \ FLARO_TRANSCODE=/transcode \ ASPNETCORE_URLS=http://+:8080 \ DOTNET_gcServer=0 WORKDIR /app COPY --from=build /app . COPY --from=web /web/dist ./wwwroot EXPOSE 8080 VOLUME ["/config", "/cache", "/transcode"] ENTRYPOINT ["dotnet", "Flaro.Api.dll"]