Docker Container Hello World with .Net 8.0 MVC Razor Web App

Jay (Vijayasimha BR)
7 min readOct 14, 2024

--

just a banner i made in canva.

For reasons I don’t really know, I did not use docker at all, all these years. Late now, but, it’s time to get this going in my life.

In this post, I am thinking of doing the following.

  1. Create a simple Razor Web App using Visual Studio 2022.
  2. containerize the web app using docker tools.
  3. run the app via docker locally.
  4. push the docker image to my docker hub as a public image.
  5. pull the docker image back from the hub.
  6. run the image locally as a container without visual studio 2022.

Assumptions, I am making.

  1. You already some experience with .net and building web apps, web api’s and MVC .net.
  2. You already have docker running on your computer, logged in to your docker account and docker is working from your command line.

Okay, first up, create the razor mvc web app. using this link as reference.

2nd step, containerize. start with ‘docker init’.

PS D:\wr\dotnetprivate2024\PPrjs\EFCore\EFCoreRazorHW> docker init

Welcome to the Docker Init CLI!

This utility will walk you through creating the following files with sensible defaults for your project:
- .dockerignore
- Dockerfile
- compose.yaml
- README.Docker.md

Let's get started!

? What application platform does your project use? ASP.NET Core
? What's the name of your solution's main project? EFCoreRazorHW
? What version of .NET do you want to use? (8.0)

? What version of .NET do you want to use? 8.0
? What port do you want to use to access your app? (8080)

? What port do you want to use to access your app? 8080

✔ Created → .dockerignore
✔ Created → Dockerfile
✔ Created → compose.yaml
✔ Created → README.Docker.md

→ Your Docker files are ready!
Review your Docker files and tailor them to your application.
Consult README.Docker.md for information about using the generated files.

What's next?
Start your application by running → docker compose up --build
Your application will be available at http://localhost:8080

as the instruction says, go to the compose command. ‘docker compose up — build’

it starts building. remember, this building can take a really long time. get a cup of coffee or watch some trailers on YouTube.

aha! okay, it is done.

[+] Running 2/2
✔ Network efcorerazorhw_default Created 0.0s
✔ Container efcorerazorhw-server-1 Created 0.1s
Attaching to server-1
server-1 | warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
server-1 | Storing keys in a directory '/home/app/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed. For more information go to https://aka.ms/aspnet/dataprotectionwarning
server-1 | warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
server-1 | No XML encryptor configured. Key {b8d749c8-999c-400e-ad0d-615e0aa3714e} may be persisted to storage in unencrypted form.
server-1 | info: Microsoft.Hosting.Lifetime[14]
server-1 | Now listening on: http://[::]:8080
server-1 | info: Microsoft.Hosting.Lifetime[0]
server-1 | Application started. Press Ctrl+C to shut down.
server-1 | info: Microsoft.Hosting.Lifetime[0]
server-1 | Hosting environment: Production
server-1 | info: Microsoft.Hosting.Lifetime[0]
server-1 | Content root path: /app

and it is running.

available at http://localhost:8080

and visible in docker desktop. at this stage, I have already closed visual studio.

nice.

now, let’s push it to my docker hub.

now, note the contents of the docker file.

# syntax=docker/dockerfile:1

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/

# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7

################################################################################

# Learn about building .NET container images:
# https://github.com/dotnet/dotnet-docker/blob/main/samples/README.md

# Create a stage for building the application.
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build

COPY . /source

WORKDIR /source/EFCoreRazorHW

# This is the architecture you’re building for, which is passed in by the builder.
# Placing it here allows the previous steps to be cached across architectures.
ARG TARGETARCH

# Build the application.
# Leverage a cache mount to /root/.nuget/packages so that subsequent builds don't have to re-download packages.
# If TARGETARCH is "amd64", replace it with "x64" - "x64" is .NET's canonical name for this and "amd64" doesn't
# work in .NET 6.0.
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app

# If you need to enable globalization and time zones:
# https://github.com/dotnet/dotnet-docker/blob/main/samples/enable-globalization.md
################################################################################
# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the build stage where the necessary files are copied from the build
# stage.
#
# The example below uses an aspnet alpine image as the foundation for running the app.
# It will also use whatever happens to be the most recent version of that tag when you
# build your Dockerfile. If reproducability is important, consider using a more specific
# version (e.g., aspnet:7.0.10-alpine-3.18),
# or SHA (e.g., mcr.microsoft.com/dotnet/aspnet@sha256:f3d99f54d504a21d38e4cc2f13ff47d67235efeeb85c109d3d1ff1808b38d034).
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final
WORKDIR /app

# Copy everything needed to run the app from the "build" stage.
COPY --from=build /app .

# Switch to a non-privileged user (defined in the base image) that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
# and https://github.com/dotnet/dotnet-docker/discussions/4764
USER $APP_UID

ENTRYPOINT ["dotnet", "EFCoreRazorHW.dll"]

and the compose.yaml file.

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/

# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
server:
build:
context: .
target: final
ports:
- 8080:8080

# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker compose up`.
# depends_on:
# db:
# condition: service_healthy
# db:
# image: postgres
# restart: always
# user: postgres
# secrets:
# - db-password
# volumes:
# - db-data:/var/lib/postgresql/data
# environment:
# - POSTGRES_DB=example
# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
# expose:
# - 5432
# healthcheck:
# test: [ "CMD", "pg_isready" ]
# interval: 10s
# timeout: 5s
# retries: 5
# volumes:
# db-data:
# secrets:
# db-password:
# file: db/password.txt

These two files are essential, or the process willl not work. this is what ‘docker init’. did. of course, you can also add these files and the code yourself.

now, docker desktop is buggy. it won’t stop the container sometimes. so, use these commands, which stop all containers and then remove them.

docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)

container is stopped, but, the docker image is in the machine.

image looks fine.

now, if you try to push from docker desktop (there is an option next to the image menu), it won’t work and gives a error.

cannot push, sadly.

so, we use commands.

docker image tag efcorerazorhw-server jaydaakarru/efcorerazorhw-server:latest
docker image push jaydaakarru/efcorerazorhw-server:latest

and this happens

PS D:\wr\dotnetprivate2024\PPrjs\EFCore\EFCoreRazorHW> docker image tag efcorerazorhw-server jaydaakarru/efcorerazorhw-server:latest
PS D:\wr\dotnetprivate2024\PPrjs\EFCore\EFCoreRazorHW> docker image push jaydaakarru/efcorerazorhw-server:latest
The push refers to repository [docker.io/jaydaakarru/efcorerazorhw-server]
ba5c451fad03: Pushed
7f1bbb9100d5: Pushed
f2c56adc1106: Pushed
e626cc920375: Pushed
701e3223ffcb: Pushed
12084d95fa0e: Pushed
3180a1fc8d19: Pushed
43c4264eed91: Mounted from jaydaakarru/webapplication4-server
latest: digest: sha256:adbd6669812a58b478408e28e714a49b1736ab38036c5f19fa5494eff31eb428 size: 856
PS D:\wr\dotnetprivate2024\PPrjs\EFCore\EFCoreRazorHW>

and, now, check out your online hub. it will be there. you can see it here on my account as well.

aha. nice.

now, I have deleted the local image. and, pull it from my online hub.

pull it from the online repo.

now, you could try to run it directly from docker desktop, but it won’t give you the option to select a port.

no port to run.

This is a problem. So, back to the command line.

docker run -d -p 8080:8080 jaydaakarru/efcorerazorhw-server

and it’s running.

nice.

and as usual, available at the port mentioned.

aha. yes.

And, that is all there is to it. easy peasy.

I work as a coding tutor. You can hire me on Upwork, Fiverr and Codementor. You can also book a session on calendly, and visit my website. Also, video tutorials on my YouTube Channel.

--

--

No responses yet