CI/CD with Dockerfile for Beginners - Deploy .NET API Using Docker
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
- Uses the official .NET 8 SDK image to build the application
- Restores NuGet packages
- Publishes the app in Release mode
- Uses a smaller runtime image (
aspnet) for the final result - Copies only the published output
- 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
- Checks out your code
- Logs in to Azure Container Registry
- Builds the Docker image using your Dockerfile
- Tags the image with the commit SHA (unique for every build)
- Pushes the image to the registry
- Tells Azure App Service to use the new image
7. Important Concepts
| Term | Meaning |
|---|---|
| Dockerfile | Instructions to create the image |
| Docker Image | Packaged application + runtime |
| Container | Running instance of an image |
| Azure Container Registry | Private storage for your images |
| Multi-stage build | Build in one stage, run in a smaller stage |
| Image tag | Version 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!