Skip to content

Deployment Container

Jung Hyun, Nam edited this page May 28, 2026 · 2 revisions

Deployment — Container (no Dockerfile)

The .NET SDK can build and push OCI container images directly from dotnet publish — no Dockerfile, no buildx, no separate tool. Cadenza simply lets you point that at a .cs file.

No orchestra — not even a Dockerfile. One file plays it all the way to a container.

The basic command

dotnet publish app.cs --os linux --arch x64 /t:PublishContainer

The SDK builds your app, layers it onto a base image, and tags it in the local Docker / Podman daemon. Push to a registry by adding ContainerRegistry (see below).

Critical caveat: bind to 0.0.0.0

Inside a container, binding to localhost (the loopback interface) makes the service unreachable from outside. Bind to 0.0.0.0. How you do this differs between the SDKs.

Cadenza.Web

There is no Cadenza-specific host helper. Use the standard ASP.NET Core mechanism — the ASPNETCORE_URLS env var (or the --urls arg):

#:sdk Cadenza.Web@1.0.15

Get("/", () => "Hello from a container");
await Run();

Set the bind URL via #:property so it's baked into the image:

#:property ContainerEnvironmentVariables=ASPNETCORE_URLS=http://0.0.0.0:8080;ASPNETCORE_HTTP_PORTS=8080

Or set it at the launch site:

docker run -e ASPNETCORE_URLS=http://0.0.0.0:8080 -p 8080:8080 my-image:latest

Cadenza.Agent

Agent.HostName defaults to localhost for safety. Inside a container, set it to 0.0.0.0 in the script itself:

#!/usr/bin/env dotnet run
#:sdk Cadenza.Agent@1.0.15

Agent.HostName = "0.0.0.0";
Agent.Port     = 8080;

SystemPrompt("You are a helpful assistant.");
UseOllama("llama3.2");
await Run();

HostName, Port, and ServedModelName are property assignments — not function calls.

Cadenza.Worker

A worker without an HTTP endpoint has nothing to bind. Just publish and run.

Cadenza.Mcp

MCP uses stdio, not HTTP. Clients launch the container with docker run -i and pipe over stdin/stdout — no bind address to worry about:

{
  "mcpServers": {
    "my-tools": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "my-image:latest"]
    }
  }
}

Choosing a base image

Goal Base
Smallest size (AOT + musl) mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine
Smallest size (AOT + chiseled Ubuntu) mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled
JIT, debuggable mcr.microsoft.com/dotnet/runtime:10.0
ASP.NET (default for Web / Agent) mcr.microsoft.com/dotnet/aspnet:10.0

Set it via #:property:

#:property ContainerBaseImage=mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine
#:property ContainerRepository=myorg/myapp
#:property ContainerImageTag=1.0.0

For the smallest images, combine AOT + an Alpine or chiseled base.

Pushing to a registry

Set ContainerRegistry at the command line (or via #:property):

dotnet publish app.cs --os linux --arch x64 /t:PublishContainer \
  /p:ContainerRegistry=ghcr.io \
  /p:ContainerRepository=rkttu/cadenza-demo \
  /p:ContainerImageTag=1.0.0

The SDK uses your existing registry credentials — set them up the way you normally would.

Registry Credential helper
Docker Hub docker login
GHCR docker login ghcr.io -u <user> -p <PAT> (read:packages, write:packages scopes)
AWS ECR aws ecr get-login-passworddocker login
Azure ACR az acr login --name <registry>
GCP GAR gcloud auth configure-docker

Multi-arch images

Publish for each platform and tag them under one manifest. The simplest path is to publish twice and manage the manifest list yourself:

dotnet publish app.cs --os linux --arch x64   /t:PublishContainer \
  /p:ContainerImageTag=1.0.0-amd64
dotnet publish app.cs --os linux --arch arm64 /t:PublishContainer \
  /p:ContainerImageTag=1.0.0-arm64

docker manifest create myorg/myapp:1.0.0 \
  myorg/myapp:1.0.0-amd64 \
  myorg/myapp:1.0.0-arm64
docker manifest push myorg/myapp:1.0.0

Putting it together: smallest possible web image

#!/usr/bin/env dotnet run
#:sdk Cadenza.Web@1.0.15

#:property PublishAot=true
#:property InvariantGlobalization=true
#:property ContainerBaseImage=mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine
#:property ContainerRepository=myorg/hello
#:property ContainerImageTag=1.0.0
#:property ContainerEnvironmentVariables=ASPNETCORE_URLS=http://0.0.0.0:8080;ASPNETCORE_HTTP_PORTS=8080
#:property ContainerPort=8080

Get("/", () => "Hello, container");
await Run();
dotnet publish app.cs --os linux --arch x64 /t:PublishContainer
docker run -p 8080:8080 myorg/hello:1.0.0
curl http://localhost:8080

See also

Clone this wiki locally