BudgetApp/docs/superpowers/specs/2026-03-21-docker-deployment-design.md
Martin Svrcina de82bc0d61 Add Docker deployment design spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 02:24:42 +01:00

4.3 KiB

Docker Deployment Design

Date: 2026-03-21 Project: BudgetApp

Overview

Deploy BudgetApp to a home server running Nginx Proxy Manager (NPM) and an existing MySQL instance. Two Docker containers are orchestrated via docker-compose: one for the .NET 8 API and one for the Vue 3 frontend served by nginx.

Architecture

┌─────────────────────────────────────────────┐
│              docker-compose.yml              │
│                                              │
│  ┌──────────────────┐  ┌──────────────────┐ │
│  │  budgetapp-web   │  │  budgetapp-api   │ │
│  │  (nginx + Vue)   │──▶  (.NET 8 API)   │ │
│  │  port 3100:80    │  │  port 8080:8080  │ │
│  └──────────────────┘  └──────────────────┘ │
│           internal network: budgetapp        │
└─────────────────────────────────────────────┘
         │                      │
         ▼                      ▼
   NPM proxy host          seth.local MySQL
   (one entry)             (existing)
  • budgetapp-web exposes port 3100 to host — NPM points here
  • budgetapp-web nginx proxies /api/http://budgetapp-api:8080 over internal Docker network
  • budgetapp-api exposes port 8080 (optional, for direct access/debugging)
  • Both containers share a Docker bridge network named budgetapp
  • API connects to the existing MySQL at seth.local

Files to Create

File Purpose
Dockerfile.api Multi-stage .NET 8 build — solution root as context
Dockerfile.web Multi-stage Node/Vite build + nginx runtime
docker-compose.yml Wires containers, network, port mappings, env vars
nginx.web.conf nginx config — serves static files, proxies /api/ to API container
.env Gitignored secrets file — DB connection string

Files to Modify

File Change
appsettings.json Connection string already cleared; env var override ConnectionStrings__MainDatabase used at runtime
.gitignore Add .env entry (already present)

Container Details

budgetapp-api

  • Base images: mcr.microsoft.com/dotnet/sdk:8.0 (build) → mcr.microsoft.com/dotnet/aspnet:8.0 (runtime)
  • Build context: Solution root (required — API references sibling projects)
  • Publish target: BudgetApp.Api/BudgetApp.Api.csproj
  • Runtime port: 8080
  • Environment variables:
    • ASPNETCORE_ENVIRONMENT=Production
    • ConnectionStrings__MainDatabase — injected from .env via docker-compose

budgetapp-web

  • Base images: node:20-alpine (build) → nginx:alpine (runtime)
  • Build context: BudgetApp.Web/
  • Build output: dist/ copied into nginx image
  • Runtime port: 80 (mapped to host port 3100)
  • API routing: nginx proxies location /api/http://budgetapp-api:8080/
  • SPA routing: try_files $uri $uri/ /index.html for Vue Router

Secrets Management

A .env file at the solution root (gitignored) holds the DB connection string:

DB_CONNECTION=Server=seth.local;User Id=budget;Password=<password>;Database=budget

docker-compose passes this as the ConnectionStrings__MainDatabase environment variable to the API container. ASP.NET Core's built-in environment variable configuration provider resolves double-underscores as nested keys, so no code changes are required.

Build & Deploy

# Build and start both containers
docker-compose up -d --build

# View logs
docker-compose logs -f

NPM Setup (Manual)

After containers are running, add one proxy host in Nginx Proxy Manager:

  • Domain: budget.yourdomain.com (or local DNS name)
  • Forward hostname/IP: <host-ip>
  • Forward port: 3100

No second proxy host needed — API traffic is routed internally by the web container's nginx.

What Is NOT Changing

  • No changes to application code
  • No changes to EF Core migrations (database already exists)
  • No changes to JWT auth configuration
  • Vue frontend API calls use relative /api/ paths — no build-time URL baking needed