Deployment

Deploy Suave applications to production with various hosting strategies.

Building for Production

Prepare your Suave application for production deployment:

// Build in Release mode
dotnet build -c Release

// Publish for standalone deployment
dotnet publish -c Release -o ./publish

// Create self-contained deployment
dotnet publish -c Release -o ./publish \
  --self-contained true \
  -r linux-x64

Docker Deployment

Deploy Suave in Docker containers:

# Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS builder
WORKDIR /build
COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/runtime:9.0
WORKDIR /app
COPY --from=builder /app/publish .
EXPOSE 8080
ENTRYPOINT ["dotnet", "Suave.Website.dll"]

Docker Compose Setup

Manage Suave with Docker Compose:

version: '3.8'
services:
  suave:
    build: .
    ports:
      - "8080:8080"
    environment:
      ASPNETCORE_ENVIRONMENT: Production
      SUAVE_PORT: 8080
      SUAVE_IP: 0.0.0.0
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
  
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - suave

Linux Systemd Service

Run Suave as a systemd service:

# /etc/systemd/system/suave.service
[Unit]
Description=Suave Web Application
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/suave
ExecStart=/usr/bin/dotnet /opt/suave/Suave.Website.dll
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

# Commands to manage the service
sudo systemctl enable suave
sudo systemctl start suave
sudo systemctl status suave
sudo systemctl restart suave

IIS Hosting

Host Suave on IIS with ASP.NET Core Module:

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

Nginx Reverse Proxy

Use Nginx as a reverse proxy for Suave:

upstream suave {
  server localhost:8080;
  server localhost:8081;
  server localhost:8082;
}

server {
  listen 80;
  server_name example.com;

  location / {
    proxy_pass http://suave;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection keep-alive;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_cache_bypass $http_upgrade;
  }

  # WebSocket support
  location /ws {
    proxy_pass http://suave;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;
  }
}

HTTPS/SSL Configuration

Configure HTTPS in Suave for production:

open System.Security.Cryptography.X509Certificates
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful

let cert = new X509Certificate2("/etc/ssl/certs/server.pfx", "password")

let httpsConfig =
  { defaultConfig with
      bindings = [
        HttpBinding.createSimple HTTP "0.0.0.0" 80
        HttpBinding.createSimple (HTTPS cert) "0.0.0.0" 443
      ]
  }

let app =
  choose [
    path "/" >=> OK "Hello World"
  ]

[<EntryPoint>]
let main argv =
  startWebServer httpsConfig app
  0

Environment Configuration

Configure Suave for different environments:

open Suave
open Suave.Operators
open System

let getConfig () =
  let env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
  let port = 
    match Environment.GetEnvironmentVariable("SUAVE_PORT") with
    | null -> 8080
    | p -> int p

  let config =
    { defaultConfig with
        bindings = [HttpBinding.createSimple HTTP "0.0.0.0" port]
    }
  
  match env with
  | "Production" ->
    { config with bufferSize = 4096; maxOps = 10000 }
  | "Development" ->
    { config with bufferSize = 2048 }
  | _ -> config

[<EntryPoint>]
let main argv =
  startWebServer (getConfig ()) app
  0

Load Balancing

Scale Suave with multiple instances:

# docker-compose with load balancing
version: '3.8'
services:
  suave1:
    build: .
    ports:
      - "8081:8080"
    environment:
      SUAVE_PORT: 8080

  suave2:
    build: .
    ports:
      - "8082:8080"
    environment:
      SUAVE_PORT: 8080

  suave3:
    build: .
    ports:
      - "8083:8080"
    environment:
      SUAVE_PORT: 8080

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx-lb.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - suave1
      - suave2
      - suave3

Monitoring and Logging

Monitor Suave applications in production:

open Suave
open Suave.Operators
open System

let monitoredConfig =
  { defaultConfig with
      bindings = [HttpBinding.createSimple HTTP "0.0.0.0" 8080]
      maxOps = 1000
  }

let loggingMiddleware =
  fun (ctx: HttpContext) ->
    async {
      let startTime = System.DateTime.UtcNow
      printfn "[%s] %s %s" 
        (startTime.ToString("yyyy-MM-dd HH:mm:ss"))
        ctx.request.method
        ctx.request.path
      
      let! result = ctx.request.rawForm |> Async.AwaitTask
      let duration = System.DateTime.UtcNow - startTime
      printfn "[Response] %s %s - %dms" 
        ctx.request.method
        ctx.request.path
        (int duration.TotalMilliseconds)
      return result
    }

let app =
  choose [
    path "/" >=> OK "Hello World"
  ]

[<EntryPoint>]
let main argv =
  startWebServer monitoredConfig (loggingMiddleware >=> app)
  0

Performance Tuning

Optimize Suave for high performance:

open Suave

// Production-optimized configuration
let productionConfig =
  { defaultConfig with
      bindings = [HttpBinding.createSimple HTTP "0.0.0.0" 8080]
      
      // Increase concurrent operations
      maxOps = 10000
      
      // Optimize buffer sizes
      bufferSize = 8192
      
      // Connection settings
      exposeServerHeader = false
      
      // Keep-alive timeout
      homeFolder = Some (System.IO.Directory.GetCurrentDirectory())
      
      // Cancellation token timeout
      cancellationToken = System.Threading.CancellationToken.None
  }

[<EntryPoint>]
let main argv =
  let config = productionConfig
  startWebServer config app
  0