Docker for Local Development

The Dockerizing for nginx page covers the production image. Day-to-day development wants a different shape: fast rebuilds, hot reload, and a database that resets easily — without needing SQL Server or Node installed on the host at all.

.dockerignore — keep the build context small

Without this, every docker build copies bin/, obj/, node_modules/, and .git into the build context first, which is slow and can leak local build artifacts into the image.

.dockerignore
**/bin
**/obj
**/node_modules
**/.vs
.git
.env
*.md
Dockerfile*
docker-compose*

docker-compose.override.yml — a dev-only layer

Compose automatically merges docker-compose.yml with docker-compose.override.yml when present. Keep production-shaped config (image tags, restart policies) in the base file, and put dev conveniences — bind-mounted source for hot reload, exposed debug ports, relaxed passwords — in the override, which never ships.

docker-compose.override.yml
services:
  api:
    build:
      target: build # stop at the SDK stage instead of the slim runtime stage
    volumes:
      - ./YourApp.API:/src/YourApp.API
      - ./YourApp.Application:/src/YourApp.Application
      - ./YourApp.Domain:/src/YourApp.Domain
      - ./YourApp.Infrastructure:/src/YourApp.Infrastructure
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    command: dotnet watch --project YourApp.API run --urls http://0.0.0.0:8080
    ports:
      - "8080:8080"

  db:
    ports:
      - "1433:1433" # exposed on the host so a local GUI client can connect
    environment:
      - SA_PASSWORD=DevOnlyPassword1!

dotnet watch inside the container plus the bind-mounted source folders means saving a file on the host triggers a rebuild inside the container — no docker build round-trip per change.

Bringing it up

shell
# override file is picked up automatically alongside docker-compose.yml
docker compose up --build

# reset the database to a clean state — drops the volume entirely
docker compose down -v
docker compose up --build

Attaching a debugger

VS Code's Dev Containers extension can attach directly to a running container's process, so breakpoints work the same as running locally — see .devcontainer/devcontainer.json if the project uses one. Without it, the simplest option is running the API on the host (against the containerized database only) during active debugging, and switching to the full compose stack for integration testing.

appsettings.Development.json — pointing the host process at the containerized DB
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost,1433;Database=YourApp;User=sa;Password=DevOnlyPassword1!;TrustServerCertificate=True"
  }
}
Keep the override file out of what actually ships: docker-compose.override.yml should be in .gitignore or clearly marked dev-only if committed, and never referenced from the deploy pipeline in CI Pipeline (GitHub Actions) — production always builds from the base docker-compose.yml and the real Dockerfile target.