Size the kernel for a container
This page describes how the Chronicle kernel image runs today, which .NET garbage collection (GC) settings you can change, and how to size a container limit from your own workload. For the general production checklist, see Production deployment. For single-instance platforms, see Container Apps, App Service and Cloud Run.
What the image runs with
Section titled “What the image runs with”The production image is built with these settings:
- Server GC. The kernel project sets
ServerGarbageCollectiontotrue. - Conserve memory. The runtime configuration sets
System.GC.ConserveMemoryto5. A higher value makes the GC trade more frequent collections, and possibly longer pauses, for a smaller heap. - Self-contained ReadyToRun publish. The release workflow publishes
linux-x64andlinux-arm64withPublishReadyToRun=trueand--self-contained. - Base image.
mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extra. The image runs./Cratis.Chronicle.Serverdirectly as the entry point. - Host shutdown timeout. The host waits 10 seconds for a graceful stop.
The kernel does not set any other GC option, such as GCHeapHardLimit, GCDynamicAdaptationMode or GCHeapCount.
How the runtime reads container limits
Section titled “How the runtime reads container limits”When the process runs in a container with a memory limit, the .NET GC treats that limit as the machine’s physical memory. If you do not set a heap hard limit, the default in that situation is the larger of 20 MB and 75 % of the container limit. See Heap hard limit and Heap hard limit percent in the .NET documentation.
GC settings you can change
Section titled “GC settings you can change”Set a GC option as an environment variable on the container, or as a configProperties entry in the runtime configuration. Environment variable values for DOTNET_GC* options that take numbers are hexadecimal, while the runtime configuration takes decimal values. For example, DOTNET_GCHeapHardLimitPercent=0x4B is 75 %.
| Environment variable | Runtime configuration name | Values | Effect |
|---|---|---|---|
DOTNET_gcServer |
System.GC.Server |
1 server, 0 workstation |
Selects server or workstation GC. Workstation GC is the .NET default. |
DOTNET_GCConserveMemory |
System.GC.ConserveMemory |
0 to 9 |
Higher values keep the heap smaller, with more frequent collections and possibly longer pauses. |
DOTNET_GCHeapHardLimit |
System.GC.HeapHardLimit |
Bytes, hexadecimal in the environment | Caps the commit size of the GC heap and its bookkeeping. |
DOTNET_GCHeapHardLimitPercent |
System.GC.HeapHardLimitPercent |
Percentage, hexadecimal in the environment | Caps the heap as a percentage of the container limit. Ignored when GCHeapHardLimit is set. |
DOTNET_GCDynamicAdaptationMode |
System.GC.DynamicAdaptationMode |
1 enabled, 0 disabled |
Turns dynamic adaptation to application sizes (DATAS) on or off. DATAS aims to keep the heap roughly proportional to the long-lived data size. |
Each option is described in Runtime configuration options for garbage collection.
Run the image with a memory limit and a heap cap:
docker run --memory 2g -e DOTNET_GCHeapHardLimitPercent=0x4B <image>Set the same variables in Docker Compose:
services: chronicle: mem_limit: 2g environment: DOTNET_GCHeapHardLimitPercent: "0x4B"Replace <image> with the Chronicle production image and tag you deploy, and replace the example limits with values from your own workload.
Size from your own peak
Section titled “Size from your own peak”Memory use depends on your workload. It grows with the number of event stores, namespaces and observers, because observer grains stay resident once they are activated.
- Run your real workload without a hard heap limit.
- Observe peak resident memory. Use the .NET runtime metrics described in OpenTelemetry, or
docker stats. - Set the container memory limit above the observed peak.
- Set a hard heap limit only when you have a reason to, and keep it above the peak heap size you observed.
A limit below what the kernel needs is not safe. Docker warns that a process can be killed when a container runs out of memory; see Resource constraints.
Shutdown
Section titled “Shutdown”Set the termination grace period to at least the 10 second host timeout. Observer progress that is not saved before the process stops is delivered again after the restart. See Rolling deploy and Graceful shutdown.
Storage on container platforms
Section titled “Storage on container platforms”Do not use SQLite on platforms where the container file system is lost on restart. See Container Apps, App Service and Cloud Run.