GB.
2026-06-1410 min read

CI/CD with Dockerfile for Beginners - Deploy .NET API Using Docker

#Docker#Dockerfile#GitHub Actions#CI/CD#.NET#Azure#DevOps

CI/CD with Dockerfile for Beginners - Deploy .NET API Using Docker

In the previous post you learned how to deploy a .NET API using GitHub Actions by publishing the files directly to Azure App Service.

That method works well. But many teams prefer a different approach: building a Docker image and deploying that image instead.

This post explains CI/CD with Dockerfile in a simple way.

You will learn:

  • What a Dockerfile is
  • Why people use Docker in CI/CD
  • How to write a basic Dockerfile for a .NET 8 API
  • How the deployment flow changes
  • A practical GitHub Actions example

1. What is a Dockerfile?

A Dockerfile is a text file that contains step-by-step instructions to build a Docker image.

Simple analogy:

Dockerfile  = recipe
Docker Image = the finished dish
Container   = a running instance of that dish

Instead of installing .NET, restoring packages, and building the project manually every time, you write those steps once inside the Dockerfile. Docker then creates a self-contained package that includes everything your application needs.

2. Why Use Docker in CI/CD?

Without Docker the flow looks like this:

Push code
   ↓
GitHub Actions builds the project
   ↓
Publishes the files
   ↓
Deploys the files to Azure App Service

With Docker the flow becomes:

Push code
   ↓
GitHub Actions builds a Docker image
   ↓
Pushes the image to a container registry
   ↓
Azure App Service pulls and runs the image

Main benefits:

  • The same image runs the same way everywhere (local, staging, production)
  • No more “it works on my machine” problems
  • Cleaner and more predictable deployments
  • Easier to move to other Azure services later (Container Apps, Kubernetes, etc.)
  • You fully control the environment (OS, runtime, dependencies)

3. The Simple Mental Model

Your .NET Code
      +
   Dockerfile
      ↓
  Docker Build
      ↓
  Docker Image
      ↓
  Push to Registry
      ↓
  Azure runs the image

You are no longer deploying raw files. You are deploying a complete, ready-to-run package.

4. Basic Dockerfile for a .NET 8 Web API

Here is a clean and commonly used Dockerfile:

# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy project file and restore first (better layer caching)
COPY ["YourApi.csproj", "."]
RUN dotnet restore

# Copy the rest of the code and publish
COPY . .
RUN dotnet publish -c Release -o /app/publish

# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .

EXPOSE 8080
ENTRYPOINT ["dotnet", "YourApi.dll"]

What this Dockerfile does

  1. Uses the official .NET 8 SDK image to build the application
  2. Restores NuGet packages
  3. Publishes the app in Release mode
  4. Uses a smaller runtime image (aspnet) for the final result
  5. Copies only the published output
  6. Tells the container to start your application

This technique is called a multi-stage build. It keeps the final image smaller and more secure because the SDK is not included in the production image.

5. How CI/CD Changes When You Use Docker

Previous approach (no Docker):

Push → Build → Publish → Deploy files

New approach (with Docker):

Push
  ↓
Build Docker image
  ↓
Push image to Azure Container Registry
  ↓
Azure App Service pulls the new image and runs it

You now work with two main Azure services:

  • Azure Container Registry (ACR) → private place to store your Docker images
  • Azure App Service (configured for containers) → runs the image

6. Example GitHub Actions Workflow

Here is a practical workflow that builds and deploys using Docker:

name: Build and Deploy with Docker

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Login to Azure Container Registry
        uses: azure/docker-login@v1
        with:
          login-server: yourregistry.azurecr.io
          username: ${{ secrets.ACR_USERNAME }}
          password: ${{ secrets.ACR_PASSWORD }}

      - name: Build and push Docker image
        run: |
          docker build -t yourregistry.azurecr.io/myapi:${{ github.sha }} .
          docker push yourregistry.azurecr.io/myapi:${{ github.sha }}

      - name: Deploy to Azure App Service
        uses: azure/webapps-deploy@v3
        with:
          app-name: 'your-app-service-name'
          images: yourregistry.azurecr.io/myapi:${{ github.sha }}

What this workflow does

  1. Checks out your code
  2. Logs in to Azure Container Registry
  3. Builds the Docker image using your Dockerfile
  4. Tags the image with the commit SHA (unique for every build)
  5. Pushes the image to the registry
  6. Tells Azure App Service to use the new image

7. Important Concepts

TermMeaning
DockerfileInstructions to create the image
Docker ImagePackaged application + runtime
ContainerRunning instance of an image
Azure Container RegistryPrivate storage for your images
Multi-stage buildBuild in one stage, run in a smaller stage
Image tagVersion of the image (latest, commit SHA…)

8. Testing Locally First

Before relying on CI/CD, always test the Dockerfile on your machine:

# Build the image
docker build -t myapi .

# Run the container
docker run -p 8080:8080 myapi

Then open:

http://localhost:8080/api/hello

If it works locally with Docker, it will almost always work the same way in Azure.

9. When Should You Use Docker in CI/CD?

Good reasons to use Docker:

  • You want consistent environments across machines
  • You plan to use Azure Container Apps or Kubernetes later
  • You have multiple services
  • You want cleaner and more repeatable deployments

You can still skip Docker if:

  • You are just getting started
  • You have a simple single API
  • The classic “publish files” method is enough for now

Many teams begin without Docker and add it later when they need more control.

10. Final Takeaway

CI/CD with Dockerfile changes what you deploy, not the overall idea.

Without Docker:

Push code → Build → Deploy files to App Service

With Docker:

Push code → Build image → Push to registry → App Service runs the image

The core benefit remains the same:

You write the code.
GitHub Actions builds and deploys it.
Azure runs the new version automatically.

Docker simply gives you a more consistent and portable package.

Once you are comfortable with this flow, you can later explore:

  • Smaller and more optimized Docker images
  • Multiple environments (Dev, Staging, Production)
  • Azure Container Apps
  • Health checks and rolling updates

But the foundation starts with a good Dockerfile and a clear GitHub Actions pipeline.

Happy building!

Enjoyed this article? Share it with your network!