.NET Academy

Capstone 3: ship TaskFlow

Containerize TaskFlow, run it with Docker Compose, automate CI with GitHub Actions, and optionally deploy it to the cloud.

30 min read

You will learn

  • Containerize the API with a multi-stage Dockerfile
  • Run the whole system with Docker Compose
  • Build and test every change with GitHub Actions
  • Deploy (optionally) and reflect on your design

From "works on my machine" to shipped

TaskFlow works locally. Now make it reproducible, automatically tested, and deployable.

Milestone 1 — Containerize

Add a multi-stage Dockerfile at the solution root:

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY src/TaskFlow.Api/TaskFlow.Api.csproj src/TaskFlow.Api/
RUN dotnet restore src/TaskFlow.Api/TaskFlow.Api.csproj
COPY . .
RUN dotnet publish src/TaskFlow.Api -c Release -o /app/publish
 
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "TaskFlow.Api.dll"]

…and a .dockerignore with bin/, obj/, .git/, tests/. Build and run it against your local database to check it starts.

Covered in: Docker for .NET.

Milestone 2 — Docker Compose

Describe the whole system in docker-compose.yml: the API, PostgreSQL with a volume, and (optionally) a fake SMTP server like MailHog/Mailpit so you can see the assignment emails in a web UI.

services:
  api:
    build: .
    ports: ["8080:8080"]
    environment:
      ConnectionStrings__TaskFlow: "Host=db;Database=taskflow;Username=postgres;Password=dev-only"
      ASPNETCORE_ENVIRONMENT: Development
    depends_on: [db]
  db:
    image: postgres:16
    environment: { POSTGRES_PASSWORD: dev-only, POSTGRES_DB: taskflow }
    volumes: ["db-data:/var/lib/postgresql/data"]
  mail:
    image: axllent/mailpit
    ports: ["8025:8025"]
volumes:
  db-data:

Decide how migrations run: at startup in development only (db.Database.MigrateAsync()), and as a separate pipeline step for real environments.

Goal: a new developer runs docker compose up and has a working system.

Milestone 3 — Continuous integration

Push the repository to GitHub and add .github/workflows/ci.yml:

  • Trigger on pushes and pull requests.
  • Restore, build (-c Release), run all tests (Testcontainers works on GitHub's Ubuntu runners).
  • Check for vulnerable packages.
  • Build the Docker image to make sure it still builds.

Then protect main: require the CI check to pass before merging.

Covered in: CI/CD with GitHub Actions.

Milestone 4 — Deploy (optional)

Pick a target that fits your budget — many offer free tiers or credits:

  • Azure Container Apps + Azure Database for PostgreSQL, or
  • Azure App Service (deploy the published output), or
  • Any container platform (Fly.io, Railway, Render, Google Cloud Run).

Whichever you choose:

  • Configuration and secrets via the platform's settings / Key Vault — never in the image.
  • /health configured as the health check.
  • Logs (and ideally OpenTelemetry traces) visible somewhere you can search.
  • A budget alert, and remember to delete the resources when you're done experimenting.

Extend the workflow with a deploy job on main.

Covered in: Deploying to the cloud · Running in production.

Milestone 5 — README and retrospective

Write a README.md a stranger could follow: what TaskFlow is, how to run it (docker compose up), how to run the tests, the API overview (link to the OpenAPI document) and the architecture diagram.

Then revisit docs/design.md and add a retrospective:

  • What changed between the design and the implementation, and why?
  • Which decision would you make differently?
  • What breaks first at 10× the users? (Hint: think about "my tasks" queries, notification volume, and a single database.) How would you fix it — caching, read replicas, a real message broker?
  • What would you add next: real-time updates with SignalR? File attachments in object storage? Search?

Show it off

A deployed, tested, containerized API with a design document and a clear README is an excellent portfolio project. You've used the skills a professional .NET backend developer uses every day.

Checklist

  • Multi-stage Dockerfile builds and runs the API
  • docker compose up starts the API, database (and mail catcher)
  • GitHub Actions builds and tests every push and pull request; main is protected
  • (Optional) Deployed with health checks, logs and secrets handled properly
  • README and design retrospective written

Congratulations 🎉

You've gone from your first Console.WriteLine to designing, building and shipping a production-style backend. Keep building — and revisit any lesson whenever you need a refresher.

Finished reading? Mark the lesson as complete to track your progress.