Skip to main content

F# on Miren

F# isn't auto-detected, so you deploy it with a Dockerfile.miren — the same .NET toolchain as C#, just with F# source. This guide uses an ASP.NET Core minimal API; the pattern also works for Giraffe and Saturn.

Let your agent do this

Ask your AI coding agent to "set up this F# app on Miren" after installing the Miren agent skills. It adds the Dockerfile.miren, points Kestrel at 0.0.0.0:$PORT, and deploys — using this page as its reference.

Does this source build need a Dockerfile?​

Yes. Miren doesn't auto-detect .NET, so add a Dockerfile.miren to your project root. Miren builds from it instead of guessing the stack — see Using Dockerfile.miren.

Want native support?

Miren auto-detects and builds common stacks (Python, Node, Bun, Go, Ruby, Rust) without a Dockerfile. This language isn't one of them yet — if you'd like first-class support, request it.

Bind to the injected port​

Miren injects PORT and routes traffic to it, so Kestrel must listen on 0.0.0.0 at that port. Pass the URL to app.Run:

open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Http

[<EntryPoint>]
let main args =
let builder = WebApplication.CreateBuilder(args)
let app = builder.Build()
app.MapGet("/", Func<string>(fun () -> "Hello from F# on Miren!\n")) |> ignore
let port = Environment.GetEnvironmentVariable("PORT")
let port = if String.IsNullOrEmpty(port) then "8080" else port
app.Run(sprintf "http://0.0.0.0:%s" port)
0

An .fsproj using the web SDK:

<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>

(F# compiles files in listed order, so keep Program.fs last.)

The Dockerfile​

Create Dockerfile.miren in your project root. Replace fsharp-bench.dll with your project's assembly name:

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

# ----- Runtime stage -----
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=builder /out .
EXPOSE 8080
CMD ["dotnet", "fsharp-bench.dll"]

.dockerignore​

.git
bin
obj

Deploy​

The Dockerfile's CMD starts the app. Miren uses it as the web service's startup default, so you don't need a Procfile or service command.

Create .miren/app.toml naming your app and deploy from your project root:

name = "fsharp-bench"
miren deploy

Environment variables​

Set variables with miren env set — -e for plain values, -s for secrets (masked in output and logs):

miren env set -e ASPNETCORE_ENVIRONMENT=Production
miren env set -s ConnectionStrings__Default

__ in an env var name maps to nested configuration keys. You can also declare variables in .miren/app.toml:

[[env]]
key = "ASPNETCORE_ENVIRONMENT"
value = "Production"

See App Configuration — Environment Variables.

Agent quick reference​

  • Detection: none — requires Dockerfile.miren (same toolchain as C#)
  • Build: dotnet publish -c Release -o /out on the SDK image; run on dotnet/aspnet
  • fsproj: use Microsoft.NET.Sdk.Web; list <Compile Include> files in dependency order
  • Startup: inherited from the Dockerfile CMD; no Procfile or service command needed
  • Port: app.Run(sprintf "http://0.0.0.0:%s" port) (reading PORT in code; ASPNETCORE_URLS is not shell-expanded)
  • Env vars: miren env set -e/-s; __ maps to nested config keys

Next steps​