[{"data":1,"prerenderedAt":1565},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fruntime\u002Fdocker":314,"\u002Fdocs\u002Fruntime\u002Fdocker-surround":1560},[4,38,86,149,176,219,257,273],{"title":5,"path":6,"stem":7,"children":8,"icon":37},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,17,22,27,32],{"title":10,"path":6,"stem":7,"icon":11},"Getting started","i-lucide-flag",{"title":13,"path":14,"stem":15,"icon":16},"Modules overview","\u002Fdocs\u002Fgetting-started\u002Fmodules-overview","1.docs\u002F1.getting-started\u002F2.modules-overview","i-lucide-boxes",{"title":18,"path":19,"stem":20,"icon":21},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","1.docs\u002F1.getting-started\u002F3.installation","i-lucide-download",{"title":23,"path":24,"stem":25,"icon":26},"License configuration","\u002Fdocs\u002Fgetting-started\u002Flicense-configuration","1.docs\u002F1.getting-started\u002F4.license-configuration","i-lucide-key-round",{"title":28,"path":29,"stem":30,"icon":31},"Your first app","\u002Fdocs\u002Fgetting-started\u002Ffirst-app","1.docs\u002F1.getting-started\u002F5.first-app","i-lucide-square-play",{"title":33,"path":34,"stem":35,"icon":36},"Project setup","\u002Fdocs\u002Fgetting-started\u002Fproject-setup","1.docs\u002F1.getting-started\u002F6.project-setup","i-lucide-package",false,{"title":39,"path":40,"stem":41,"children":42,"icon":45},"Core Concepts","\u002Fdocs\u002Fconcepts","1.docs\u002F2.concepts\u002F1.index",[43,46,51,56,61,66,71,76,81],{"title":44,"path":40,"stem":41,"icon":45},"Core concepts","i-lucide-book-open",{"title":47,"path":48,"stem":49,"icon":50},"Architecture","\u002Fdocs\u002Fconcepts\u002Farchitecture","1.docs\u002F2.concepts\u002F2.architecture","i-lucide-layers",{"title":52,"path":53,"stem":54,"icon":55},"Exactly-once semantics","\u002Fdocs\u002Fconcepts\u002Fexactly-once","1.docs\u002F2.concepts\u002F3.exactly-once","i-lucide-shield-check",{"title":57,"path":58,"stem":59,"icon":60},"Lanes and parallelism","\u002Fdocs\u002Fconcepts\u002Flanes-and-parallelism","1.docs\u002F2.concepts\u002F4.lanes-and-parallelism","i-lucide-split",{"title":62,"path":63,"stem":64,"icon":65},"State and thread-safety","\u002Fdocs\u002Fconcepts\u002Fstate-and-thread-safety","1.docs\u002F2.concepts\u002F5.state-and-thread-safety","i-lucide-lock",{"title":67,"path":68,"stem":69,"icon":70},"Event time and watermarks","\u002Fdocs\u002Fconcepts\u002Fevent-time-and-watermarks","1.docs\u002F2.concepts\u002F6.event-time-and-watermarks","i-lucide-clock",{"title":72,"path":73,"stem":74,"icon":75},"The configuration model","\u002Fdocs\u002Fconcepts\u002Fconfiguration-model","1.docs\u002F2.concepts\u002F7.configuration-model","i-lucide-sliders-horizontal",{"title":77,"path":78,"stem":79,"icon":80},"The error-handling model","\u002Fdocs\u002Fconcepts\u002Ferror-handling-model","1.docs\u002F2.concepts\u002F8.error-handling-model","i-lucide-triangle-alert",{"title":82,"path":83,"stem":84,"icon":85},"How StoatFlow differs from Kafka Streams","\u002Fdocs\u002Fconcepts\u002Fhow-stoatflow-differs-from-ks","1.docs\u002F2.concepts\u002F9.how-stoatflow-differs-from-ks","i-lucide-arrow-left-right",{"title":87,"path":88,"stem":89,"children":90,"icon":93},"Building Topologies","\u002Fdocs\u002Fbuilding","1.docs\u002F3.building\u002F1.index",[91,94,99,104,109,114,119,124,129,134,139,144],{"title":92,"path":88,"stem":89,"icon":93},"Building topologies","i-lucide-blocks",{"title":95,"path":96,"stem":97,"icon":98},"Error handling and DLQ","\u002Fdocs\u002Fbuilding\u002Ferror-handling-dlq","1.docs\u002F3.building\u002F10.error-handling-dlq","i-lucide-circle-x",{"title":100,"path":101,"stem":102,"icon":103},"State stores","\u002Fdocs\u002Fbuilding\u002Fstate-stores","1.docs\u002F3.building\u002F11.state-stores","i-lucide-database",{"title":105,"path":106,"stem":107,"icon":108},"Testing topologies","\u002Fdocs\u002Fbuilding\u002Ftesting","1.docs\u002F3.building\u002F12.testing","i-lucide-flask-conical",{"title":110,"path":111,"stem":112,"icon":113},"Sources and sinks","\u002Fdocs\u002Fbuilding\u002Fstreams-builder","1.docs\u002F3.building\u002F2.streams-builder","i-lucide-import",{"title":115,"path":116,"stem":117,"icon":118},"KStream and KTable operations","\u002Fdocs\u002Fbuilding\u002Fkstream-ktable","1.docs\u002F3.building\u002F3.kstream-ktable","i-lucide-waypoints",{"title":120,"path":121,"stem":122,"icon":123},"Aggregations","\u002Fdocs\u002Fbuilding\u002Faggregations","1.docs\u002F3.building\u002F4.aggregations","i-lucide-sigma",{"title":125,"path":126,"stem":127,"icon":128},"Windowing","\u002Fdocs\u002Fbuilding\u002Fwindowing","1.docs\u002F3.building\u002F5.windowing","i-lucide-calendar-clock",{"title":130,"path":131,"stem":132,"icon":133},"Joins","\u002Fdocs\u002Fbuilding\u002Fjoins","1.docs\u002F3.building\u002F6.joins","i-lucide-git-merge",{"title":135,"path":136,"stem":137,"icon":138},"The Processor API","\u002Fdocs\u002Fbuilding\u002Fprocessor-api","1.docs\u002F3.building\u002F7.processor-api","i-lucide-cpu",{"title":140,"path":141,"stem":142,"icon":143},"Scheduled sources","\u002Fdocs\u002Fbuilding\u002Fscheduled-sources","1.docs\u002F3.building\u002F8.scheduled-sources","i-lucide-timer",{"title":145,"path":146,"stem":147,"icon":148},"Serdes and Avro","\u002Fdocs\u002Fbuilding\u002Fserdes","1.docs\u002F3.building\u002F9.serdes","i-lucide-binary",{"title":150,"path":151,"stem":152,"children":153,"icon":75},"Configuration","\u002Fdocs\u002Fconfiguration","1.docs\u002F4.configuration\u002F1.index",[154,156,161,166,171],{"title":155,"path":151,"stem":152,"icon":75},"How configuration works",{"title":157,"path":158,"stem":159,"icon":160},"Engine configuration (:core)","\u002Fdocs\u002Fconfiguration\u002Fcore-config","1.docs\u002F4.configuration\u002F2.core-config","i-lucide-settings-2",{"title":162,"path":163,"stem":164,"icon":165},"Runtime configuration (:runtime)","\u002Fdocs\u002Fconfiguration\u002Fruntime-config","1.docs\u002F4.configuration\u002F3.runtime-config","i-lucide-server-cog",{"title":167,"path":168,"stem":169,"icon":170},"Defaults, adaptivity, and presets","\u002Fdocs\u002Fconfiguration\u002Fdefaults-and-presets","1.docs\u002F4.configuration\u002F4.defaults-and-presets","i-lucide-gauge",{"title":172,"path":173,"stem":174,"icon":175},"Kafka client configuration","\u002Fdocs\u002Fconfiguration\u002Fkafka-client-config","1.docs\u002F4.configuration\u002F5.kafka-client-config","i-lucide-plug",{"title":177,"path":178,"stem":179,"children":180,"icon":183},"Running in Production","\u002Fdocs\u002Fruntime","1.docs\u002F5.runtime\u002F1.index",[181,184,189,194,199,204,209,214],{"title":182,"path":178,"stem":179,"icon":183},"The runtime","i-lucide-server",{"title":185,"path":186,"stem":187,"icon":188},"The REST API","\u002Fdocs\u002Fruntime\u002Frest-api","1.docs\u002F5.runtime\u002F2.rest-api","i-lucide-globe",{"title":190,"path":191,"stem":192,"icon":193},"Health checks","\u002Fdocs\u002Fruntime\u002Fhealth-checks","1.docs\u002F5.runtime\u002F3.health-checks","i-lucide-heart-pulse",{"title":195,"path":196,"stem":197,"icon":198},"Metrics","\u002Fdocs\u002Fruntime\u002Fmetrics","1.docs\u002F5.runtime\u002F4.metrics","i-lucide-activity",{"title":200,"path":201,"stem":202,"icon":203},"Pause and resume","\u002Fdocs\u002Fruntime\u002Fpause-unpause","1.docs\u002F5.runtime\u002F5.pause-unpause","i-lucide-pause",{"title":205,"path":206,"stem":207,"icon":208},"Plugins and lifecycle hooks","\u002Fdocs\u002Fruntime\u002Fplugins","1.docs\u002F5.runtime\u002F6.plugins","i-lucide-puzzle",{"title":210,"path":211,"stem":212,"icon":213},"Docker images","\u002Fdocs\u002Fruntime\u002Fdocker","1.docs\u002F5.runtime\u002F7.docker","i-lucide-container",{"title":215,"path":216,"stem":217,"icon":218},"GraalVM native image","\u002Fdocs\u002Fruntime\u002Fnative-image","1.docs\u002F5.runtime\u002F8.native-image","i-lucide-zap",{"title":220,"path":221,"stem":222,"children":223,"icon":226},"Deploying & Operating","\u002Fdocs\u002Foperating","1.docs\u002F6.operating\u002F1.index",[224,227,232,237,242,247,252],{"title":225,"path":221,"stem":222,"icon":226},"Deploying and operating","i-lucide-life-buoy",{"title":228,"path":229,"stem":230,"icon":231},"Running on Kubernetes","\u002Fdocs\u002Foperating\u002Fkubernetes","1.docs\u002F6.operating\u002F2.kubernetes","i-lucide-ship",{"title":233,"path":234,"stem":235,"icon":236},"High availability","\u002Fdocs\u002Foperating\u002Fhigh-availability","1.docs\u002F6.operating\u002F3.high-availability","i-lucide-copy",{"title":238,"path":239,"stem":240,"icon":241},"Liveness and readiness probes","\u002Fdocs\u002Foperating\u002Fprobes","1.docs\u002F6.operating\u002F4.probes","i-lucide-stethoscope",{"title":243,"path":244,"stem":245,"icon":246},"Observability","\u002Fdocs\u002Foperating\u002Fobservability","1.docs\u002F6.operating\u002F5.observability","i-lucide-telescope",{"title":248,"path":249,"stem":250,"icon":251},"Tuning under load","\u002Fdocs\u002Foperating\u002Ftuning","1.docs\u002F6.operating\u002F6.tuning","i-lucide-sliders",{"title":253,"path":254,"stem":255,"icon":256},"Production checklist","\u002Fdocs\u002Foperating\u002Fproduction-checklist","1.docs\u002F6.operating\u002F7.production-checklist","i-lucide-clipboard-check",{"title":258,"path":259,"stem":260,"children":261,"icon":85},"Migrating from Kafka Streams","\u002Fdocs\u002Fmigration","1.docs\u002F7.migration\u002F1.index",[262,263,268],{"title":258,"path":259,"stem":260,"icon":85},{"title":264,"path":265,"stem":266,"icon":267},"Migration without carrying state","\u002Fdocs\u002Fmigration\u002Fwithout-data-migration","1.docs\u002F7.migration\u002F2.without-data-migration","i-lucide-sparkles",{"title":269,"path":270,"stem":271,"icon":272},"Migration carrying state","\u002Fdocs\u002Fmigration\u002Fwith-data-migration","1.docs\u002F7.migration\u002F3.with-data-migration","i-lucide-database-backup",{"title":274,"path":275,"stem":276,"children":277,"icon":279},"Reference","\u002Fdocs\u002Freference","1.docs\u002F8.reference\u002F1.index",[278,280,285,290,295,300,305,309],{"title":274,"path":275,"stem":276,"icon":279},"i-lucide-list",{"title":281,"path":282,"stem":283,"icon":284},"Configuration reference","\u002Fdocs\u002Freference\u002Fconfiguration-reference","1.docs\u002F8.reference\u002F2.configuration-reference","i-lucide-table",{"title":286,"path":287,"stem":288,"icon":289},"REST API reference","\u002Fdocs\u002Freference\u002Frest-api-reference","1.docs\u002F8.reference\u002F3.rest-api-reference","i-lucide-network",{"title":291,"path":292,"stem":293,"icon":294},"Gradle plugin reference","\u002Fdocs\u002Freference\u002Fgradle-plugin-reference","1.docs\u002F8.reference\u002F4.gradle-plugin-reference","i-lucide-box",{"title":296,"path":297,"stem":298,"icon":299},"Maven reference","\u002Fdocs\u002Freference\u002Fmaven-reference","1.docs\u002F8.reference\u002F5.maven-reference","i-simple-icons-apachemaven",{"title":301,"path":302,"stem":303,"icon":304},"Kafka Streams compatibility matrix","\u002Fdocs\u002Freference\u002Fks-compatibility-matrix","1.docs\u002F8.reference\u002F6.ks-compatibility-matrix","i-lucide-table-2",{"title":306,"path":307,"stem":308,"icon":170},"Metrics reference","\u002Fdocs\u002Freference\u002Fmetrics-reference","1.docs\u002F8.reference\u002F7.metrics-reference",{"title":310,"path":311,"stem":312,"icon":313},"Glossary","\u002Fdocs\u002Freference\u002Fglossary","1.docs\u002F8.reference\u002F8.glossary","i-lucide-book-a",{"id":315,"title":210,"body":316,"description":1553,"extension":1554,"meta":1555,"navigation":1556,"pageType":1557,"path":211,"seo":1558,"stem":212,"__hash__":1559},"docs\u002F1.docs\u002F5.runtime\u002F7.docker.md",{"type":317,"value":318,"toc":1542},"minimark",[319,349,440,445,459,599,602,639,653,689,693,696,906,921,925,932,953,962,1047,1051,1076,1080,1102,1105,1135,1138,1190,1196,1218,1251,1255,1279,1350,1357,1434,1438,1472,1494,1498,1538],[320,321,322,323,330,331,340,341,344,345,348],"p",{},"The supported way to containerize a StoatFlow app is the StoatFlow build conventions' ",[324,325,329],"a",{"href":326,"rel":327},"https:\u002F\u002Fgithub.com\u002FGoogleContainerTools\u002Fjib",[328],"nofollow","Jib"," integration — ",[332,333,334,335,339],"strong",{},"no ",[336,337,338],"code",{},"Dockerfile",", no Docker daemon to assemble layers",". It produces a reproducible image wired with the JVM flags StoatFlow needs and an entrypoint that sizes the heap from the container's memory limit at startup. Both the ",[324,342,343],{"href":34},"Gradle plugin"," and the ",[324,346,347],{"href":297},"Maven"," conventions build the same image.",[350,351,352],"tldr-panel",{},[353,354,355,370,384,401,428],"ul",{},[356,357,358,361,362,365,366,369],"li",{},[332,359,360],{},"Gradle:"," ",[336,363,364],{},"stoatflow { docker { enabled.set(true) } }",", then ",[336,367,368],{},".\u002Fgradlew jibDockerBuild",".",[356,371,372,375,376,379,380,383],{},[332,373,374],{},"Maven:"," set ",[336,377,378],{},"\u003Cstoatflow.docker.*>"," properties, then ",[336,381,382],{},"mvn -Pstoatflow-docker package"," (one command).",[356,385,386,361,389,392,393,396,397,400],{},[332,387,388],{},"Base image:",[336,390,391],{},"eclipse-temurin:25-jre-noble",", built for the host architecture (",[336,394,395],{},"linux\u002Farm64"," or ",[336,398,399],{},"linux\u002Famd64",").",[356,402,403,406,407,410,411,410,414,417,418,421,422,361,425,369],{},[332,404,405],{},"Set for you:"," the ",[336,408,409],{},"--enable-preview"," \u002F ",[336,412,413],{},"--enable-native-access=ALL-UNNAMED",[336,415,416],{},"-XX:+UseG1GC"," flags, port ",[336,419,420],{},"8080"," exposed, a non-root user, a heap-sizing entrypoint, and a ",[332,423,424],{},"reproducible",[336,426,427],{},"creationTime",[356,429,430,361,433,436,437,369],{},[332,431,432],{},"Full knob list:",[324,434,291],{"href":435},"\u002Fdocs\u002Freference\u002Fgradle-plugin-reference#docker-image-defaults"," · ",[324,438,296],{"href":439},"\u002Fdocs\u002Freference\u002Fmaven-reference#properties-reference",[441,442,444],"h2",{"id":443},"build-the-image-jib","Build the image (Jib)",[320,446,447,448,450,451,454,455,458],{},"Container support is opt-in. Apply the conventions (see ",[324,449,33],{"href":34},"), set ",[336,452,453],{},"mainClass",", and turn the ",[336,456,457],{},"docker"," build on:",[460,461,463,567],"code-tabs",{"group":462},"build",[464,465,471],"pre",{"className":466,"code":467,"filename":468,"language":469,"meta":470,"style":470},"language-kotlin shiki shiki-themes vitesse-light","\u002F\u002F build.gradle.kts\nstoatflow {\n    mainClass.set(\"com.example.MainKt\")\n    docker {\n        enabled.set(true)\n        imageName.set(\"acme\u002Fword-count\")   \u002F\u002F default: the Gradle project name\n    }\n}\n","Gradle","kotlin","",[336,472,473,482,493,512,520,536,555,561],{"__ignoreMap":470},[474,475,478],"span",{"class":476,"line":477},"line",1,[474,479,481],{"class":480},"s8zF2","\u002F\u002F build.gradle.kts\n",[474,483,485,489],{"class":476,"line":484},2,[474,486,488],{"class":487},"sySUi","stoatflow",[474,490,492],{"class":491},"suHK_"," {\n",[474,494,496,499,502,505,509],{"class":476,"line":495},3,[474,497,498],{"class":491},"    mainClass.",[474,500,501],{"class":487},"set",[474,503,504],{"class":491},"(",[474,506,508],{"class":507},"spphp","\"com.example.MainKt\"",[474,510,511],{"class":491},")\n",[474,513,515,518],{"class":476,"line":514},4,[474,516,517],{"class":487},"    docker",[474,519,492],{"class":491},[474,521,523,526,528,530,534],{"class":476,"line":522},5,[474,524,525],{"class":491},"        enabled.",[474,527,501],{"class":487},[474,529,504],{"class":491},[474,531,533],{"class":532},"sbBg2","true",[474,535,511],{"class":491},[474,537,539,542,544,546,549,552],{"class":476,"line":538},6,[474,540,541],{"class":491},"        imageName.",[474,543,501],{"class":487},[474,545,504],{"class":491},[474,547,548],{"class":507},"\"acme\u002Fword-count\"",[474,550,551],{"class":491},")   ",[474,553,554],{"class":480},"\u002F\u002F default: the Gradle project name\n",[474,556,558],{"class":476,"line":557},7,[474,559,560],{"class":491},"    }\n",[474,562,564],{"class":476,"line":563},8,[474,565,566],{"class":491},"}\n",[464,568,572],{"className":569,"code":570,"filename":347,"language":571,"meta":470,"style":470},"language-xml shiki shiki-themes vitesse-light","\u003C!-- pom.xml -->\n\u003Cproperties>\n  \u003Cstoatflow.mainClass>com.example.Main\u003C\u002Fstoatflow.mainClass>\n  \u003Cstoatflow.docker.imageName>acme\u002Fword-count\u003C\u002Fstoatflow.docker.imageName>\n\u003C\u002Fproperties>\n","xml",[336,573,574,579,584,589,594],{"__ignoreMap":470},[474,575,576],{"class":476,"line":477},[474,577,578],{},"\u003C!-- pom.xml -->\n",[474,580,581],{"class":476,"line":484},[474,582,583],{},"\u003Cproperties>\n",[474,585,586],{"class":476,"line":495},[474,587,588],{},"  \u003Cstoatflow.mainClass>com.example.Main\u003C\u002Fstoatflow.mainClass>\n",[474,590,591],{"class":476,"line":514},[474,592,593],{},"  \u003Cstoatflow.docker.imageName>acme\u002Fword-count\u003C\u002Fstoatflow.docker.imageName>\n",[474,595,596],{"class":476,"line":522},[474,597,598],{},"\u003C\u002Fproperties>\n",[320,600,601],{},"Then build the image into your local Docker daemon:",[460,603,604,619],{"group":462},[464,605,609],{"className":606,"code":607,"filename":468,"language":608,"meta":470,"style":470},"language-bash shiki shiki-themes vitesse-light",".\u002Fgradlew jibDockerBuild\n","bash",[336,610,611],{"__ignoreMap":470},[474,612,613,616],{"class":476,"line":477},[474,614,615],{"class":487},".\u002Fgradlew",[474,617,618],{"class":507}," jibDockerBuild\n",[464,620,622],{"className":606,"code":621,"filename":347,"language":608,"meta":470,"style":470},"mvn -Pstoatflow-docker package   # fat JAR + image, one command\n",[336,623,624],{"__ignoreMap":470},[474,625,626,629,633,636],{"class":476,"line":477},[474,627,628],{"class":487},"mvn",[474,630,632],{"class":631},"sEi1f"," -Pstoatflow-docker",[474,634,635],{"class":507}," package",[474,637,638],{"class":480},"   # fat JAR + image, one command\n",[320,640,641,642,644,645,648,649,652],{},"Jib reads your compiled classes and dependencies and assembles an OCI image directly — no ",[336,643,338],{},", no intermediate fat JAR to manage. The image is tagged ",[336,646,647],{},"latest"," and the project version, named after ",[336,650,651],{},"imageName"," (or the project name if unset).",[654,655,658],"callout",{"color":656,"icon":657},"info","i-lucide-info",[320,659,660,410,663,665,666,669,670,673,674,676,677,680,681,684,685,688],{},[336,661,662],{},"jibDockerBuild",[336,664,382],{}," needs a reachable Docker daemon to receive the image, but Jib does ",[332,667,668],{},"not"," run a ",[336,671,672],{},"docker build"," — it constructs the layers itself. On macOS the build resolves the ",[336,675,457],{}," executable from common install paths (",[336,678,679],{},"\u002Fusr\u002Flocal\u002Fbin\u002Fdocker",", ",[336,682,683],{},"\u002Fopt\u002Fhomebrew\u002Fbin\u002Fdocker",") when the daemon's ",[336,686,687],{},"PATH"," doesn't include them.",[441,690,692],{"id":691},"what-the-build-configures","What the build configures",[320,694,695],{},"The Jib image has the following baked in:",[697,698,699,715],"table",{},[700,701,702],"thead",{},[703,704,705,709,712],"tr",{},[706,707,708],"th",{},"Aspect",[706,710,711],{},"Value",[706,713,714],{},"Knob",[716,717,718,735,756,770,786,803,821,835,858,881],"tbody",{},[703,719,720,726,730],{},[721,722,723],"td",{},[332,724,725],{},"Base image",[721,727,728],{},[336,729,391],{},[721,731,732],{},[336,733,734],{},"baseImage",[703,736,737,742,753],{},[721,738,739],{},[332,740,741],{},"Platform",[721,743,744,746,747,750,751],{},[336,745,395],{}," on an ",[336,748,749],{},"aarch64"," host, otherwise ",[336,752,399],{},[721,754,755],{},"—",[703,757,758,763,768],{},[721,759,760],{},[332,761,762],{},"Tags",[721,764,765,767],{},[336,766,647],{}," and the project version",[721,769,755],{},[703,771,772,778,781],{},[721,773,774],{},[332,775,776],{},[336,777,427],{},[721,779,780],{},"HEAD commit timestamp (reproducible)",[721,782,783],{},[336,784,785],{},"reproducibleBuild",[703,787,788,793,798],{},[721,789,790],{},[332,791,792],{},"Exposed port",[721,794,795,797],{},[336,796,420],{}," — the runtime's HTTP + metrics server",[721,799,800],{},[336,801,802],{},"port",[703,804,805,810,816],{},[721,806,807],{},[332,808,809],{},"User",[721,811,812,815],{},[336,813,814],{},"1000:1000"," (non-root)",[721,817,818],{},[336,819,820],{},"user",[703,822,823,828,833],{},[721,824,825],{},[332,826,827],{},"Working directory",[721,829,830],{},[336,831,832],{},"\u002Fapp",[721,834,755],{},[703,836,837,842,856],{},[721,838,839],{},[332,840,841],{},"Entrypoint",[721,843,844,845,848,849,852,853],{},"a heap-sizing script (",[336,846,847],{},"\u002Fapp\u002Fstoatflow-entrypoint.sh",") that ",[336,850,851],{},"exec","s ",[336,854,855],{},"java",[721,857,755],{},[703,859,860,865,877],{},[721,861,862],{},[332,863,864],{},"JVM flags",[721,866,867,680,869,680,871,873,874],{},[336,868,409],{},[336,870,413],{},[336,872,416],{},", plus your ",[336,875,876],{},"jvmFlags",[721,878,879],{},[336,880,876],{},[703,882,883,888,898],{},[721,884,885],{},[332,886,887],{},"Env",[721,889,890,893,894,897],{},[336,891,892],{},"STOATFLOW_STATE_DIR=\u002Fdata\u002Fstate","; ",[336,895,896],{},"STOATFLOW_ROCKSDB_MB=256"," when RocksDB is on the classpath",[721,899,900,680,903],{},[336,901,902],{},"stateDir",[336,904,905],{},"rocksdbMb",[320,907,908,909,912,913,916,917,344,919,369],{},"Every knob in the right column has the same name in the Gradle ",[336,910,911],{},"docker { }"," block and a ",[336,914,915],{},"stoatflow.docker.\u003Cname>"," Maven property — the full enumeration with types and defaults is in the ",[324,918,291],{"href":435},[324,920,296],{"href":439},[922,923,864],"h3",{"id":924},"jvm-flags",[320,926,927,928,931],{},"The image always carries the flags StoatFlow requires at runtime, the same ones applied to the ",[336,929,930],{},"run"," task:",[353,933,934,939,944],{},[356,935,936,938],{},[336,937,409],{}," — StoatFlow is built on JDK preview features.",[356,940,941,943],{},[336,942,413],{}," — RocksDB's state store uses the Foreign Function & Memory API.",[356,945,946,948,949,952],{},[336,947,416],{}," — short, predictable GC pauses, set explicitly because the JVM only auto-selects G1 on \"server-class\" hardware (≥ 2 CPUs and ≥ ~2 GB memory). See ",[324,950,18],{"href":951},"\u002Fdocs\u002Fgetting-started\u002Finstallation#_3-configure-the-jvm-toolchain"," for the reasoning.",[320,954,955,956,958,959,961],{},"Anything you add via ",[336,957,876],{}," is appended after these (and ",[336,960,876],{}," is itself overridable — the two preview\u002Fnative-access flags are always re-added on top):",[460,963,964,1029],{"group":462},[464,965,967],{"className":466,"code":966,"filename":468,"language":469,"meta":470,"style":470},"stoatflow {\n    docker {\n        enabled.set(true)\n        jvmFlags.add(\"-XX:+ExitOnOutOfMemoryError\")\n        jvmFlags.add(\"-Dsome.property=value\")\n    }\n}\n",[336,968,969,975,981,993,1008,1021,1025],{"__ignoreMap":470},[474,970,971,973],{"class":476,"line":477},[474,972,488],{"class":487},[474,974,492],{"class":491},[474,976,977,979],{"class":476,"line":484},[474,978,517],{"class":487},[474,980,492],{"class":491},[474,982,983,985,987,989,991],{"class":476,"line":495},[474,984,525],{"class":491},[474,986,501],{"class":487},[474,988,504],{"class":491},[474,990,533],{"class":532},[474,992,511],{"class":491},[474,994,995,998,1001,1003,1006],{"class":476,"line":514},[474,996,997],{"class":491},"        jvmFlags.",[474,999,1000],{"class":487},"add",[474,1002,504],{"class":491},[474,1004,1005],{"class":507},"\"-XX:+ExitOnOutOfMemoryError\"",[474,1007,511],{"class":491},[474,1009,1010,1012,1014,1016,1019],{"class":476,"line":522},[474,1011,997],{"class":491},[474,1013,1000],{"class":487},[474,1015,504],{"class":491},[474,1017,1018],{"class":507},"\"-Dsome.property=value\"",[474,1020,511],{"class":491},[474,1022,1023],{"class":476,"line":538},[474,1024,560],{"class":491},[474,1026,1027],{"class":476,"line":557},[474,1028,566],{"class":491},[464,1030,1032],{"className":569,"code":1031,"filename":347,"language":571,"meta":470,"style":470},"\u003Cproperties>\n  \u003Cstoatflow.docker.jvmFlags>-XX:+UseG1GC -XX:+ExitOnOutOfMemoryError\u003C\u002Fstoatflow.docker.jvmFlags>\n\u003C\u002Fproperties>\n",[336,1033,1034,1038,1043],{"__ignoreMap":470},[474,1035,1036],{"class":476,"line":477},[474,1037,583],{},[474,1039,1040],{"class":476,"line":484},[474,1041,1042],{},"  \u003Cstoatflow.docker.jvmFlags>-XX:+UseG1GC -XX:+ExitOnOutOfMemoryError\u003C\u002Fstoatflow.docker.jvmFlags>\n",[474,1044,1045],{"class":476,"line":495},[474,1046,598],{},[922,1048,1050],{"id":1049},"reproducible-builds","Reproducible builds",[320,1052,1053,1054,1056,1057,1059,1060,1063,1064,1067,1068,1071,1072,1075],{},"By default the image is ",[332,1055,424],{},": its ",[336,1058,427],{}," is pinned to your ",[332,1061,1062],{},"HEAD commit timestamp",", not wall-clock — so identical source produces an identical digest with a meaningful ",[336,1065,1066],{},"Created"," date, friendly to layer caching and supply-chain verification. Outside a git repo the timestamp falls back to the Unix epoch. Set ",[336,1069,1070],{},"reproducibleBuild = false"," (Gradle) \u002F ",[336,1073,1074],{},"\u003Cstoatflow.docker.reproducibleBuild>false\u003C\u002F…>"," (Maven) to restore wall-clock timestamps.",[441,1077,1079],{"id":1078},"container-aware-heap-sizing","Container-aware heap sizing",[320,1081,1082,1083,1085,1086,1089,1090,852,1092,1094,1095,1097,1098,1101],{},"The Jib image does ",[332,1084,668],{}," hardcode ",[336,1087,1088],{},"-Xmx",". Its entrypoint computes the heap from the memory available to the container at startup, reserving space for the JVM's own overhead and for RocksDB's off-heap usage, then ",[336,1091,851],{},[336,1093,855],{}," with the computed ",[336,1096,1088],{},". The same image right-sizes itself whether it runs with a 512 MiB limit or a 4 GiB limit — you set the ",[332,1099,1100],{},"container"," memory limit, not a JVM flag.",[320,1103,1104],{},"The memory limit is resolved from the first available source, in order:",[1106,1107,1108,1115,1122,1128],"ol",{},[356,1109,1110,1111,1114],{},"cgroup v2 (",[336,1112,1113],{},"\u002Fsys\u002Ffs\u002Fcgroup\u002Fmemory.max",") — modern containers (Ubuntu 22.04+, Kubernetes 1.25+).",[356,1116,1117,1118,1121],{},"cgroup v1 (",[336,1119,1120],{},"\u002Fsys\u002Ffs\u002Fcgroup\u002Fmemory\u002Fmemory.limit_in_bytes",") — older runtimes.",[356,1123,1124,1127],{},[336,1125,1126],{},"\u002Fproc\u002Fmeminfo"," — bare-metal Linux.",[356,1129,1130,1131,1134],{},"If none report a limit, the entrypoint falls back to ",[336,1132,1133],{},"-XX:MaxRAMPercentage=75.0"," and lets the JVM decide.",[320,1136,1137],{},"When a limit is found, the heap is the total minus a non-heap budget: a JVM baseline (25% of total, capped at 512 MiB) plus the RocksDB reservation. Two environment variables tune this:",[697,1139,1140,1153],{},[700,1141,1142],{},[703,1143,1144,1147,1150],{},[706,1145,1146],{},"Variable",[706,1148,1149],{},"Default",[706,1151,1152],{},"Effect",[716,1154,1155,1177],{},[703,1156,1157,1162,1171],{},[721,1158,1159],{},[336,1160,1161],{},"STOATFLOW_ROCKSDB_MB",[721,1163,1164,1167,1168],{},[336,1165,1166],{},"256"," when RocksDB is on the classpath, else ",[336,1169,1170],{},"0",[721,1172,1173,1174,1176],{},"Off-heap MiB reserved for RocksDB. Subtracted from the heap. Set via the ",[336,1175,905],{}," knob at build time, or override at run time.",[703,1178,1179,1184,1187],{},[721,1180,1181],{},[336,1182,1183],{},"STOATFLOW_NON_HEAP_MB",[721,1185,1186],{},"unset",[721,1188,1189],{},"Explicit total non-heap budget (overrides the baseline + RocksDB calculation). Clamped to ≤ 50% of total.",[320,1191,1192,1193,1195],{},"The entrypoint logs the source, total, non-heap budget, and the chosen ",[336,1194,1088],{}," to stderr at startup, so the sizing decision is visible in the container logs. If the resolved memory is too small to leave a viable heap, it logs a warning and forces a 128 MiB floor (expect an OOMKill — give the container more memory).",[654,1197,1199],{"color":1198,"icon":80},"warning",[320,1200,1201,1203,1204,1206,1207,1210,1211,1213,1214,1217],{},[336,1202,1161],{}," and ",[336,1205,1183],{}," must be ",[332,1208,1209],{},"plain integers"," (MiB) — e.g. ",[336,1212,1166],{},", not ",[336,1215,1216],{},"256m",". The entrypoint fails fast on a non-numeric value.",[654,1219,1220],{"color":656,"icon":657},[320,1221,1222,1225,1226,1229,1230,1233,1234,1236,1237,1239,1240,1242,1243,1250],{},[332,1223,1224],{},"RocksDB reservation is automatic."," Because ",[336,1227,1228],{},"stoatflow-runtime","\u002F",[336,1231,1232],{},"stoatflow-core"," bring RocksDB onto the classpath, the image sets ",[336,1235,896],{}," for you. In-memory-only apps omit it. Override with the ",[336,1238,905],{}," knob, or ",[336,1241,1170],{}," to omit. The native ",[324,1244,1246,1249],{"href":1245},"\u002Fdocs\u002Fruntime\u002Fnative-image#runtime-target",[336,1247,1248],{},"runtime"," image"," uses the same entrypoint and the same reservation.",[441,1252,1254],{"id":1253},"run-the-container","Run the container",[320,1256,1257,1258,1260,1261,1203,1264,1266,1267,1270,1271,1274,1275,1278],{},"The image exposes port ",[336,1259,420],{}," — the runtime's HTTP admin + Prometheus metrics server (see ",[324,1262,1263],{"href":186},"REST API",[324,1265,195],{"href":196},") — and reads its own configuration (bootstrap servers, license key, topics) from ",[336,1268,1269],{},"application.yaml"," and environment overrides (see ",[324,1272,1273],{"href":163},"Runtime config","). Mount a volume at ",[336,1276,1277],{},"STOATFLOW_STATE_DIR"," to persist RocksDB state across restarts:",[464,1280,1282],{"className":606,"code":1281,"language":608,"meta":470,"style":470},"docker run --rm \\\n  -e KAFKA_BOOTSTRAP_SERVERS=host.docker.internal:9092 \\\n  -e STOATFLOW_LICENSE_KEY=\"key\u002F...your key...\" \\\n  -v stoatflow-state:\u002Fdata\u002Fstate \\\n  -p 8080:8080 \\\n  acme\u002Fword-count:latest\n",[336,1283,1284,1297,1307,1325,1335,1345],{"__ignoreMap":470},[474,1285,1286,1288,1291,1294],{"class":476,"line":477},[474,1287,457],{"class":487},[474,1289,1290],{"class":507}," run",[474,1292,1293],{"class":631}," --rm",[474,1295,1296],{"class":631}," \\\n",[474,1298,1299,1302,1305],{"class":476,"line":484},[474,1300,1301],{"class":631},"  -e",[474,1303,1304],{"class":507}," KAFKA_BOOTSTRAP_SERVERS=host.docker.internal:9092",[474,1306,1296],{"class":631},[474,1308,1309,1311,1314,1318,1321,1323],{"class":476,"line":495},[474,1310,1301],{"class":631},[474,1312,1313],{"class":507}," STOATFLOW_LICENSE_KEY=",[474,1315,1317],{"class":1316},"sSP4y","\"",[474,1319,1320],{"class":507},"key\u002F...your key...",[474,1322,1317],{"class":1316},[474,1324,1296],{"class":631},[474,1326,1327,1330,1333],{"class":476,"line":514},[474,1328,1329],{"class":631},"  -v",[474,1331,1332],{"class":507}," stoatflow-state:\u002Fdata\u002Fstate",[474,1334,1296],{"class":631},[474,1336,1337,1340,1343],{"class":476,"line":522},[474,1338,1339],{"class":631},"  -p",[474,1341,1342],{"class":507}," 8080:8080",[474,1344,1296],{"class":631},[474,1346,1347],{"class":476,"line":538},[474,1348,1349],{"class":507},"  acme\u002Fword-count:latest\n",[320,1351,1352,1353,1356],{},"A Kubernetes deployment then needs only a memory limit (the entrypoint sizes the heap from it) and a volume for the state dir — see ",[324,1354,1355],{"href":229},"Kubernetes",":",[464,1358,1362],{"className":1359,"code":1360,"language":1361,"meta":470,"style":470},"language-yaml shiki shiki-themes vitesse-light","resources:\n  limits:\n    memory: 2Gi          # the entrypoint sizes the heap from this\nvolumeMounts:\n  - { name: state, mountPath: \u002Fdata\u002Fstate }   # matches stateDir\n","yaml",[336,1363,1364,1374,1381,1394,1401],{"__ignoreMap":470},[474,1365,1366,1370],{"class":476,"line":477},[474,1367,1369],{"class":1368},"su6XF","resources",[474,1371,1373],{"class":1372},"sYZai",":\n",[474,1375,1376,1379],{"class":476,"line":484},[474,1377,1378],{"class":1368},"  limits",[474,1380,1373],{"class":1372},[474,1382,1383,1386,1388,1391],{"class":476,"line":495},[474,1384,1385],{"class":1368},"    memory",[474,1387,1356],{"class":1372},[474,1389,1390],{"class":507}," 2Gi",[474,1392,1393],{"class":480},"          # the entrypoint sizes the heap from this\n",[474,1395,1396,1399],{"class":476,"line":514},[474,1397,1398],{"class":1368},"volumeMounts",[474,1400,1373],{"class":1372},[474,1402,1403,1406,1409,1412,1414,1417,1420,1423,1425,1428,1431],{"class":476,"line":522},[474,1404,1405],{"class":1372},"  -",[474,1407,1408],{"class":1372}," {",[474,1410,1411],{"class":1368}," name",[474,1413,1356],{"class":1372},[474,1415,1416],{"class":507}," state",[474,1418,1419],{"class":1372},",",[474,1421,1422],{"class":1368}," mountPath",[474,1424,1356],{"class":1372},[474,1426,1427],{"class":507}," \u002Fdata\u002Fstate",[474,1429,1430],{"class":1372}," }",[474,1432,1433],{"class":480},"   # matches stateDir\n",[441,1435,1437],{"id":1436},"rolling-your-own-dockerfile","Rolling your own Dockerfile",[320,1439,1440,1441,1443,1444,410,1447,1450,1451,1453,1454,406,1457,1460,1461,1464,1465,1467,1468,1471],{},"The Jib path is the supported, simplest route and right-sizes the heap automatically — prefer it. If your pipeline standardizes on ",[336,1442,672],{},", build the fat JAR (",[336,1445,1446],{},".\u002Fgradlew shadowJar",[336,1448,1449],{},"mvn package",") and write a thin image over it: a ",[336,1452,391],{}," base, ",[336,1455,1456],{},"COPY",[336,1458,1459],{},"-all.jar",", and launch ",[336,1462,1463],{},"java --enable-preview --enable-native-access=ALL-UNNAMED -XX:+UseG1GC -jar app.jar",". You lose the automatic container-aware heap sizing — pin ",[336,1466,1088],{}," or use ",[336,1469,1470],{},"-XX:MaxRAMPercentage"," yourself.",[320,1473,1474,1475,1478,1479,1483,1484,1486,1487,1490,1491,369],{},"For a ",[332,1476,1477],{},"native"," image (GraalVM), the containerized build ",[1480,1481,1482],"em",{},"is"," a ",[336,1485,672],{}," against a bundled, repo-independent ",[336,1488,1489],{},"Dockerfile.native"," — see ",[324,1492,1493],{"href":216},"Native image",[441,1495,1497],{"id":1496},"next-steps","Next steps",[353,1499,1500,1513,1524,1531],{},[356,1501,1502,1506,1507,1203,1509,1512],{},[332,1503,1504],{},[324,1505,33],{"href":34}," — the full build-conventions surface, including the ",[336,1508,457],{},[336,1510,1511],{},"nativeImage"," blocks.",[356,1514,1515,436,1519,1523],{},[332,1516,1517],{},[324,1518,291],{"href":292},[332,1520,1521],{},[324,1522,296],{"href":297}," — every property, task, and default.",[356,1525,1526,1530],{},[332,1527,1528],{},[324,1529,1493],{"href":216}," — the GraalVM native-image build for fast startup and low memory.",[356,1532,1533,1537],{},[332,1534,1535],{},[324,1536,190],{"href":191}," — the liveness\u002Freadiness endpoints the container probes hit.",[1539,1540,1541],"style",{},"html pre.shiki code .s8zF2, html code.shiki .s8zF2{--shiki-default:#A0ADA0}html pre.shiki code .sySUi, html code.shiki .sySUi{--shiki-default:#59873A}html pre.shiki code .suHK_, html code.shiki .suHK_{--shiki-default:#393A34}html pre.shiki code .spphp, html code.shiki .spphp{--shiki-default:#B56959}html pre.shiki code .sbBg2, html code.shiki .sbBg2{--shiki-default:#1E754F}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sEi1f, html code.shiki .sEi1f{--shiki-default:#A65E2B}html pre.shiki code .sSP4y, html code.shiki .sSP4y{--shiki-default:#B5695977}html pre.shiki code .su6XF, html code.shiki .su6XF{--shiki-default:#998418}html pre.shiki code .sYZai, html code.shiki .sYZai{--shiki-default:#999999}",{"title":470,"searchDepth":484,"depth":484,"links":1543},[1544,1545,1549,1550,1551,1552],{"id":443,"depth":484,"text":444},{"id":691,"depth":484,"text":692,"children":1546},[1547,1548],{"id":924,"depth":495,"text":864},{"id":1049,"depth":495,"text":1050},{"id":1078,"depth":484,"text":1079},{"id":1253,"depth":484,"text":1254},{"id":1436,"depth":484,"text":1437},{"id":1496,"depth":484,"text":1497},"Build a container image for your StoatFlow app with the StoatFlow build conventions (Jib) — base image, JVM flags, reproducible builds, container-aware heap sizing, ports and env — on Gradle or Maven.","md",{},{"icon":213},"how-to",{"title":210,"description":1553},"OVc_wxkPTEdLRB0g-wDr88pJrbFZJiTSJ5SdHqWWN-A",[1561,1563],{"title":205,"path":206,"stem":207,"description":1562,"icon":208,"children":-1},"Extend the StoatFlow runtime with custom metrics, HTTP endpoints, and health indicators via the RuntimePlugin interface and lifecycle listeners.",{"title":215,"path":216,"stem":217,"description":1564,"icon":218,"children":-1},"Compile your StoatFlow app to a GraalVM native binary — a one-line opt-in. The framework ships its own reflection\u002FJNI\u002FFFM metadata; you choose the GC, optionally PGO, and add metadata only for your own serde value classes.",1783713362120]