Files
Htmx/GCR/Dockerfile
T

71 lines
3.6 KiB
Docker

# ─────────────────────────────────────────────────────────────────────────────
# Stage 1 — npm install (Tailwind CLI)
# Uses node:24-alpine for a small image. npm ci is used for reproducible,
# fast installs from the lockfile.
# ─────────────────────────────────────────────────────────────────────────────
FROM node:24-alpine AS npm-install
WORKDIR /npm
COPY Htmx.ApiDemo/package.json Htmx.ApiDemo/package-lock.json* ./
RUN npm ci
# ─────────────────────────────────────────────────────────────────────────────
# Stage 2 — AOT publish
# Uses the Alpine SDK image so the binary is linked against musl libc,
# making it compatible with the Alpine runtime image in Stage 3.
#
# Alpine packages are cached via BuildKit mount cache — after the first build
# `apk add` is near-instant because the package index and downloads are
# reused from the local cache rather than re-fetched from the network.
# ─────────────────────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS publish
# Install AOT linker tools and Node.js (for the Tailwind MSBuild target).
# --mount=type=cache keeps the apk package cache between builds on this machine.
RUN --mount=type=cache,target=/var/cache/apk \
apk add --no-cache clang lld musl-dev build-base nodejs npm
WORKDIR /src
# Copy project files first — NuGet restore layer is cached until these change.
COPY Htmx.slnx .
COPY Htmx.ApiDemo/Htmx.ApiDemo.csproj Htmx.ApiDemo/
COPY Htmx.SourceGenerator/Htmx.SourceGenerator.csproj Htmx.SourceGenerator/
RUN dotnet restore Htmx.ApiDemo/Htmx.ApiDemo.csproj -r linux-musl-x64
# Bring in pre-installed node_modules from Stage 1.
COPY --from=npm-install /npm/node_modules Htmx.ApiDemo/node_modules
# Copy the rest of the source
COPY . .
# AOT publish targeting musl so the binary runs on Alpine.
RUN dotnet publish Htmx.ApiDemo/Htmx.ApiDemo.csproj \
-c Release \
-r linux-musl-x64 \
--no-restore \
--self-contained true \
-o /publish
# ─────────────────────────────────────────────────────────────────────────────
# Stage 3 — Runtime image (~12 MB base vs ~100 MB on Debian)
# runtime-deps:alpine provides only the native libs the AOT binary needs.
# No .NET runtime is included — the binary is fully self-contained.
# ─────────────────────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine AS runtime
WORKDIR /app
COPY --from=publish /publish .
# Cloud Run injects PORT (default 8080).
# ASP.NET Core reads ASPNETCORE_HTTP_PORTS directly — no entrypoint script needed.
ENV ASPNETCORE_HTTP_PORTS=8080
# Use the built-in non-root user provided by the official .NET Alpine image.
USER $APP_UID
EXPOSE 8080
ENTRYPOINT ["./Htmx.ApiDemo"]