2026-04-18·6 min read·sota.io team

Deploy F# & Giraffe to Europe — EU Hosting for Functional .NET in 2026

F# occupies a unique position in the European technology landscape. It sits at the intersection of two worlds: the functional programming community's demand for correctness and composability, and the .NET ecosystem's industrial-strength runtime, tooling, and enterprise integrations. For EU financial services teams that already run on Azure or .NET — but want the expressiveness and type-safety of a functional language — F# is the answer that doesn't require leaving the JIT.

Nordea, Scandinavia's largest financial group, uses F# for analytical and quantitative systems. Multiple London-based quantitative finance firms use F# for pricing models and risk calculations. A number of EU fintech startups building in the .NET ecosystem choose F# over C# specifically for its ability to encode domain invariants directly in the type system.

This guide covers deploying an F# web API — using Giraffe or Saturn on ASP.NET Core — to European infrastructure with sota.io, the EU-native PaaS running on German servers.

Why F# for EU Financial Services

F# is not widely discussed outside its niche, but within EU quantitative finance and functional .NET development, it has a loyal and growing user base. The reasons are structural:

Discriminated Unions for domain modeling. F# discriminated unions let you express business rules that C# can only approximate with class hierarchies. An Order type can be Pending | Filled of price: decimal | Cancelled of reason: string — exhaustive, compile-time-checked, impossible to mishandle. For trading systems, insurance products, and financial instruments, this eliminates entire categories of "impossible state" bugs.

Railway-oriented programming. F#'s Result<'Ok, 'Error> type enables a compositional error-handling style where operations chain like a railway: success continues on the happy path, errors branch off without exceptions. This maps naturally to financial workflows where every step can fail for multiple domain reasons — insufficient funds, compliance rejection, market hours, counterparty limits.

Type Providers. F#'s killer feature. The SqlClient.SqlProvider generates F# types directly from your database schema at compile time. If your PostgreSQL table changes, your F# code stops compiling. The FSharp.Data.CsvProvider does the same for CSV files. For data-heavy financial applications — regulatory reports, market data feeds, actuals-vs-budget comparisons — this means zero manual serialization code and zero schema drift.

Immutability by default. F# records and union types are immutable. Mutation requires mutable annotation. For audit trails, event sourcing, and financial ledgers where immutability is a regulatory requirement, F# makes the right choice the default choice.

Full .NET ecosystem access. F# compiles to the same IL as C#. Every .NET library works from F#. If your organization already runs on Azure, has NuGet packages, or uses Entity Framework, F# works with all of it. You don't choose between functional programming and the .NET ecosystem — you get both.

Giraffe and Saturn: Web Frameworks for F# on ASP.NET Core

Giraffe is a functional wrapper around ASP.NET Core. It exposes the HttpHandler abstraction — a function from HttpContext to Task<HttpContext option> — which can be composed with operators. An entire API is a single composed expression:

let webApp =
    choose [
        GET >=> choose [
            route "/health" >=> text "OK"
            routef "/users/%i" getUser
        ]
        POST >=> route "/users" >=> bindJson<CreateUser> createUser
        RequestErrors.NOT_FOUND "Not Found"
    ]

No controllers, no magic, no reflection. The entire routing table is a value in your program. If it compiles, the routes are correct.

Saturn builds on Giraffe with a higher-level API closer to Phoenix (Elixir) in style — pipelines, scopes, controllers. If your team is more comfortable with conventional MVC patterns, Saturn provides them in an F#-idiomatic way:

let userRouter = router {
    get "/" getUsers
    getf "/%i" getUser
    post "/" createUser
    putf "/%i/%O" updateUser
    deletef "/%i" deleteUser
}

let appRouter = router {
    forward "/api/users" userRouter
}

Both compile to ASP.NET Core middleware and have identical deployment characteristics — choose based on team preference.

Deploy F# to sota.io in 3 Steps

Step 1: Multi-Stage Dockerfile

F# compiles to .NET IL via the same toolchain as C#. The Dockerfile is nearly identical to an ASP.NET Core deployment:

# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder

WORKDIR /app
COPY *.fsproj .
RUN dotnet restore

COPY . .
RUN dotnet publish -c Release -o /app/publish \
    --no-restore \
    -p:PublishSingleFile=false

# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0

WORKDIR /app
COPY --from=builder /app/publish .

ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080

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

Build time: 2-4 minutes first build (NuGet restore + F# compilation). 60-90 seconds with layer cache. Runtime image size: ~180-220 MB (ASP.NET Core runtime + application) RAM at startup: ~60-100 MB (ASP.NET Core with Kestrel is notably lighter than JVM alternatives)

Step 2: Database Connection with Donald or Dapper

sota.io injects DATABASE_URL automatically when PostgreSQL is attached. For F#, Donald is the idiomatic functional data access library — a thin, composable wrapper around ADO.NET:

open Donald
open Npgsql

let connectionString =
    System.Environment.GetEnvironmentVariable("DATABASE_URL")
    |> NpgsqlConnectionStringBuilder.parse  // parses postgresql://... format

let getUser (id: int) : Async<User option> =
    use conn = new NpgsqlConnection(connectionString)
    conn
    |> Db.newCommand "SELECT id, email, name FROM users WHERE id = @id"
    |> Db.setParams [ "id", SqlType.Int id ]
    |> Db.querySingle (fun rd ->
        { Id = rd.ReadInt32 "id"
          Email = rd.ReadString "email"
          Name = rd.ReadString "name" })
    |> Db.Async.read

For teams already using Entity Framework, it works from F# with query { } computation expressions. For type-provider-based access, FSharp.Data.SqlClient generates typed queries from SQL strings at compile time.

Step 3: Giraffe Application Entry Point

open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.DependencyInjection
open Giraffe

let webApp =
    choose [
        GET >=> route "/health" >=> text "OK"
        GET >=> routef "/users/%i" getUser
        POST >=> route "/users" >=> bindJson<CreateUser> createUser
        RequestErrors.NOT_FOUND "Not found"
    ]

[<EntryPoint>]
let main _ =
    let builder = WebApplication.CreateBuilder()
    builder.Services.AddGiraffe() |> ignore

    let app = builder.Build()
    app.UseGiraffe(webApp)
    app.Run()
    0

Step 4: Deploy

sota deploy

sota.io reads your Dockerfile, builds the F# project in the cloud, and deploys to Germany. TLS is provisioned automatically. PostgreSQL connection string injected as DATABASE_URL.

Performance: F# on .NET 8

RuntimeCold StartRAM (idle)Notes
Giraffe on ASP.NET Core 8~0.3-0.8s~60-100 MBKestrel is production-fast
Saturn on ASP.NET Core 8~0.3-0.8s~65-110 MBSlightly more overhead
.NET 8 Native AOT~50-100ms~20-40 MBRequires AOT-compatible libraries
C# ASP.NET Core (comparison)~0.3-0.8s~55-90 MBSimilar to Giraffe

F# on .NET 8 with Kestrel delivers performance comparable to C# — same IL, same runtime, same HTTP server. The functional abstractions in Giraffe are zero-cost: HttpHandler compositioncompiles down to the same code as a C# controller.

.NET 8 Native AOT is available for F# but comes with constraints: type providers don't work (they require runtime code generation), and some reflection-heavy libraries need substitution. For APIs that don't use type providers, Native AOT is viable and delivers startup times competitive with Go or Rust.

EU Data Residency for .NET Teams

The challenge for EU .NET teams is that the obvious deployment target — Azure — is a US-owned entity under the US CLOUD Act. The Azure Germany North region (formerly operated by T-Systems as the "German cloud") was retired in 2018. Today's Azure Germany regions run on US-owned Microsoft infrastructure with EU region designation — the processor remains a US company.

For BaFin-regulated institutions, DSGVO Article 28 requires an EU-established processor. Microsoft's EU Data Boundary commitments help but don't change the US corporate structure. Legal teams at Volksbank, Allianz, and comparable German institutions routinely flag this.

sota.io is EU-incorporated, operates exclusively on Hetzner Frankfurt, and provides an Article 28 DPA with an EU legal entity. For F# teams in the .NET ecosystem who need true EU data residency without rebuilding on a different runtime, sota.io is the Azure-independent path.

Nordics and DACH: Where F# Is Used in Production

Quantitative Finance (Stockholm/Copenhagen): Nordea's analytical systems, pricing models at Nordic hedge funds. F#'s type providers make quant models type-safe against market data schema changes. Finansinspektionen (Sweden) and Finanstilsynet (Denmark) require EU data processing for customer financial data.

Insurance Pricing (Swiss/Austrian Market): Premium calculation engines where F#'s discriminated unions model insurance product variants precisely. FINMA (Switzerland) and FMA (Austria) require EU data residency for personal data processing.

Domain-Driven Design in DACH Enterprise: F# records and discriminated unions map directly to DDD bounded contexts. German enterprise teams building greenfield microservices increasingly choose F# over C# when they want functional purity without leaving .NET. DSGVO requires EU processors.

London Fintech (EU client data): Post-Brexit, London fintech serving EU clients must keep EU resident data in EU jurisdiction. F# teams in London increasingly need EU-hosted deployments alongside their UK infrastructure — sota.io handles this as a primary use case.

Getting Started with F# on sota.io

# Install sota CLI
curl -fsSL https://sota.io/install.sh | bash

# Deploy an existing F#/Giraffe project
cd your-fsharp-project
sota deploy

# Your API is live at https://yourapp.sota.io in ~5 minutes
# TLS provisioned, PostgreSQL available as DATABASE_URL

If you're starting a new F# project, the Giraffe template is the fastest starting point: dotnet new giraffe -n MyApi. Saturn projects use dotnet new saturn.

Both work on sota.io without modification. Write F# — we handle the EU infrastructure.


Related Guides: