Core API Deployment

The BookStore API is now complete — it has full CRUD operations, JWT authentication, caching, logging, real-time updates, and a test suite. The final step is deploying it to a production environment so real users can access it. This topic covers deployment to both Windows IIS and Linux using Docker, with Azure as the cloud platform.

Deployment Targets Overview

PlatformBest ForComplexity
IIS (Windows Server)Windows-based infrastructureLow
Docker + LinuxCloud, containers, scalabilityMedium
Azure App ServiceManaged cloud hosting, easy scalingLow
Azure Container AppsContainerized microservicesMedium

Step 1 – Prepare for Production

Update appsettings.Production.json

// appsettings.Production.json
{
  "ConnectionStrings": {
    "BookStoreDB": "Server=prod-sql.database.windows.net;Database=BookStoreDB;User Id=bookstoreuser;Password=StrongPassword123!;"
  },
  "JwtSettings": {
    "Key": "ProductionSuperSecretKeyThatIsAtLeast32CharsLong!",
    "Issuer": "BookStoreAPI",
    "Audience": "BookStoreClient",
    "ExpiresInMinutes": 60
  },
  "Cors": {
    "AllowedOrigins": [ "https://bookstore-frontend.com" ]
  },
  "Serilog": {
    "MinimumLevel": {
      "Default": "Warning",
      "Override": {
        "Microsoft": "Error"
      }
    },
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "File",
        "Args": { "path": "/var/logs/bookstore-.log", "rollingInterval": "Day" }
      }
    ]
  }
}

Environment Variable Strategy (Secure)

// Never store secrets in appsettings.json in source control.
// Use environment variables in production:

// Linux:
export ASPNETCORE_ENVIRONMENT=Production
export ConnectionStrings__BookStoreDB="Server=...;..."
export JwtSettings__Key="YourProductionKey"

// Windows:
setx ASPNETCORE_ENVIRONMENT "Production"

Step 2 – Publish the Application

// Self-contained deployment (includes .NET runtime — no runtime needed on server):
dotnet publish -c Release -r linux-x64 --self-contained true -o ./publish

// Framework-dependent deployment (smaller size — requires .NET on server):
dotnet publish -c Release -o ./publish
OptionOutput SizeRequires .NET on Server
Self-containedLarger (~70MB)No
Framework-dependentSmaller (~5MB)Yes

Option A – Deploy to IIS (Windows)

Prerequisites

  • Windows Server with IIS installed
  • .NET Hosting Bundle installed (includes the IIS module for ASP.NET Core)

Steps

1. Publish the app:
   dotnet publish -c Release -o C:\inetpub\BookStoreAPI

2. In IIS Manager:
   - Create a new Site
   - Point Physical Path to C:\inetpub\BookStoreAPI
   - Set Application Pool to "No Managed Code"
   - Assign a port (e.g., 80 or 443)

3. Set environment variable in IIS:
   - Application Pool → Advanced Settings → Environment Variables
   - Add: ASPNETCORE_ENVIRONMENT = Production

4. Browse to http://yourserver/swagger to verify

web.config (Auto-Generated by publish)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore" path="*" verb="*"
           modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="dotnet"
                arguments=".\BookStoreAPI.dll"
                stdoutLogEnabled="true"
                stdoutLogFile=".\logs\stdout"
                hostingModel="inprocess"/>
  </system.webServer>
</configuration>

Option B – Deploy with Docker (Linux / Cloud)

Create Dockerfile

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

COPY ["BookStoreAPI/BookStoreAPI.csproj", "BookStoreAPI/"]
RUN dotnet restore "BookStoreAPI/BookStoreAPI.csproj"

COPY . .
WORKDIR "/src/BookStoreAPI"
RUN dotnet build -c Release -o /app/build

# Stage 2: Publish
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish

# Stage 3: Final runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
EXPOSE 80
EXPOSE 443

COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "BookStoreAPI.dll"]

Create docker-compose.yml (App + Database)

# docker-compose.yml
version: '3.8'

services:
  bookstore-api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "5000:80"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__BookStoreDB=Server=sqlserver;Database=BookStoreDB;User Id=sa;Password=YourStrong!Password;TrustServerCertificate=True;
      - JwtSettings__Key=ProductionSuperSecretKeyThatIsAtLeast32CharsLong!
    depends_on:
      - sqlserver

  sqlserver:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=YourStrong!Password
    ports:
      - "1433:1433"
    volumes:
      - sqlserver_data:/var/opt/mssql

volumes:
  sqlserver_data:

Run with Docker Compose

docker-compose up --build -d

# Verify running containers:
docker ps

# Check API logs:
docker logs bookstore-api

# Apply database migrations inside the container:
docker exec bookstore-api dotnet ef database update

Option C – Deploy to Azure App Service

// Step 1: Install Azure CLI
az login

// Step 2: Create a resource group
az group create --name BookStoreRG --location eastus

// Step 3: Create an App Service Plan
az appservice plan create --name BookStorePlan --resource-group BookStoreRG --sku B1 --is-linux

// Step 4: Create the web app
az webapp create --resource-group BookStoreRG --plan BookStorePlan \
  --name bookstore-api-prod --runtime "DOTNET|8.0"

// Step 5: Set environment variables in Azure
az webapp config appsettings set --resource-group BookStoreRG \
  --name bookstore-api-prod \
  --settings ASPNETCORE_ENVIRONMENT=Production \
             JwtSettings__Key="ProductionKeyHere"

// Step 6: Deploy via ZIP
dotnet publish -c Release -o ./publish
cd publish
zip -r ../bookstore.zip .
az webapp deploy --resource-group BookStoreRG \
  --name bookstore-api-prod \
  --src-path ../bookstore.zip

Health Check Endpoint

Add a health check endpoint so load balancers and container orchestrators can verify the API is running:

// Program.cs
builder.Services.AddHealthChecks()
    .AddDbContextCheck<BookStoreDbContext>("database");

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = async (context, report) =>
    {
        context.Response.ContentType = "application/json";
        var result = new
        {
            status = report.Status.ToString(),
            timestamp = DateTime.UtcNow,
            checks = report.Entries.Select(e => new
            {
                name = e.Key,
                status = e.Value.Status.ToString()
            })
        };
        await context.Response.WriteAsJsonAsync(result);
    }
});
GET /health

Response: 200 OK
{
  "status": "Healthy",
  "timestamp": "2024-01-15T10:00:00Z",
  "checks": [
    { "name": "database", "status": "Healthy" }
  ]
}

Production Checklist

ItemStatus
ASPNETCORE_ENVIRONMENT set to ProductionRequired
Swagger UI disabled in ProductionRequired
Secrets in environment variables (not appsettings)Required
HTTPS enforcedRequired
Database migrations appliedRequired
Health check endpoint activeRequired
Logging writing to files or cloud loggingRequired
CORS configured for production domain onlyRequired
Rate limiting enabledRecommended
JWT expiry appropriate for productionRecommended

Key Points

  • Use environment-specific appsettings.Production.json and environment variables for production configuration — never hardcode secrets.
  • Disable Swagger UI in Production to avoid exposing internal API details.
  • The dotnet publish command compiles the API into a deployable folder with all dependencies included.
  • Docker provides consistent, portable deployments across any Linux server or cloud platform.
  • Azure App Service offers managed hosting with easy scaling, environment variable support, and built-in HTTPS.
  • A health check endpoint (/health) allows load balancers and container orchestrators to monitor the API's status automatically.

Leave a Comment