The previous post covered how to set up a web application with a .NET Core backend and Svelte frontend.
In this post we'll package it up into a single Docker image ready to be deployed somewhere.
Prerequisites
Approach
We'll be taking advantage of Docker's multi-stage builds to:
- build the frontend using Node
- then the backend using .NET Core SDK
- copy the results to an image containing only the ASP.NET Core runtime
Dockerfile
Create a file named Dockerfile
. I usually do this with:
touch Dockerfile
Frontend Build Stage
We'll start the file off with the below snippet. I've added comments to describe each step:
# Use Node.js 14, name this stage 'frontend'
FROM node:14 AS frontend
# Our working directory within the image
WORKDIR /build
# Copy package and lock files then install dependencies
COPY package.json .
COPY package-lock.json .
RUN npm install
# Copy the rest of the files for the frontend
COPY rollup.config.js .
COPY svelte-app ./svelte-app
# Build - this'll output files to /build/wwwroot
RUN npm run build
If you're wondering "why copy the package and lock files first?": it's so Docker creates and caches an intermediate layer containing your dependencies (in node_modules). This gets reused in subsequent builds if those files remain unchanged and saves having to redownload dependencies on each build.
Backend Build Stage
Now add:
# Use .NET Core SDK, name this stage 'backend'
FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS backend
WORKDIR /build
# Copy the csproj file then install dependencies
COPY dotnet-svelte.csproj .
RUN dotnet restore dotnet-svelte.csproj
# Copy everything else
COPY . .
# Publish, and output the results to /publish
RUN dotnet publish -c Release -o /publish
Similar to the frontend stage, we copy the csproj
file first and restore dependencies to create an intermediate layer.
Combine the Results
We named our stages in the earlier snippets with AS frontend
and AS backend
. In our final stage we can reference the earlier stages and copy files from them. Add to the file:
# ASP.NET Core Runtime
FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine
# Where our application will live
WORKDIR /app
# Copy the build results of the frontend stage
COPY --from=frontend /build/wwwroot ./wwwroot
# Copy the build results of the backend stage
COPY --from=backend /publish .
# Run our web application on startup (will listen on port 80 by default)
ENTRYPOINT /app/dotnet-svelte
Final Dockerfile
Should look like this (comments omitted):
FROM node:14 AS frontend
WORKDIR /build
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY rollup.config.js .
COPY svelte-app ./svelte-app
RUN npm run build
FROM mcr.microsoft.com/dotnet/sdk:5.0-alpine AS backend
WORKDIR /build
COPY dotnet-svelte.csproj .
RUN dotnet restore dotnet-svelte.csproj
COPY . .
RUN dotnet publish -c Release -o /publish
FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine
WORKDIR /app
COPY --from=frontend /build/wwwroot ./wwwroot
COPY --from=backend /publish .
ENTRYPOINT /app/dotnet-svelte
Building & Running
Builds an image from the Dockerfile, and name it dotnet-svelte
:
docker build -t dotnet-svelte .
Then run with:
docker run --rm -p 5000:80 dotnet-svelte
-
--rm
tells docker to automatically remove the container once stopped -
-p 5000:80
maps your local port 5000 to the container's port 80
Finally, browse to http://localhost:5000 and once again you will see:
Congratulations! You now have an image ready to be deployed somewhere.
Summary
This post was really more about Docker and multi-stage builds than anything else. We:
- Used a Node image to build the Svelte application
- Used a .NET Core SDK image to build the ASP.NET application
- Copied the results of both to an ASP.NET runtime image
The resulting image contains only the parts we need: static files for the frontend, binary files for the backend and the runtime required to run it.
Top comments (2)
This is perfect timing! Perhaps you could cover deploying to AWS or a similar cloud provider?
Hi Miles, Thanks for reading!
This is something that's probably covered a lot by others. For AWS I'd have a look at their documentation first: aws.amazon.com/getting-started/han...