<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sanskriti Harmukh</title>
    <description>The latest articles on DEV Community by Sanskriti Harmukh (@sharmukh).</description>
    <link>https://dev.to/sharmukh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3900728%2F38e545fb-274b-483d-a868-8780a6503800.png</url>
      <title>DEV Community: Sanskriti Harmukh</title>
      <link>https://dev.to/sharmukh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sharmukh"/>
    <language>en</language>
    <item>
      <title>Building Serverless APIs with Kotlin Ktor and Coroutines</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:24:17 +0000</pubDate>
      <link>https://dev.to/vultr/building-serverless-apis-with-kotlin-ktor-and-coroutines-3h3f</link>
      <guid>https://dev.to/vultr/building-serverless-apis-with-kotlin-ktor-and-coroutines-3h3f</guid>
      <description>&lt;p&gt;Modern serverless platforms demand fast startup times, low resource overhead, and efficient concurrency. Kotlin, with its modern syntax and coroutine-based concurrency, and Ktor, its lightweight asynchronous web framework, provide an ideal combination for building high-performance serverless APIs. This guide demonstrates how to build a &lt;strong&gt;Product Catalog CRUD API&lt;/strong&gt; using Kotlin and Ktor, structure a coroutine-driven application, handle concurrent requests efficiently, and package it for deployment across cloud providers in fully serverless or containerized environments. By the end, you'll have a working CRUD API, hardened for external calls, packaged as a portable JAR ready for serverless or container deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have access to an Ubuntu 24.04 based server as a non-root user with sudo privileges.&lt;/li&gt;
&lt;li&gt;Install &lt;strong&gt;JDK 17+&lt;/strong&gt; (OpenJDK recommended).&lt;/li&gt;
&lt;li&gt;Use the project's &lt;strong&gt;Gradle wrapper&lt;/strong&gt; (&lt;code&gt;./gradlew&lt;/code&gt;). Installing Gradle system-wide is optional.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up Your Development Environment
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Verify Java and Gradle:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;java &lt;span class="nt"&gt;-version&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./gradlew &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Gradle wrapper will be generated by Ktor project"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Generate a new project at &lt;a href="https://start.ktor.io/" rel="noopener noreferrer"&gt;start.ktor.io&lt;/a&gt; with:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group: &lt;code&gt;io.demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Artifact: &lt;code&gt;product-api&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Package: &lt;code&gt;io.demo.productcatalog&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dependencies: &lt;strong&gt;Server Core&lt;/strong&gt;, &lt;strong&gt;Netty&lt;/strong&gt;, &lt;strong&gt;Routing&lt;/strong&gt;, &lt;strong&gt;Content Negotiation (kotlinx-serialization)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Unpack and open the project:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;unzip product-api.zip &lt;span class="nt"&gt;-d&lt;/span&gt; product-api
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;product-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Run the template to confirm the toolchain:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./gradlew run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8080/ &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Ensure JSON serialization is enabled (add if missing):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-core-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-netty-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-content-negotiation-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-serialization-kotlinx-json-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-config-yaml-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-call-logging-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-compression-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-caching-headers-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-request-timeout-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-server-metrics-micrometer-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ch.qos.logback:logback-classic:1.5.6"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Wire the plugin in the application:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/main/kotlin/io/demo/productcatalog/Application.kt&lt;/span&gt;
&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding Ktor and Coroutines for Serverless
&lt;/h2&gt;

&lt;p&gt;Ktor uses &lt;strong&gt;non-blocking I/O&lt;/strong&gt; and Kotlin &lt;strong&gt;coroutines&lt;/strong&gt; to handle high concurrency with low memory overhead, ideal for short-lived, autoscaled serverless workloads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event loop + suspending handlers:&lt;/strong&gt; Handlers suspend on I/O instead of blocking threads, so a few threads serve many requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coroutines vs. threads:&lt;/strong&gt; Coroutines are lightweight; you can run thousands without the context-switch cost of OS threads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured concurrency:&lt;/strong&gt; Scope your launches (e.g., &lt;code&gt;call&lt;/code&gt; or &lt;code&gt;application&lt;/code&gt; scope) to avoid leaks across requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backpressure:&lt;/strong&gt; Keep endpoints non-blocking; offload CPU-heavy or blocking work to &lt;code&gt;Dispatchers.Default&lt;/code&gt;/custom pools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold starts:&lt;/strong&gt; Trim dependencies and init work; defer heavy setup until first use.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building Your First Serverless API with Ktor
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Product Catalog CRUD API&lt;/strong&gt; to demonstrate routing, JSON serialization, and coroutine-friendly data access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create the project structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
└── main/
    ├── kotlin/io/demo/productcatalog/
    │   ├── Application.kt
    │   ├── Routing.kt
    │   ├── model/Product.kt
    │   └── repository/ProductRepository.kt
    └── resources/
        ├── application.yaml
        └── logback.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create the model file at &lt;code&gt;src/main/kotlin/io/demo/productcatalog/model/Product.kt&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;ProductCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Create the in-memory repository file (simulated async) at &lt;code&gt;src/main/kotlin/io/demo/productcatalog/repository/ProductRepository.kt&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1200.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Smartphone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;800.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Headphones"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;150.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOfFirst&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;also&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeIf&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Create the routing configuration file at &lt;code&gt;src/main/kotlin/io/demo/productcatalog/Routing.kt&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureRouting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"Product Catalog API"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/ping"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"pong"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProductRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products/{id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nc"&gt;ProductRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dto&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductCreate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isBlank&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="nd"&gt;@post&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"Invalid product: name must be non-blank, price and stock ≥ 0"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;saved&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products/{id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="nd"&gt;@put&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dto&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductCreate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isBlank&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="nd"&gt;@put&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"Invalid product: name must be non-blank, price and stock ≥ 0"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;updated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/products/{id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="nd"&gt;@delete&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProductRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Edit the application file with the following module configuration in &lt;code&gt;src/main/kotlin/io/demo/productcatalog/Application.kt&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;configureRouting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This enables JSON serialization and registers the routing configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Run the application using Gradle:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./gradlew run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Task :run
2025-10-13 15:19:55.869 [main] INFO  Application - Autoreload is disabled because the development mode is off.
2025-10-13 15:19:56.298 [main] INFO  Application - Application started in 0.774 seconds.
2025-10-13 15:19:56.629 [DefaultDispatcher-worker-1] INFO  Application - Responding at http://0.0.0.0:8080
&amp;lt;==========---&amp;gt; 83% EXECUTING [19m 10s]
&amp;gt; :run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Verify the base routes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl http://localhost:8080/
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl http://localhost:8080/ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Product Catalog API"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"pong"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling Concurrency and Performance
&lt;/h2&gt;

&lt;p&gt;Ktor serves each request on a lightweight coroutine, so a handful of threads can handle heavy concurrency. Tune the app to stay non-blocking, predictable, and fast.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Keep handlers non-blocking. Use &lt;code&gt;withContext(Dispatchers.IO)&lt;/code&gt; for blocking work (file I/O, JDBC) so you don't stall the event loop.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use structured concurrency in routes. Launch child coroutines in a scope and &lt;code&gt;await()&lt;/code&gt; them together. If one fails, Ktor cancels siblings and the request.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/report"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;coroutineScope&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;a&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchA&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;// suspend fun&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right-size dispatchers (only when needed). Let Ktor manage event loops. If you must isolate heavy I/O/CPU work, bound the pool and close it on shutdown.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;ioPool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;asCoroutineDispatcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApplicationStopped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ioPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remove artificial delays in production. Replace &lt;code&gt;delay()&lt;/code&gt; in repositories with real async I/O (database, HTTP client).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add low-cost server features. Compression, caching headers, and request timeouts improve latency and protect the app.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Compression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;gzip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CachingHeaders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;options&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;CachingOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MaxAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxAgeSeconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RequestTimeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;requestTimeoutMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;configureRouting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Integrating External Services
&lt;/h2&gt;

&lt;p&gt;Connect to databases and APIs with suspendable, time-bounded calls. Add retries sparingly, log context, and protect upstreams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ktor HTTP Client (timeouts, retries, JSON)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Configure a resilient Ktor HTTP client with JSON, timeouts, and limited retries.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;http&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CIO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;expectSuccess&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;

    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpTimeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;requestTimeoutMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2_000&lt;/span&gt;
        &lt;span class="n"&gt;connectTimeoutMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000&lt;/span&gt;
        &lt;span class="n"&gt;socketTimeoutMillis&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2_000&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Ktor's retry feature (Ktor 2.x)&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpRequestRetry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;retryOnServerErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxRetries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;exponentialDelay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxDelayMs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// Optionally retry on specific exceptions:&lt;/span&gt;
        &lt;span class="nf"&gt;retryIf&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cause&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cause&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;HttpRequestTimeoutException&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;retryOnExceptionIf&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cause&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cause&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;defaultRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add client dependencies to your build file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-client-core-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-client-cio-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-client-content-negotiation-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-serialization-kotlinx-json-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-client-http-timeout-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-client-retry-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;testImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.ktor:ktor-client-mock-jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Close the shared client on shutdown to prevent resource leaks.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApplicationStopped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inject the client and base URL for testability. Example with a repository and a MockEngine-based test setup:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PriceRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fetchPrices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$baseUrl/prices"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// wiring (e.g., in Application.module)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pricesBaseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;propertyOrNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"app.services.prices.baseUrl"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PRICES_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s"&gt;"https://api.example.com"&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;priceRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PriceRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pricesBaseUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// test setup&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mockClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MockEngine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;addHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encodedPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/prices"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[{\"sku\":\"A\",\"value\":10.0}]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;headersOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unhandled ${'$'}{request.url}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mockRepo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PriceRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"https://mock.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Call the client from a repository; keep functions &lt;code&gt;suspend&lt;/code&gt;, add per-call timeouts if a dependency is flaky.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Serializable&lt;/span&gt; &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;PriceService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fetchPrices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/prices"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Per-call timeout override:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fetchPricesFast&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/prices"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;requestTimeoutMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Minimal "circuit breaker" guard (lightweight)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If an upstream often times out, short-circuit for a brief window. Keep it simple without extra libs.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleBreaker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;openMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;failures&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;openedAt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0L&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;openedAt&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;openMillis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;recordSuccess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;openedAt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;recordFailure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;++&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;openedAt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;breaker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleBreaker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;guardedPrices&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;breaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// fallback&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PriceService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchPrices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;breaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recordSuccess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;breaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recordFailure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// degrade gracefully&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database integration tips (JDBC/JPA/R2DBC)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JDBC/JPA:&lt;/strong&gt; treat calls as blocking; wrap in &lt;code&gt;withContext(Dispatchers.IO)&lt;/code&gt;. Keep pools small but sufficient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;R2DBC / async drivers:&lt;/strong&gt; prefer truly non-blocking drivers when available.&lt;/li&gt;
&lt;li&gt;Set short query timeouts and fail fast on connection errors.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findProductById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepareStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT ... WHERE id = ?"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queryTimeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;// seconds&lt;/span&gt;
                &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeQuery&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* map to Product */&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Observability (logs, metrics, traces)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Log request IDs: propagate a &lt;code&gt;X-Request-ID&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;Emit timing metrics (Micrometer) around integrations.&lt;/li&gt;
&lt;li&gt;Sample traces on external calls (OpenTelemetry) for latency hotspots.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CallLogging&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;mdc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"requestId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"X-Request-ID"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hardening checklist
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Time-box every external call (client + per-call timeouts).&lt;/li&gt;
&lt;li&gt;Add bounded retries with exponential backoff (no infinite retries).&lt;/li&gt;
&lt;li&gt;Use fallback/cached responses for non-critical paths.&lt;/li&gt;
&lt;li&gt;Validate and sanitize all inbound/outbound payloads.&lt;/li&gt;
&lt;li&gt;Keep secrets in environment variables or a secret store; never hard-code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploying and Monitoring Across Cloud Providers
&lt;/h2&gt;

&lt;p&gt;Ship a portable artifact, run it the same way everywhere, and expose health/metrics for hands-off ops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Package a single, portable JAR
&lt;/h3&gt;

&lt;p&gt;Serverless/container platforms don't resolve Gradle deps, so build a fat JAR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add the following content in &lt;code&gt;libs.versions.toml&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[plugins]&lt;/span&gt;
&lt;span class="py"&gt;shadow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.github.johnrengelman.shadow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"8.1.1"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Add the following content in &lt;code&gt;build.gradle.kts&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jvm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serialization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ktor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mainClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.demo.productcatalog.ApplicationKt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;shadowJar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;archiveBaseName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"product-api"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;archiveClassifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;archiveVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Build and smoke-test locally:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./gradlew shadowJar
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;java &lt;span class="nt"&gt;-jar&lt;/span&gt; build/libs/product-api-all.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Make the app cloud-friendly (port, health, graceful shutdown)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Configure the application port and external services in &lt;code&gt;resources/application.yaml&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ktor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PORT:8080}&lt;/span&gt;   &lt;span class="c1"&gt;# use env var PORT if provided&lt;/span&gt;
  &lt;span class="na"&gt;application&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;io.demo.productcatalog.ApplicationKt.module&lt;/span&gt;
&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prices&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PRICES_BASE_URL:https://api.example.com}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration allows the application to bind to a dynamic port provided by the cloud platform and defines an external service base URL through environment variables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Add lightweight ops features:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CallLogging&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Compression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;gzip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// optional timeouts to avoid hung requests&lt;/span&gt;
    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RequestTimeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;requestTimeoutMillis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/ready"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;// readiness&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/live"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;    &lt;span class="c1"&gt;// liveness&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;configureRouting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These changes make the application suitable for cloud platforms by supporting dynamic port binding, health probes for orchestration systems, request logging, response compression, and graceful request timeouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Observability: logs, metrics, traces (portable setup)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Structured logs:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CallLogging&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// keep message rates low, include request ids if available&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prometheus metrics:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.gradle.kts: implementation("io.micrometer:micrometer-registry-prometheus:&amp;lt;version&amp;gt;")&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;registry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;micrometer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PrometheusMeterRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;micrometer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prometheus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PrometheusConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ktor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;micrometer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MicrometerMetrics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/metrics"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scrape&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advanced Tips for Optimizing Ktor in Serverless Environments
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep dependencies lean.&lt;/strong&gt; Every jar adds cold-start time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid blocking the event loop.&lt;/strong&gt; Wrap unavoidable blocking in a bounded IO dispatcher.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warm paths on deploy.&lt;/strong&gt; Optionally pre-load serializers/config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache smartly.&lt;/strong&gt; Memoize hot, static reads with short TTLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark routinely.&lt;/strong&gt; Use &lt;code&gt;wrk&lt;/code&gt;/&lt;code&gt;k6&lt;/code&gt; to tune timeouts, thread pools, and memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add authentication/authorization (e.g., JWT) in front of the &lt;code&gt;/products&lt;/code&gt; routes.&lt;/li&gt;
&lt;li&gt;Swap the in-memory &lt;code&gt;ProductRepository&lt;/code&gt; for an async-friendly database driver.&lt;/li&gt;
&lt;li&gt;Wire up the Prometheus metrics endpoint and an OpenTelemetry exporter for production tracing.&lt;/li&gt;
&lt;li&gt;Load-test the packaged JAR with &lt;code&gt;wrk&lt;/code&gt; or &lt;code&gt;k6&lt;/code&gt; to validate timeout and retry settings under concurrency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-build-serverless-apis-with-kotlin-ktor-and-coroutines" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>ktor</category>
      <category>api</category>
      <category>coroutines</category>
    </item>
    <item>
      <title>Using Rsync to Transfer Files in Linux</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:23:25 +0000</pubDate>
      <link>https://dev.to/vultr/using-rsync-to-transfer-files-in-linux-1c6f</link>
      <guid>https://dev.to/vultr/using-rsync-to-transfer-files-in-linux-1c6f</guid>
      <description>&lt;p&gt;Rsync is a file transfer tool for transferring and synchronizing files across systems. It's commonly used for backups, file mirroring, and updating files between local directories or remote servers. Rsync reduces data transfer by copying only the differences between the source and destination files, and its speed, flexibility, and extensive options make it a popular choice for system administrators and developers. This guide installs Rsync and walks through using it to perform local and remote file transfers from your workstation. By the end, you'll have transferred and excluded files locally, and synchronized a directory to a remote machine over SSH.&lt;/p&gt;




&lt;h2&gt;
  
  
  Install Rsync
&lt;/h2&gt;

&lt;p&gt;Rsync is available in the official repositories on most Linux distributions and is often pre-installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install Rsync on Ubuntu and Debian using the APT package manager:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;rsync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Install Rsync on FreeBSD using the PKG package manager:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;rsync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Install Rsync on Arch Linux using the Pacman package manager:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; rsync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Install Rsync on Fedora, AlmaLinux, Rocky Linux, and VzLinux using the DNF package manager:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;rsync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Install Rsync on CentOS using the YUM package manager:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;rsync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rsync Command Syntax
&lt;/h2&gt;

&lt;p&gt;Rsync uses the following command syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="o"&gt;[&lt;/span&gt;OPTION] SRC DEST
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[OPTION]&lt;/code&gt;: Represents the options or flags passed to the Rsync command.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SRC&lt;/code&gt;: The source files and directories you want to synchronize.

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;USER@HOST:SRC&lt;/code&gt; when transferring files remotely over SSH.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEST&lt;/code&gt;: The destination where the files or directories will be synchronized.

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;USER@HOST:DEST&lt;/code&gt; when transferring files remotely over SSH.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rsync Command Options
&lt;/h3&gt;

&lt;p&gt;The following are the most common Rsync command options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Long Form&lt;/th&gt;
&lt;th&gt;Short Form&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Help&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show Rsync command help&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verbosity&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--verbose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Increase verbosity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--quiet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Suppress non-error messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File Selection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--archive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Archive mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--recursive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recurse into directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--relative&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-R&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use relative path names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Updates&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip files that are newer on the receiver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--inplace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Update destination files in-place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backups&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make backups (see &lt;code&gt;--suffix&lt;/code&gt; &amp;amp; &lt;code&gt;--backup-dir&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--backup-dir=DIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Make backups into hierarchy based in DIR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Symlinks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--links&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy symlinks as symlinks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--copy-links&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transform symlink into referent file/dir&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--perms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preserve permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timestamps&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--times&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preserve modification times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deletion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Delete extraneous files from dest dirs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--delete-excluded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Also delete excluded files from dest dirs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simulation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--dry-run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Perform a trial run with no changes made&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote Shell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--rsh=COMMAND&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Specify the remote shell to use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size Control&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--max-size=SIZE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Don't transfer any file larger than &lt;code&gt;SIZE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--min-size=SIZE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Don't transfer any file smaller than &lt;code&gt;SIZE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial Transfer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--partial&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Keep incomplete transferred files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File Filtering&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--exclude=PATTERN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Exclude files matching PATTERN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--include=PATTERN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Don't exclude files matching PATTERN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output Format&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--progress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Show progress during transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-P&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Same as &lt;code&gt;--partial --progress&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--human-readable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output numbers in a human-readable format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compression&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--compress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-z&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compress file data during the transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP Version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--ipv4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prefer IPv4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--ipv6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prefer IPv6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Transfer Files and Directories Using Rsync
&lt;/h2&gt;

&lt;p&gt;Set up a test environment before running the Rsync commands below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a directory named &lt;code&gt;rsync-test&lt;/code&gt; to work with:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;rsync-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Navigate into the newly created directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;rsync-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Create three sample files:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;file1 file2 file3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Transfer Local Files
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Copy &lt;code&gt;file1&lt;/code&gt;, &lt;code&gt;file2&lt;/code&gt;, and &lt;code&gt;file3&lt;/code&gt; to a new directory named &lt;code&gt;dest&lt;/code&gt;. In Rsync, the last argument always represents the destination. Ensure the destination path is correctly specified to prevent accidental overwrites:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rsync file1 file2 file3 dest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;file1 file2 file3&lt;/code&gt;: Source files to copy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dest&lt;/code&gt;: Destination directory where the files will be copied.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Verify that the files were copied successfully:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;dest/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output should be similar to the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./dest/file1
./dest/file2
./dest/file3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Transfer Local Directories
&lt;/h3&gt;

&lt;p&gt;Rsync does not copy files inside a directory by default. Use the &lt;code&gt;-a&lt;/code&gt; flag to enable recursive copying, which enables archive mode, preserving timestamps, permissions, group, owner, and symbolic links.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Copy the &lt;code&gt;dest&lt;/code&gt; directory you created earlier:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rsync &lt;span class="nt"&gt;-a&lt;/span&gt; dest/ dest-copy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-a&lt;/code&gt;: Enables archive mode to preserve file attributes while copying recursively.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dest/&lt;/code&gt;: The source directory to copy (trailing &lt;code&gt;/&lt;/code&gt; means copy contents, not the directory itself. If you omit the trailing &lt;code&gt;/&lt;/code&gt;, Rsync copies the entire directory instead of just its contents).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dest-copy&lt;/code&gt;: The destination directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Verify the copy operation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;dest-copy/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output should be similar to the one below. A &lt;code&gt;dest-copy&lt;/code&gt; directory is created with the same content as the &lt;code&gt;dest&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./dest-copy/file1
./dest-copy/file2
./dest-copy/file3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Exclude Files or Directories Using Rsync
&lt;/h3&gt;

&lt;p&gt;To exclude specific files or directories from being copied, use the Rsync &lt;code&gt;--exclude&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Copy the contents of the current directory, excluding &lt;code&gt;file2&lt;/code&gt; and all directories that start with &lt;code&gt;dest&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rsync &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'file2'&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'dest*'&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-a&lt;/code&gt;: Enable archive mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--exclude 'file2'&lt;/code&gt;: Exclude files and directories named &lt;code&gt;file2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--exclude 'dest*'&lt;/code&gt;: Exclude files and directories that start with &lt;code&gt;dest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt;: Specify the current directory as the source.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt;: The destination directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Verify the copy operation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;output/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output should be similar to the one below. Notice that the &lt;code&gt;output&lt;/code&gt; directory contains only &lt;code&gt;file1&lt;/code&gt; and &lt;code&gt;file3&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./output/file1
./output/file3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Synchronize Data to a Remote Machine Using Rsync
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Synchronize files and directories to a remote machine:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rsync &lt;span class="nt"&gt;-hazvP&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="nt"&gt;--stats&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; user@192.0.2.1:synced_from_local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-h&lt;/code&gt;: Output numbers in human-readable format.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-a&lt;/code&gt;: Enables archive mode for preserving metadata.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-z&lt;/code&gt;: Compresses data during transfer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt;: Enables verbose output.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-P&lt;/code&gt;: Displays progress and resumes partial transfers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--delete&lt;/code&gt;: Removes files from the destination that no longer exist in the source.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--stats&lt;/code&gt;: Provides a detailed summary of the transfer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt;: Specify the current directory as the source.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user@192.0.2.1:synced_from_local&lt;/code&gt;: Specify the remote machine as the destination. This consists of three parts:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;user&lt;/code&gt;: The username to access the remote machine.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;192.0.2.1&lt;/code&gt;: The IP address of the remote machine.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;synced_from_local&lt;/code&gt;: The destination path.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Log in to the remote machine using SSH:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ssh user@192.0.2.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Verify that the files have been synced:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;synced_from_local/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;synced_from_local/dest-copy/file1
synced_from_local/dest-copy/file2
synced_from_local/dest-copy/file3
synced_from_local/dest/file1
synced_from_local/dest/file2
synced_from_local/dest/file3
synced_from_local/file1
synced_from_local/file2
synced_from_local/file3
synced_from_local/output/file1
synced_from_local/output/file3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Warning on Using the &lt;code&gt;--delete&lt;/code&gt; Rsync Option
&lt;/h3&gt;

&lt;p&gt;Be cautious when using &lt;code&gt;--delete&lt;/code&gt;, as it permanently removes files from the destination if they don't exist in the source. Always perform a dry run first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rsync &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt; SRC DEST
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Schedule recurring Rsync jobs with cron to keep a backup directory continuously in sync.&lt;/li&gt;
&lt;li&gt;Combine &lt;code&gt;--exclude&lt;/code&gt; with a &lt;code&gt;.rsync-filter&lt;/code&gt; file to manage more complex exclusion rules.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;--dry-run&lt;/code&gt; as a standard habit before any sync that includes &lt;code&gt;--delete&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;man rsync&lt;/code&gt; for the full list of options and remote-shell configurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-use-rsync-to-transfer-files-in-linux" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>rsync</category>
      <category>linux</category>
      <category>cli</category>
      <category>backup</category>
    </item>
    <item>
      <title>Using Curl</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:22:54 +0000</pubDate>
      <link>https://dev.to/vultr/using-curl-o4l</link>
      <guid>https://dev.to/vultr/using-curl-o4l</guid>
      <description>&lt;p&gt;&lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;Curl&lt;/a&gt; is a command-line utility and underlying library (libcurl) used to transfer data across a wide range of network protocols, including HTTP, HTTPS, FTP, FTPS, SCP, SFTP, LDAP, MQTT, and more. It supports features like proxy support, user authentication, cookies, file uploads, and SSL connections, making it a go-to tool for developers, system administrators, and anyone working with web APIs or networked services. This guide walks through using curl to perform HTTP requests from your terminal, including downloading files, setting custom headers, working with different HTTP methods, handling redirects, sending JSON payloads, authenticating with servers, and troubleshooting with verbose mode. By the end, you'll be comfortable using curl to test APIs and automate network operations from the command line.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Short Answer Version
&lt;/h2&gt;

&lt;p&gt;If you just want a quick reference for common &lt;code&gt;curl&lt;/code&gt; commands, here's a compact list of essentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Basic GET request
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://example.com
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Save output to file
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; page.html https://example.com
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Follow redirects
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; http://example.com
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Send POST form data
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"name=John&amp;amp;age=30"&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://example.com
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Send JSON payload
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"John"}'&lt;/span&gt; https://example.com
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Add custom header
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;token&amp;gt;"&lt;/span&gt; https://api.example.com
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Basic auth
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-u&lt;/span&gt; user:pass https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Curl
&lt;/h2&gt;

&lt;p&gt;This section expands on the quick reference above and explains basic &lt;code&gt;curl&lt;/code&gt; operations in more detail, including file downloads, protocol handling, and request formatting.&lt;/p&gt;

&lt;p&gt;The basic syntax of the &lt;code&gt;curl&lt;/code&gt; command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="o"&gt;[&lt;/span&gt;options] &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;curl&lt;/code&gt; Command Options
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-o &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save output to the specified file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save with the same name as the remote file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-C -&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resume an interrupted download&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable verbose mode (request/response debugging)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Silent mode, hides progress meter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-L&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow redirects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-I&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetch only response headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-X &amp;lt;METHOD&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set HTTP method (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d &amp;lt;data&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send POST form data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-H "&amp;lt;header&amp;gt;"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add custom headers (e.g., &lt;code&gt;Content-Type&lt;/code&gt;, &lt;code&gt;Authorization&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--user-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override the default User-Agent string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-u user:pass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send basic authentication credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-F&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload file using multipart form&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--fail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit with error on 4xx/5xx responses, suppresses body output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output custom variables like time taken (&lt;code&gt;%{time_total}&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Detailed Usage
&lt;/h2&gt;

&lt;p&gt;The following sections explain how each curl command works in more detail, with examples and syntax breakdowns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Download a Webpage
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Download a webpage and print its source code to the terminal:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl https://www.example.com/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use FTP Protocol
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Retrieve a file using FTP:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl ftp://&amp;lt;host&amp;gt;/&amp;lt;path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl ftp://ftp.example.com/public/readme.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Save Output to a File
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Save the downloaded output to a local file instead of printing it to the terminal:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; &amp;lt;filename&amp;gt; &amp;lt;URL&amp;gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-o&lt;/code&gt;: Save output with a custom filename.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-O&lt;/code&gt;: Save output using the remote filename.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; localcopy.html https://example.com/index.html
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://example.com/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resume an Interrupted Download
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Resume an interrupted download from where it left off instead of restarting from scratch:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-C -&lt;/code&gt;: Resume download.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-O&lt;/code&gt;: Save using remote file name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; https://example.com/bigfile.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Verbose Output
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Enable verbose output to debug request/response communication:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making HTTP Requests
&lt;/h2&gt;

&lt;p&gt;Curl provides a wide range of options to interact with web services, including inspecting headers, following redirects, changing HTTP methods, and customizing request headers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Only Response Headers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Fetch only the response headers of a URL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Follow Redirects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Enable following of HTTP 3xx redirects:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; http://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Change the HTTP Method
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Set a custom HTTP method using &lt;code&gt;-X&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;METHOD] &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE https://example.com/resource/123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Custom Headers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Use the &lt;code&gt;-H&lt;/code&gt; flag to add one or more HTTP headers:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Header-Name: value"&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"User-Agent: Mozilla/5.0"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cache-Control: no-cache"&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set a Custom User-Agent
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Use &lt;code&gt;--user-agent&lt;/code&gt; to define the request's User-Agent string:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;--user-agent&lt;/span&gt; &lt;span class="s2"&gt;"[Agent String]"&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;--user-agent&lt;/span&gt; &lt;span class="s2"&gt;"Mozilla/5.0 (Macintosh)"&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Send Form or JSON Data (POST)
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;curl&lt;/code&gt; to send form-encoded or JSON data in HTTP POST requests. This is essential for interacting with REST APIs or submitting data to a server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Send form-encoded or JSON data:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;key1=value1&amp;amp;key2=value2&amp;gt;"&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &amp;lt;URL&amp;gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;JSON&amp;gt;'&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"name=John&amp;amp;age=30"&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://example.com
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"John"}'&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Basic Authentication
&lt;/h3&gt;

&lt;p&gt;When accessing resources protected by HTTP Basic Auth, use the &lt;code&gt;-u&lt;/code&gt; option to include your credentials directly in the request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Send basic auth credentials:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-u&lt;/span&gt; &amp;lt;username&amp;gt;:&amp;lt;password&amp;gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-u&lt;/span&gt; admin:secret https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Usage and Tips
&lt;/h2&gt;

&lt;p&gt;This section covers advanced curl patterns commonly used in scripts, automation, and API integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exit on HTTP Errors (&lt;code&gt;--fail&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Make curl exit silently with a non-zero code when HTTP errors like 404 or 500 occur:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;--fail&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--fail&lt;/code&gt;: Exit silently on HTTP errors (4xx, 5xx).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;--fail&lt;/span&gt; https://example.com/missing-file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this in scripts to halt execution when a URL is unreachable or responds with an error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure HTTP Request Time (&lt;code&gt;-w&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Print the total time taken to complete a request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{time_total}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-w&lt;/code&gt;: Custom output after completion.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;%{time_total}&lt;/code&gt;: Total transfer time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-o /dev/null&lt;/code&gt;: Discard body.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-s&lt;/code&gt;: Silent mode to suppress progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{time_total}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps measure API response time or network latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upload Files (&lt;code&gt;-F&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Upload files using a multipart form submission:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"key=@filename.ext"&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-F&lt;/code&gt;: Send form field with file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@filename.ext&lt;/code&gt;: Reads the file from local disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@report.pdf"&lt;/span&gt; https://example.com/upload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;-F&lt;/code&gt; for uploading files as part of a form. Use &lt;code&gt;--data-binary&lt;/code&gt; if you want to send raw files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authenticate with Bearer Token
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Use bearer tokens to access secured APIs (e.g., OAuth2 access tokens):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;your_token&amp;gt;"&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer abc123"&lt;/span&gt; https://api.example.com/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is required when accessing private endpoints on services like GitHub, Stripe, or cloud APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quiet or Debug Output (&lt;code&gt;-s&lt;/code&gt;, &lt;code&gt;-v&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Suppress the progress bar and errors for scripts:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Show full request and response details for debugging:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &amp;lt;URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;-s&lt;/code&gt; for scripts and logging. Use &lt;code&gt;-v&lt;/code&gt; when debugging failures or testing headers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Script a health-check that uses &lt;code&gt;curl --fail&lt;/code&gt; and &lt;code&gt;-w "%{time_total}"&lt;/code&gt; to alert on slow or failing endpoints.&lt;/li&gt;
&lt;li&gt;Combine &lt;code&gt;-H "Authorization: Bearer &amp;lt;token&amp;gt;"&lt;/code&gt; with &lt;code&gt;-X POST&lt;/code&gt; to scaffold quick API client tests.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;-F&lt;/code&gt; to exercise file-upload endpoints before writing a full client library.&lt;/li&gt;
&lt;li&gt;Review the &lt;a href="https://curl.se/docs/" rel="noopener noreferrer"&gt;official curl documentation&lt;/a&gt; for protocol-specific features and scripting patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-use-curl" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>curl</category>
      <category>http</category>
      <category>cli</category>
      <category>networking</category>
    </item>
    <item>
      <title>Using the Linux IP Command to Manage Server Network Interfaces</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:22:30 +0000</pubDate>
      <link>https://dev.to/vultr/using-the-linux-ip-command-to-manage-server-network-interfaces-4ngk</link>
      <guid>https://dev.to/vultr/using-the-linux-ip-command-to-manage-server-network-interfaces-4ngk</guid>
      <description>&lt;p&gt;The &lt;code&gt;ip&lt;/code&gt; command is a built-in Linux network management utility that offers a wide range of features and tools for managing network interfaces. It's an improved utility that combines the functionality of legacy commands such as &lt;code&gt;ifconfig&lt;/code&gt;, &lt;code&gt;arp&lt;/code&gt;, and &lt;code&gt;route&lt;/code&gt; into a single tool for managing, monitoring, and troubleshooting your server's network interfaces. This guide covers using the &lt;code&gt;ip&lt;/code&gt; command to manage &lt;code&gt;ip&lt;/code&gt; objects such as links, addresses, routes, and neighbors, including interface configuration, static routing, enabling/disabling interfaces, and MTU management. By the end, you'll be able to inspect, configure, and troubleshoot your server's network interfaces using the &lt;code&gt;ip&lt;/code&gt; command.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy an Ubuntu 24.04 server instance to test the &lt;code&gt;ip&lt;/code&gt; command functionalities.&lt;/li&gt;
&lt;li&gt;Attach the server to a VPC network.&lt;/li&gt;
&lt;li&gt;Access the server using SSH as a non-root user with sudo privileges.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Use the IP Command
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ip&lt;/code&gt; command uses the following syntax to manage network information and interfaces on your server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="o"&gt;[&lt;/span&gt;OPTIONS] OBJECT COMMAND &lt;span class="o"&gt;[&lt;/span&gt;ARGUMENTS]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the above command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OPTIONS&lt;/strong&gt;: Sets the &lt;code&gt;ip&lt;/code&gt; command operation mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OBJECT&lt;/strong&gt;: Specifies the type of network object to view or manage. For example, &lt;code&gt;link&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;route&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COMMAND&lt;/strong&gt;: Defines the action to perform on the specified object. For example, &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;del&lt;/code&gt;, &lt;code&gt;show&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ARGUMENTS&lt;/strong&gt;: Sets additional options to execute with the command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  View Network Interface Information
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ip&lt;/code&gt; command displays network interface information at varying levels of detail depending on the object and options used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. View information about all network interfaces available on the server:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output should be similar to the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp1s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 56:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Based on the above output, the command displays the total number of available interfaces, the MTU value, state, and MAC address for each interface. Active interfaces use the &lt;code&gt;UP&lt;/code&gt; state while inactive interfaces use the &lt;code&gt;DOWN&lt;/code&gt; state. Interfaces with a &lt;code&gt;DOWN&lt;/code&gt; state cannot send or receive network traffic on the server unless enabled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Display network interfaces in an active and running state:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;show up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp1s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 56:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Use the &lt;code&gt;-br&lt;/code&gt; option to display only important information about the network interfaces:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nt"&gt;-br&lt;/span&gt; &lt;span class="nb"&gt;link &lt;/span&gt;show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lo               UNKNOWN        00:00:00:00:00:00 &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; 
enp1s0           UP             56:00:04:ef:40:23 &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; 
enp8s0           UP             5a:00:04:ef:40:23 &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Use the &lt;code&gt;addr&lt;/code&gt; option, or its &lt;code&gt;a&lt;/code&gt; shorthand, to view full information including IP addresses on all network interfaces:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output should look like the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp1s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 56:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
    inet 192.0.2.100/23 brd 192.0.2.255 scope global dynamic enp1s0
       valid_lft 78163sec preferred_lft 78163sec
    inet6 2a05:f480:2c00:1ef7:5400:4ff:feef:4023/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591891sec preferred_lft 604691sec
    inet6 fe80::5400:4ff:feef:4023/64 scope link 
       valid_lft forever preferred_lft forever
3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc mq state UP group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
    inet 10.50.112.3/20 brd 10.50.127.255 scope global enp8s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5800:4ff:feef:4023/64 scope link 
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. View specific information about a specific network interface. For example, view information about the VPC interface, &lt;code&gt;enp8s0&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip addr show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc mq state UP group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
    inet 10.50.112.3/20 brd 10.50.127.255 scope global enp8s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5800:4ff:feef:4023/64 scope link 
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. View detailed statistics about a specific network interface. For example, &lt;code&gt;enp8s0&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nb"&gt;link &lt;/span&gt;show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output should be similar to the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
    RX:  bytes packets errors dropped  missed   mcast
      25426631   61808      0       0       0       0
    TX:  bytes packets errors dropped carrier collsns
       4351567   40677      0       0       0       0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Manage Interface IP Addresses Using the IP Command
&lt;/h2&gt;

&lt;p&gt;IP addresses can be assigned to network interfaces using the Dynamic Host Configuration Protocol (DHCP) or static addressing to enable network connections on the interface. DHCP is the default address assignment protocol on most servers, but static IP addressing may be required when working with multiple addresses or new interfaces on your server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add a new IP address on your network interface. Replace &lt;code&gt;enp8s0&lt;/code&gt; with your desired interface name:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip addr add 192.168.1.10/24 dev enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command adds a new private network address &lt;code&gt;192.168.1.10&lt;/code&gt; to the &lt;code&gt;enp8s0&lt;/code&gt; interface. Depending on your addressing scheme, the interface can run with multiple IP addresses that offer different functions or services on your server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. View the interface information to verify that the new IP address is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip addr show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc mq state UP group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
    inet 10.50.112.3/20 brd 10.50.127.255 scope global enp8s0
       valid_lft forever preferred_lft forever
    inet 192.168.1.10/24 scope global enp8s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5800:4ff:feef:4023/64 scope link 
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using multiple IP addresses, you may need to configure routing to enable specific network connections to use the interface IP address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use the &lt;code&gt;del&lt;/code&gt; option to remove IP addresses on network interfaces. For example, remove the &lt;code&gt;192.168.1.10/24&lt;/code&gt; address on the &lt;code&gt;enp8s0&lt;/code&gt; interface:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip addr del 192.168.1.10/24 dev enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. View the interface address information to verify that the address is removed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip addr show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc mq state UP group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
    inet 10.50.112.3/20 brd 10.50.127.255 scope global enp8s0
       valid_lft forever preferred_lft forever
    inet6 fe80::5800:4ff:feef:4023/64 scope link 
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure Static Routing Using the IP Command
&lt;/h2&gt;

&lt;p&gt;Static routing directs traffic to remote networks through a specific network interface or IP address as the next hop to reach the network. Network requests to unmatched destinations are directed through the default route via the main server interface, while requests to unknown routes may fail due to missing route information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. View the default IP routing table:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip route show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output should be similar to the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;default via 192.0.2.100 dev enp1s0 proto dhcp src 192.0.2.100 metric 100 
10.50.112.0/20 dev enp8s0 proto kernel scope link src 10.50.112.3 
192.0.2.0/23 dev enp1s0 proto kernel scope link src 192.0.2.100 
169.254.169.254 via 192.0.2.100 dev enp1s0 proto dhcp src 192.0.2.100 metric 100 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Based on the above output, direct network addresses use the &lt;code&gt;dev&lt;/code&gt; option while non-linked addresses use the &lt;code&gt;via&lt;/code&gt; option to direct network traffic using the specified address as the next hop. The &lt;code&gt;default&lt;/code&gt; route handles all unknown routes on the server unless specified in the routing table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Add a new static route to a remote network such as &lt;code&gt;10.0.0.0/16&lt;/code&gt; accessible through a server on the VPC network at &lt;code&gt;10.50.112.4&lt;/code&gt; as the next hop:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip route add 10.0.0.0/16 via 10.50.112.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. View the routing table again to verify that the new route is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip route show
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;default via 192.0.2.100 dev enp1s0 proto dhcp src 192.0.2.100 metric 100 
10.0.0.0/16 via 10.50.112.4 dev enp8s0 
10.50.112.0/20 dev enp8s0 proto kernel scope link src 10.50.112.3 
192.0.2.0/23 dev enp1s0 proto kernel scope link src 192.0.2.100 
169.254.169.254 via 192.0.2.100 dev enp1s0 proto dhcp src 192.0.2.100 metric 100 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enable or Disable Network Interfaces
&lt;/h2&gt;

&lt;p&gt;Disabling a network interface stops it from sending or receiving traffic without removing its configuration, which is useful for maintenance or troubleshooting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use the &lt;code&gt;ip link set&lt;/code&gt; command to disable the &lt;code&gt;enp8s0&lt;/code&gt; interface:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev enp8s0 down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. View the network interface status and verify that the state is &lt;code&gt;DOWN&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1450 qdisc mq state DOWN mode DEFAULT group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Enable the network interface:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev enp8s0 up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. View the interface information to verify the new status:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing the Network Interface MTU (Maximum Transmission Unit)
&lt;/h2&gt;

&lt;p&gt;MTU is the maximum size of a packet in bytes that can be transmitted over a network interface without fragmentation. The default MTU value on most network interfaces is &lt;code&gt;1500&lt;/code&gt;. Depending on your needs, you may modify the value to allow the transfer of large or small data packets on private networks such as a VPC network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. View your target interface MTU value. For example, the VPC interface &lt;code&gt;enp8s0&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Verify the MTU value in your output, similar to the one below:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Set a new MTU value on the interface. For example, &lt;code&gt;3000&lt;/code&gt;, to transfer large data packets and improve efficiency with other servers in the VPC network:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;dev enp8s0 mtu 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. View the interface information again and verify the new MTU value:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;show enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3: enp8s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 3000 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 5a:00:04:ef:40:23 brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;ip neigh&lt;/code&gt; to inspect and manage the ARP/neighbor table on your server.&lt;/li&gt;
&lt;li&gt;Persist static routes and IP addresses in your distribution's network configuration so they survive a reboot.&lt;/li&gt;
&lt;li&gt;Combine &lt;code&gt;ip -br&lt;/code&gt; output with a monitoring script to alert on interfaces that drop to a &lt;code&gt;DOWN&lt;/code&gt; state.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;man ip&lt;/code&gt; for the full list of &lt;code&gt;ip&lt;/code&gt; objects and commands beyond what's covered here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-use-the-linux-ip-command-to-manage-server-network-interfaces" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>networking</category>
      <category>cli</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Scanning for Open Ports and Vulnerabilities with Nmap</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:21:07 +0000</pubDate>
      <link>https://dev.to/vultr/scanning-for-open-ports-and-vulnerabilities-with-nmap-2f9g</link>
      <guid>https://dev.to/vultr/scanning-for-open-ports-and-vulnerabilities-with-nmap-2f9g</guid>
      <description>&lt;p&gt;Nmap (Network Mapper) is an open-source network discovery tool that identifies hosts, services, and potential security issues. It's an essential tool for system administrators and developers to map a server's network, letting you see which ports are open, what services are running, and what operating system a target system is using. This guide covers key security tasks with Nmap, including scanning a host for open ports, identifying services running on those ports, and detecting vulnerabilities using the Nmap Scripting Engine (NSE), plus how to interpret the output and take basic remediation steps. By the end, you'll have run multiple Nmap scans, detected a real misconfiguration, and fixed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, you need access to a Linux-based server as a non-root user with sudo privileges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Nmap
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Install &lt;code&gt;nmap&lt;/code&gt; according to your operating system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On Debian/Ubuntu-based systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Rocky Linux and other RHEL-based systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Verify the installation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nmap &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nmap version 7.94SVN ( https://nmap.org )
Platform: x86_64-pc-linux-gnu
Compiled with: liblua-5.4.6 openssl-3.0.13 libssh2-1.11.0 libz-1.3 libpcre2-10.42 libpcap-1.10.4 nmap-libdnet-1.12 ipv6
Compiled without:
Available nsock engines: epoll poll select
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The &lt;code&gt;nmap&lt;/code&gt; Command
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;nmap&lt;/code&gt; command uses the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nmap [SCAN TYPES...] [OPTIONS] TARGET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[SCAN TYPES...]&lt;/code&gt;: Defines how Nmap scans. This group controls if and how Nmap probes the target for open ports.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[OPTIONS]&lt;/code&gt;: One or more optional flags and parameters that modify Nmap's behavior. They control the output's scope, speed, and verbosity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TARGET&lt;/code&gt;: The hosts or networks to be scanned. You can provide a single IP, an IP range, a hostname, or a subnet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scan Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-sS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP SYN scan (default, root)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-sT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP connect scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-sU&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UDP scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-sn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Host discovery only (no port scan)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-sV&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detect service versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-O&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detect operating system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aggressive scan (enables -sV, -O, -sC)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Command Options
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Port Specification&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Specify which ports to scan, like &lt;code&gt;-p 22,80,443&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast Scan&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-F&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scans only the 100 most common ports instead of the default 1000.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verbose Output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provides detailed output.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Perform a Basic Port Scan
&lt;/h2&gt;

&lt;p&gt;A basic scan checks the 1,000 most common ports. This guide uses &lt;code&gt;localhost&lt;/code&gt; as the target — replace it with an IP address or hostname you have explicit permission to scan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Run a basic scan:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nmap localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-03 21:49 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000011s latency).
Other addresses for localhost (not scanned): ::1
Not shown: 997 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
3001/tcp open  nessus
5432/tcp open  postgresql

Nmap done: 1 IP address (1 host up) scanned in 0.07 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding the output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not shown: 997 closed tcp ports&lt;/strong&gt;: These ports are closed or filtered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PORT&lt;/strong&gt;: Port number and protocol.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STATE&lt;/strong&gt;: Whether the port is open, closed, or filtered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SERVICE&lt;/strong&gt;: The service Nmap detected running on the port (when identifiable).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this example, open ports include 22 (SSH), 3001 (reported as nessus), and 5432 (PostgreSQL).&lt;/p&gt;

&lt;h2&gt;
  
  
  Scan for Service Versions and Operating Systems
&lt;/h2&gt;

&lt;p&gt;Nmap can reveal detailed service information on open ports, including version numbers, which helps detect outdated or vulnerable services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use the &lt;code&gt;-sV&lt;/code&gt; flag to probe open ports for running services:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nmap &lt;span class="nt"&gt;-sV&lt;/span&gt; localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-03 21:56 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000011s latency).
Other addresses for localhost (not scanned): ::1
Not shown: 997 closed tcp ports (reset)
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 9.6p1 Ubuntu 3ubuntu13.13 (Ubuntu Linux; protocol 2.0)
3001/tcp open  nessus?
5432/tcp open  postgresql PostgreSQL DB 9.6.0 or later
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scan shows specific versions for detected services, such as &lt;code&gt;OpenSSH 9.6p1&lt;/code&gt; and &lt;code&gt;PostgreSQL 9.6.0&lt;/code&gt; or later. The question mark after Nessus indicates that Nmap is unsure, and the service identification may require further verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scan for Vulnerabilities Using the Nmap Scripting Engine (NSE)
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Nmap Scripting Engine (NSE)&lt;/strong&gt; allows Nmap to use scripts to automate various networking tasks, including vulnerability scanning. The &lt;code&gt;vuln&lt;/code&gt; script category tests services for known vulnerabilities. This section installs &lt;code&gt;vsftpd&lt;/code&gt;, an FTP server, intentionally creates a vulnerability, and then detects and mitigates it using Nmap's scripting engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install vsftpd.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On Ubuntu/Debian-based systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; vsftpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Rocky Linux/RHEL-based systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; vsftpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Enable anonymous login for vsftpd to create a vulnerability. Edit the vsftpd configuration file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/vsftpd.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the line &lt;code&gt;anonymous_enable=NO&lt;/code&gt; and change the value from &lt;code&gt;NO&lt;/code&gt; to &lt;code&gt;YES&lt;/code&gt;. Save and close the file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Restart vsftpd:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart vsftpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you've intentionally created this issue, you can use a specific Nmap script to detect it. The &lt;code&gt;ftp-anon&lt;/code&gt; script is part of the NSE and automatically tests whether an FTP server allows anonymous login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Run a vulnerability scan using &lt;code&gt;ftp-anon&lt;/code&gt; on port &lt;code&gt;21&lt;/code&gt;, the port used by vsftpd:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nmap &lt;span class="nt"&gt;-p&lt;/span&gt; 21 &lt;span class="nt"&gt;--script&lt;/span&gt; ftp-anon localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-22 11:04 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00010s latency).
Other addresses for localhost (not scanned): ::1

PORT   STATE SERVICE
21/tcp open  ftp
|_ftp-anon: Anonymous FTP login allowed (FTP code 230)

Nmap done: 1 IP address (1 host up) scanned in 0.14 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scan detected an issue: &lt;code&gt;Anonymous FTP login allowed (FTP code 230)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps to Mitigate
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Edit the configuration file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/vsftpd.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the line &lt;code&gt;anonymous_enable=YES&lt;/code&gt; and change the value from &lt;code&gt;YES&lt;/code&gt; to &lt;code&gt;NO&lt;/code&gt;. Save and exit the file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Restart the service to apply the changes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart vsftpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Run the Nmap scan on port &lt;code&gt;21&lt;/code&gt; again:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nmap &lt;span class="nt"&gt;-p&lt;/span&gt; 21 &lt;span class="nt"&gt;--script&lt;/span&gt; ftp-anon localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-08-22 11:19 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00010s latency).
Other addresses for localhost (not scanned): ::1

PORT   STATE SERVICE
21/tcp open  ftp

Nmap done: 1 IP address (1 host up) scanned in 2.53 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Automate recurring Nmap scans with cron to catch newly opened ports or services.&lt;/li&gt;
&lt;li&gt;Explore the full &lt;code&gt;vuln&lt;/code&gt; NSE script category to check for known CVEs beyond anonymous FTP.&lt;/li&gt;
&lt;li&gt;Pair Nmap output with a firewall tool like UFW or firewalld to close unneeded ports.&lt;/li&gt;
&lt;li&gt;Review the &lt;a href="https://nmap.org/docs.html" rel="noopener noreferrer"&gt;Nmap official documentation&lt;/a&gt; for more scan types and NSE scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-scan-for-open-ports-and-vulnerabilities-with-nmap" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>nmap</category>
      <category>security</category>
      <category>networking</category>
      <category>linux</category>
    </item>
    <item>
      <title>Setting Up Firewall Policies using Firewalld</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:20:24 +0000</pubDate>
      <link>https://dev.to/vultr/setting-up-firewall-policies-using-firewalld-1cc0</link>
      <guid>https://dev.to/vultr/setting-up-firewall-policies-using-firewalld-1cc0</guid>
      <description>&lt;p&gt;Firewalld is a dynamic firewall application that provides a flexible way of setting up network filtering policies on a server by extending the kernel-based iptables functionality. It provides flexible configuration options, including zone-based firewall management policies that represent a specific level of trust depending on your server's network connections, to protect the system from unauthorized access. This guide walks through setting up firewall policies using Firewalld and configuring sample rules to filter network requests on a Linux server. By the end, you'll have Firewalld installed, zones and rules configured, and panic mode ready as a last-resort lockdown.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Rocky Linux (or other RHEL-based) server to use as the workstation.&lt;/li&gt;
&lt;li&gt;Access to the server using SSH as a non-root user with sudo privileges.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the server:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf update
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Install Firewalld
&lt;/h2&gt;

&lt;p&gt;Firewalld is available as a default system package on most Red Hat-based server distributions such as CentOS, Rocky Linux, and AlmaLinux. When unavailable, follow the steps below to install and enable Firewalld to set up firewall filtering rules on the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install Firewalld:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;firewalld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Enable the Firewalld application to start at boot time:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;firewalld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Start Firewalld:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start firewalld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Verify that the Firewalld service is active and running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Save all runtime changes to the permanent Firewalld configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--runtime-to-permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Set Up Firewalld Zones
&lt;/h2&gt;

&lt;p&gt;Firewalld zones represent the level of trust for network connections associated with a specific network interface. Each zone includes a different security level and policies that govern the traffic flow on the associated interface. By default, Firewalld runs with the following zones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;public&lt;/code&gt;: Applies to public-facing interfaces connected to external networks such as the Internet.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;home&lt;/code&gt;: Applies to network interfaces associated with home networks or small office environments.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;work&lt;/code&gt;: Works with corporate or organization network interfaces with better security over the home zone.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal&lt;/code&gt;: Works with private interfaces connected to networks such as a VPC.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;external&lt;/code&gt;: Applies to external untrusted networks. It's similar to the public zone but applies a higher security level by blocking incoming connection requests while accepting outgoing network connections.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dmz&lt;/code&gt;: Works with network interfaces connected to demilitarized zones partly connected to the Internet and filtered by a firewall.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;block&lt;/code&gt;: Blocks all incoming and outgoing network traffic. It's useful when troubleshooting server network interfaces or networking errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;1. View all available Firewalld zones:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--get-zones&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;block dmz drop external home internal nm-shared public trusted work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. View the default Firewalld zone:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--get-default-zone&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. View the active Firewalld zone and the associated server network interfaces:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--get-active-zones&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public
  interfaces: enp1s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. View the Firewalld zone assigned to a specific network interface. For example, view the &lt;code&gt;enp8s0&lt;/code&gt; network interface zone:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--get-zone-of-interface&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;enp8s0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Change an interface from one zone to another. For example, change the &lt;code&gt;enp1s0&lt;/code&gt; interface to the external zone:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;home &lt;span class="nt"&gt;--change-interface&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eth0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switching interfaces between zones sets different firewall rules and policies on the network interface for troubleshooting or testing purposes on the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. View the active zones again to verify that two Firewalld zones are active due to the new interface assignment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--get-active-zones&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;home&lt;/span&gt;
  &lt;span class="s"&gt;interfaces&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;enp8s0&lt;/span&gt;
&lt;span class="s"&gt;public&lt;/span&gt;
  &lt;span class="s"&gt;interfaces&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;enp1s0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. View all firewall rules associated with the active zone. By default, &lt;code&gt;public&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--list-all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;public (active)&lt;/span&gt;
  &lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="s"&gt;icmp-block-inversion&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
  &lt;span class="s"&gt;interfaces&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;enp1s0 enp8s0&lt;/span&gt;
  &lt;span class="s"&gt;sources&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cockpit dhcpv6-client ssh&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;forward&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;masquerade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
  &lt;span class="na"&gt;forward-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;source-ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;icmp-blocks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;rich rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;8. Change the active zone depending on your server networking environment. For example, change the Firewalld zone to &lt;code&gt;external&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--change-zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Set Up Firewall Policies using Firewalld
&lt;/h2&gt;

&lt;p&gt;Firewalld accepts network ports, system services, and service names across multiple zones depending on your server interfaces. Every Firewalld zone, for example &lt;code&gt;public&lt;/code&gt;, applies the set policies and rules to the associated interfaces. Follow the steps below to set up firewall policies using Firewalld on the server.&lt;/p&gt;

&lt;p&gt;To delete Firewalld rules, replace the &lt;code&gt;--add&lt;/code&gt; option with &lt;code&gt;--remove&lt;/code&gt; to delete the rule. For example, delete the FTP port &lt;code&gt;21&lt;/code&gt; policy in the public zone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--remove-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allow Network Ports
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Add a new Firewalld rule that allows the SSH TCP port &lt;code&gt;22&lt;/code&gt; through the public zone:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above rule allows SSH access to the server using port &lt;code&gt;22&lt;/code&gt; through the public zone. The rule is also permanently saved in the firewall table using the &lt;code&gt;--permanent&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Allow another service port, &lt;code&gt;80&lt;/code&gt;, through the firewall:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. View the active Firewalld zone ports to verify the new rules:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-ports&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;22/tcp 80/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Allow a range of ports through the public Firewalld zone. For example, port &lt;code&gt;4990&lt;/code&gt; to &lt;code&gt;4999&lt;/code&gt; depending on your internal hosts:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4990-4999/udp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allow System Services
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. View the list of available Firewalld services on your server:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--get-services&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RH-Satellite-6 RH-Satellite-6-capsule afp amanda-client amanda-k5-client amqp amqps apcupsd audit ausweisapp2 bacula bacula-client bb bgp bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc bittorrent-lsd ceph ceph-mon cfengine checkmk-agent cockpit collectd condor-collector cratedb ctdb dhcp dhcpv6 dhcpv6-client distcc dns dns-over-tls docker-registry docker-swarm dropbox-lansync elasticsearch etcd-client etcd-server finger foreman foreman-proxy freeipa-4 freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp galera ganglia-client ganglia-master git gpsd grafana gre high-availability http http3 https ident imap imaps ipfs ipp ipp-client ipsec irc ircs iscsi-target isns jenkins kadmin kdeconnect kerberos kibana klogin kpasswd kprop kshell kube-api kube-apiserver kube-control-plane kube-control-plane-secure kube-controller-manager kube-controller-manager-secure kube-nodeport-services kube-scheduler kube-scheduler-secure kube-worker kubelet kubelet-readonly kubelet-worker ldap ldaps libvirt libvirt-tls lightning-network llmnr llmnr-tcp llmnr-udp managesieve matrix mdns memcache minidlna mongodb mosh mountd mqtt mqtt-tls ms-wbt mssql murmur mysql nbd netbios-ns netdata-dashboard nfs nfs3 nmea-0183 nrpe ntp nut openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole plex pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy prometheus prometheus-node-exporter proxy-dhcp ps3netsrv ptp pulseaudio puppetmaster quassel radius rdp redis redis-sentinel rpc-bind rquotad rsh rsyncd rtsp salt-master samba samba-client samba-dc sane sip sips slp smtp smtp-submission smtps snmp snmptls snmptls-trap snmptrap spideroak-lansync spotify-sync squid ssdp ssh steam-streaming svdrp svn syncthing syncthing-gui synergy syslog syslog-tls telnet tentacle tftp tile38 tinc tor-socks transmission-client upnp-client vdsm vnc-server wbem-http wbem-https wireguard ws-discovery ws-discovery-client ws-discovery-tcp ws-discovery-udp wsman wsmans xdmcp xmpp-bosh xmpp-client xmpp-local xmpp-server zabbix-agent zabbix-server zerotier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Allow a service such as HTTPS for access through the public zone table:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Allow another service, such as FTP, through the public zone:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ftp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Reload Firewalld to apply the new service changes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cockpit dhcpv6-client ftp http https ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable Forwarding and IP Address Rules
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Allow incoming traffic to the SSH port &lt;code&gt;22&lt;/code&gt; from your public server IP address &lt;code&gt;192.0.2.100&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-rich-rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'rule family="ipv4" source address="192.0.2.100" port protocol="tcp" port="80" accept'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Forward traffic from one port to another. For example, forward traffic from the internal port &lt;code&gt;8080&lt;/code&gt; to the destination port &lt;code&gt;8900&lt;/code&gt; through all IP addresses:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-forward-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80:proto&lt;span class="o"&gt;=&lt;/span&gt;tcp:toport&lt;span class="o"&gt;=&lt;/span&gt;8080:toaddr&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0 &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above rule forwards all incoming requests on port &lt;code&gt;8080&lt;/code&gt; to &lt;code&gt;8900&lt;/code&gt; to securely expose a specific resource on different ports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Reload Firewalld to save the zone changes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Create Custom Firewalld Zones
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Create a new Firewalld zone with a custom name, such as &lt;code&gt;example-zone&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--new-zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;example-zone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Reload Firewalld to save changes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. View the Firewalld zones and verify that the new custom zone is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--get-zones&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;block dmz drop example-zone external home internal nm-shared public trusted work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Firewalld Panic Mode
&lt;/h2&gt;

&lt;p&gt;Firewalld panic mode blocks all incoming and outgoing network connections on the server to protect the system in case of security threats. When enabled, the server blocks all connections, including your active SSH session. Follow the steps below to enable panic mode and troubleshoot the system in case of external threats or network failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. View the Firewalld panic mode status:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--query-panic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Enable panic mode to block all network connections on the server:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--panic-on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. View the Firewalld panic mode status and verify that it's active:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--query-panic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case Firewalld blocks your SSH session, use your cloud provider's web console to start a new, out-of-band terminal session to the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Audit your zone assignments with &lt;code&gt;firewall-cmd --get-active-zones&lt;/code&gt; before opening any new ports in production.&lt;/li&gt;
&lt;li&gt;Replace broad rich rules with tighter source-address scoping once you've confirmed which hosts actually need access.&lt;/li&gt;
&lt;li&gt;Script &lt;code&gt;firewall-cmd --panic-off&lt;/code&gt; into your incident runbook so panic mode doesn't leave the server unreachable longer than intended.&lt;/li&gt;
&lt;li&gt;Explore &lt;code&gt;/etc/firewalld&lt;/code&gt; directly if you need to version-control your zone and service definitions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-set-up-firewall-policies-using-firewalld" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>security</category>
      <category>networking</category>
      <category>firewall</category>
    </item>
    <item>
      <title>Building Disaggregated Inference with NVIDIA Dynamo</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:18:08 +0000</pubDate>
      <link>https://dev.to/vultr/building-disaggregated-inference-with-nvidia-dynamo-5fn8</link>
      <guid>https://dev.to/vultr/building-disaggregated-inference-with-nvidia-dynamo-5fn8</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.nvidia.com/dynamo/getting-started/quickstart" rel="noopener noreferrer"&gt;NVIDIA Dynamo&lt;/a&gt; is an open-source inference framework for deploying large-scale generative AI models across multi-node, multi-GPU environments. Disaggregated serving architecture separates prompt processing (prefill) and token generation (decode) phases across different GPU workers, enabling independent scaling and optimization of each phase based on their distinct computational characteristics. This guide covers building disaggregated inference deployments with NVIDIA Dynamo using AIConfigurator for optimal configuration discovery, deploying on Kubernetes with RDMA, and validating performance. Disaggregated architecture can achieve up to 1.7x throughput improvement over aggregated serving for large models (32B+) with long input contexts; for smaller models, AIConfigurator automatically determines when aggregated serving is more efficient. By the end, you'll have used AIConfigurator to find an optimal serving configuration, deployed it on a GPU Kubernetes cluster with RDMA-accelerated KV cache transfer, and validated throughput with AIPerf.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, ensure you have &lt;strong&gt;one&lt;/strong&gt; of the following environments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Kubernetes Deployment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Kubernetes cluster with NVIDIA GPU nodes and the &lt;a href="https://github.com/Mellanox/k8s-rdma-shared-dev-plugin" rel="noopener noreferrer"&gt;RDMA device plugin&lt;/a&gt; installed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; configured to access your cluster.&lt;/li&gt;
&lt;li&gt;An RDMA-capable network (InfiniBand or RoCE).&lt;/li&gt;
&lt;li&gt;The NVIDIA Dynamo platform installed on your cluster, including the Dynamo Operator and GPU resource management configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Standalone Instance Deployment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GPU-enabled server with NVIDIA GPUs and the NVIDIA Container Toolkit configured.&lt;/li&gt;
&lt;li&gt;2+ GPUs for disaggregated serving.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;Docker Engine&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A non-root user with sudo privileges.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Common Requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.10+ for running AIConfigurator.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://huggingface.co/join" rel="noopener noreferrer"&gt;Hugging Face account&lt;/a&gt; with an &lt;a href="https://huggingface.co/settings/tokens" rel="noopener noreferrer"&gt;access token&lt;/a&gt; for gated models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding Disaggregated Serving Architecture
&lt;/h2&gt;

&lt;p&gt;Disaggregated serving splits LLM inference into two specialized phases, each optimized for different computational characteristics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prefill Phase vs Decode Phase
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Characteristics&lt;/th&gt;
&lt;th&gt;Optimization Focus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prefill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Processes entire input prompt in parallel&lt;/td&gt;
&lt;td&gt;Memory-bound, benefits from high-bandwidth GPUs, uses low batch sizes (1-4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generates tokens sequentially, one at a time&lt;/td&gt;
&lt;td&gt;Compute-bound, benefits from high concurrency, uses high batch sizes (256-1024)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Architecture Components
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prefill Workers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Process incoming prompts and generate KV cache.&lt;/li&gt;
&lt;li&gt;Run with low batch sizes for optimal TTFT.&lt;/li&gt;
&lt;li&gt;Can use different tensor parallelism (TP) than decode workers.&lt;/li&gt;
&lt;li&gt;Transfer KV cache to decode workers via NIXL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decode Workers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Receive KV cache from prefill workers.&lt;/li&gt;
&lt;li&gt;Generate output tokens sequentially.&lt;/li&gt;
&lt;li&gt;Handle high concurrency with large batch sizes.&lt;/li&gt;
&lt;li&gt;Optimized for sustained token throughput.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Request Routing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend routes requests to available prefill workers.&lt;/li&gt;
&lt;li&gt;PrefillRouter selects prefill workers based on KV cache affinity.&lt;/li&gt;
&lt;li&gt;After prefill, decode workers receive KV cache and continue generation.&lt;/li&gt;
&lt;li&gt;NIXL enables GPU-to-GPU KV cache transfer with RDMA.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Use Disaggregated Serving
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use Disaggregated Serving When:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model size is large (32B+ parameters)&lt;/strong&gt; — smaller models have lower KV cache transfer overhead that doesn't justify phase separation.&lt;/li&gt;
&lt;li&gt;Input sequence length (ISL) significantly exceeds output sequence length (OSL) — typically ISL &amp;gt; 4000 tokens.&lt;/li&gt;
&lt;li&gt;Workload requires independent scaling of prefill and decode capacity.&lt;/li&gt;
&lt;li&gt;RDMA-capable network infrastructure is available (critical requirement).&lt;/li&gt;
&lt;li&gt;Maximizing per-GPU throughput is the primary objective.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Aggregated Serving When:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model size is small (&amp;lt;10B parameters)&lt;/strong&gt; — KV cache transfer overhead outweighs benefits.&lt;/li&gt;
&lt;li&gt;Balanced ISL/OSL ratios (between 2:1 and 10:1).&lt;/li&gt;
&lt;li&gt;Simpler deployment with lower operational complexity is preferred.&lt;/li&gt;
&lt;li&gt;RDMA infrastructure is not available.&lt;/li&gt;
&lt;li&gt;Strict latency SLAs require minimal inter-worker communication overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance Benefits (for Large Models):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Up to &lt;strong&gt;1.7x&lt;/strong&gt; higher throughput compared to aggregated serving for large models with long-context workloads.&lt;/li&gt;
&lt;li&gt;Better GPU utilization through phase-specific optimization.&lt;/li&gt;
&lt;li&gt;Independent scaling of prefill and decode resources based on workload patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: RDMA is required. Disaggregated serving requires RDMA for KV cache transfer. Without RDMA, performance degrades by &lt;strong&gt;40x&lt;/strong&gt; (TTFT increases from ~400ms to 10+ seconds) as the system falls back to TCP transfer, creating a severe bottleneck.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Install and Configure AIConfigurator
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ai-dynamo/aiconfigurator" rel="noopener noreferrer"&gt;AIConfigurator&lt;/a&gt; is a performance optimization tool that automatically determines the optimal configuration for deploying LLMs with Dynamo by evaluating both aggregated and disaggregated architectures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a Python 3 virtual environment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv aiconfigurator_env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Activate the virtual environment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;aiconfigurator_env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Install AIConfigurator using pip:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;aiconfigurator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Verify the installation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;aiconfigurator version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output displays the installed version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. View available parameters and help documentation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;aiconfigurator cli default &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding AIConfigurator Parameters
&lt;/h3&gt;

&lt;p&gt;AIConfigurator requires the following key parameters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--hf_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HuggingFace model ID or local path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--system&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GPU system type&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;h200_sxm&lt;/code&gt;, &lt;code&gt;h100_sxm&lt;/code&gt;, &lt;code&gt;a100_sxm&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--total_gpus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of GPUs available for deployment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--isl&lt;/code&gt; / &lt;code&gt;--osl&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Input/Output sequence lengths in tokens&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;4000&lt;/code&gt; / &lt;code&gt;500&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--ttft&lt;/code&gt; / &lt;code&gt;--tpot&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;SLA targets - Time To First Token (ms) and Time Per Output Token (ms)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;600&lt;/code&gt; / &lt;code&gt;16.67&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--backend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inference backend&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vllm&lt;/code&gt;, &lt;code&gt;trtllm&lt;/code&gt;, &lt;code&gt;sglang&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--backend_version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Backend version&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0.12.0&lt;/code&gt; (for vLLM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--save_dir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Directory to save generated configs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;./results_vllm&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How AIConfigurator Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Evaluates all possible configurations (tensor/pipeline parallelism combinations).&lt;/li&gt;
&lt;li&gt;Simulates performance for both aggregated and disaggregated architectures.&lt;/li&gt;
&lt;li&gt;Filters configurations that meet SLA targets (TTFT, TPOT).&lt;/li&gt;
&lt;li&gt;Ranks results by per-GPU throughput (tokens/s/gpu).&lt;/li&gt;
&lt;li&gt;Automatically selects the best architecture (agg or disagg) based on actual performance.&lt;/li&gt;
&lt;li&gt;Generates deployment manifests for top-ranked configurations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AIConfigurator considers factors like model size, KV cache transfer overhead, and GPU utilization to determine whether disaggregated serving will outperform aggregated for your specific workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Find the Optimal Disaggregated Configuration
&lt;/h2&gt;

&lt;p&gt;Use AIConfigurator to discover the best configuration for your model and hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Run AIConfigurator with your model and system specifications:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;aiconfigurator cli default &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --hf_id nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 \
  --system h200_sxm \
  --total_gpus 4 \
  --isl 4000 \
  --osl 500 \
  --ttft 600 \
  --tpot 16.67 \
  --backend vllm \
  --backend_version 0.12.0 \
  --save_dir ./results_vllm
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command evaluates all possible configurations for 4x H200 GPUs with the specified model and SLA targets, generating deployment manifests in the &lt;code&gt;./results_vllm&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Review the AIConfigurator output summary:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;********************************************************************************
*                     Dynamo aiconfigurator Final Results                      *
********************************************************************************
  ----------------------------------------------------------------------------
  Input Configuration &amp;amp; SLA Target:
    Model: nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 (is_moe: False)
    Total GPUs: 4
    Best Experiment Chosen: agg at 3785.20 tokens/s/gpu (disagg 0.81x better)
  ----------------------------------------------------------------------------
  Overall Best Configuration:
    - Best Throughput: 15,140.82 tokens/s
    - Per-GPU Throughput: 3785.20 tokens/s/gpu
    - Per-User Throughput: 61.41 tokens/s/user
    - TTFT: 395.88ms
    - TPOT: 16.28ms
    - Request Latency: 8521.26ms
  ----------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The summary shows that for this 4B model, aggregated serving performs better than disaggregated (3785.20 vs 3067.51 tokens/s/gpu). This is expected for smaller models where the overhead of KV cache transfer outweighs the benefits of phase separation. Disaggregated serving becomes advantageous with larger models (32B+) and longer context lengths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Review the detailed configuration comparison:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;agg Top Configurations: (Sorted by tokens/s/gpu)
+------+--------------+---------------+--------+-----------------+--------------+-------------------+----------+----------+----+
| Rank | tokens/s/gpu | tokens/s/user |  TTFT  | request_latency | concurrency  | total_gpus (used) | replicas | parallel | bs |
+------+--------------+---------------+--------+-----------------+--------------+-------------------+----------+----------+----+
|  1   |   3785.20    |     61.41     | 395.88 |     8521.26     | 256 (=64x4)  |     4 (4=4x1)     |    4     |  tp1pp1  | 64 |
|  2   |   3036.90    |     65.06     | 331.10 |     8000.51     | 192 (=96x2)  |     4 (4=2x2)     |    2     |  tp2pp1  | 96 |
|  3   |   2305.76    |     65.36     | 249.55 |     7883.93     | 144 (=144x1) |     4 (4=1x4)     |    1     |  tp4pp1  | 144|
+------+--------------+---------------+--------+-----------------+--------------+-------------------+----------+----------+----+

disagg Top Configurations: (Sorted by tokens/s/gpu)
+------+--------------+---------------+--------+-----------------+--------------+-------------------+----------+-------------+-------------+
| Rank | tokens/s/gpu | tokens/s/user |  TTFT  | request_latency | concurrency  | total_gpus (used) | replicas | (p)parallel | (d)parallel |
+------+--------------+---------------+--------+-----------------+--------------+-------------------+----------+-------------+-------------+
|  1   |   3067.51    |     63.99     | 111.33 |     7909.20     | 208 (=104x2) |     4 (4=2x2)     |    2     |   tp1pp1    |  tp1pp1     |
|  2   |   3011.39    |     62.82     | 111.33 |     8054.41     | 208 (=208x1) |     4 (4=1x4)     |    1     |   tp1pp1    |  tp2pp1     |
+------+--------------+---------------+--------+-----------------+--------------+-------------------+----------+-------------+-------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding the Output:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;tokens/s/gpu&lt;/strong&gt;: Overall throughput efficiency — higher is better (agg: 3785.20 vs disagg: 3067.51 = agg is 23% faster).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tokens/s/user&lt;/strong&gt;: Per-request generation speed (inverse of TPOT).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTFT&lt;/strong&gt;: Predicted time to first token (395.88ms for agg, 111.33ms for disagg prefill).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;concurrency&lt;/strong&gt;: Total concurrent requests (e.g., &lt;code&gt;256 (=64x4)&lt;/code&gt; = batch size 64 x 4 replicas).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;parallel&lt;/strong&gt;: Tensor/pipeline parallelism (tp=tensor parallel, pp=pipeline parallel).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt;: For this 4B model, aggregated serving is more efficient due to lower overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: model size matters. Disaggregated serving becomes advantageous with larger models (32B+) where the benefits of phase-specific optimization outweigh KV cache transfer overhead. For smaller models like this 4B example, aggregated serving is more efficient.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;4. Examine the generated directory structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tree results_vllm/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;results_vllm/
└── nvidia/
    └── Llama-3.1-Nemotron-Nano-4B-v1.1_isl4000_osl500_ttft600_tpot16_472853/
        ├── agg/
        │   ├── best_config_topn.csv
        │   ├── config.yaml
        │   ├── pareto.csv
        │   ├── top1/
        │   │   ├── generator_config.yaml
        │   │   ├── k8s_deploy.yaml
        │   │   └── run_0.sh
        │   ├── top2/
        │   │   └── ...
        │   └── top3/
        │       └── ...
        ├── disagg/
        │   ├── best_config_topn.csv
        │   ├── config.yaml
        │   ├── pareto.csv
        │   ├── top1/
        │   │   ├── generator_config.yaml
        │   │   ├── k8s_deploy.yaml
        │   │   └── run_0.sh
        │   └── top2/
        │       └── ...
        └── pareto_frontier.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are organized by model name and experiment parameters. While aggregated serving is optimal for this 4B model, the following sections demonstrate disaggregated deployment for learning purposes and larger model scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Deploy on Kubernetes
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: while AIConfigurator determined that aggregated serving is optimal for this 4B model, the following sections demonstrate disaggregated deployment for learning purposes. Use disaggregated architecture when deploying larger models (32B+) or when AIConfigurator recommends it for your specific workload.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kubernetes deployment provides the infrastructure for disaggregated serving with RDMA-accelerated KV cache transfer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the NVIDIA Dynamo Platform
&lt;/h3&gt;

&lt;p&gt;Before deploying disaggregated workloads, install the NVIDIA Dynamo platform (the Dynamo Operator) on your Kubernetes cluster, configure GPU resource management, set up the &lt;code&gt;DynamoGraphDeployment&lt;/code&gt; CRD, and verify the cluster is ready for Dynamo workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare the Kubernetes Environment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Set your Kubernetes namespace as an environment variable. Replace &lt;code&gt;YOUR_NAMESPACE&lt;/code&gt; with the namespace where you want to deploy Dynamo workloads:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LLM_NAMESPACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_NAMESPACE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create the namespace:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl create namespace &lt;span class="nv"&gt;$LLM_NAMESPACE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Export your Hugging Face token. Replace &lt;code&gt;YOUR_HF_TOKEN&lt;/code&gt; with your Hugging Face access token:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_HF_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Create a Hugging Face token secret for accessing gated models:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl create secret generic hf-token-secret &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;  --namespace $&lt;/span&gt;LLM_NAMESPACE &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;  --from-literal=HF_TOKEN="$&lt;/span&gt;HF_TOKEN&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Create a persistent volume claim for caching model weights:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; model-cache-pvc.yaml
&lt;/span&gt;&lt;span class="go"&gt;apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-cache
&lt;/span&gt;&lt;span class="gp"&gt;  namespace: $&lt;/span&gt;LLM_NAMESPACE
&lt;span class="go"&gt;spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 100Gi
EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: this PVC uses &lt;code&gt;ReadWriteMany&lt;/code&gt; (RWX) access mode, which allows multiple pods to mount the volume simultaneously for shared model caching. Ensure your cluster has a StorageClass that supports RWX access mode (such as NFS, CephFS, or cloud-provider RWX storage). Verify available StorageClasses:&lt;/p&gt;


&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get storageclass
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;6. Apply the PVC configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; model-cache-pvc.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Verify the RDMA device plugin is running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system | &lt;span class="nb"&gt;grep &lt;/span&gt;rdma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should show RDMA device plugin pods running on GPU nodes. If not installed, follow the &lt;a href="https://github.com/Mellanox/k8s-rdma-shared-dev-plugin" rel="noopener noreferrer"&gt;Mellanox RDMA device plugin installation guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Verify RDMA resources are available on your nodes. Replace &lt;code&gt;NODE-NAME&lt;/code&gt; with your GPU node name:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get node NODE-NAME &lt;span class="nt"&gt;-o&lt;/span&gt; json | jq &lt;span class="s1"&gt;'.status.allocatable'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should show &lt;code&gt;rdma/hca_shared_devices&lt;/code&gt; resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;......&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rdma/hca_shared_devices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1k"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;......&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rdma/hca_shared_devices: "1k"&lt;/code&gt; entry confirms RDMA resources are available. If this resource is missing, verify the RDMA device plugin DaemonSet configuration and node labels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Using Generated Manifests
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Review the generated disaggregated deployment manifest:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;results_vllm/nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1_isl4000_osl500_ttft600_tpot16_&lt;span class="k"&gt;*&lt;/span&gt;/disagg/top1/k8s_deploy.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file contains a &lt;code&gt;DynamoGraphDeployment&lt;/code&gt; custom resource with frontend, prefill workers, and decode workers configured according to AIConfigurator's recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Create the complete deployment manifest with RDMA configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; dynamo-disagg-deployment.yaml
&lt;/span&gt;&lt;span class="go"&gt;apiVersion: nvidia.com/v1alpha1
kind: DynamoGraphDeployment
metadata:
  name: dynamo-disagg
&lt;/span&gt;&lt;span class="gp"&gt;  namespace: $&lt;/span&gt;LLM_NAMESPACE
&lt;span class="go"&gt;spec:
  backendFramework: vllm
  pvcs:
    - name: model-cache
      create: false
  services:
    Frontend:
      componentType: frontend
      replicas: 1
      volumeMounts:
        - name: model-cache
          mountPoint: /opt/models
      envs:
        - name: HF_HOME
          value: /opt/models
      extraPodSpec:
        mainContainer:
          image: nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0-cuda13
          imagePullPolicy: IfNotPresent

    VLLMPrefillWorker:
      envFromSecret: hf-token-secret
      componentType: worker
      subComponentType: prefill
      replicas: 2
      resources:
        limits:
          gpu: "1"
      sharedMemory:
        size: 16Gi
      volumeMounts:
        - name: model-cache
          mountPoint: /opt/models
      envs:
        - name: HF_HOME
          value: /opt/models
        - name: POD_UID
          valueFrom:
            fieldRef:
              fieldPath: metadata.uid
        - name: UCX_TLS
          value: "rc_x,rc,dc_x,dc,cuda_copy,cuda_ipc"
        - name: UCX_RNDV_SCHEME
          value: "get_zcopy"
        - name: UCX_RNDV_THRESH
          value: "0"
      extraPodSpec:
        mainContainer:
          image: nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0-cuda13
          workingDir: /workspace
          imagePullPolicy: IfNotPresent
          securityContext:
            capabilities:
              add: ["IPC_LOCK"]
          resources:
            limits:
              rdma/hca_shared_devices: "1"
            requests:
              rdma/hca_shared_devices: "1"
          command: ["python3", "-m", "dynamo.vllm"]
          args:
            - --model
            - "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1"
            - "--tensor-parallel-size"
            - "1"
            - "--kv-cache-dtype"
            - "fp8"
            - "--max-num-seqs"
            - "1"
            - --trust-remote-code
            - --is-prefill-worker

    VLLMDecodeWorker:
      envFromSecret: hf-token-secret
      componentType: worker
      subComponentType: decode
      replicas: 2
      resources:
        limits:
          gpu: "1"
      sharedMemory:
        size: 16Gi
      volumeMounts:
        - name: model-cache
          mountPoint: /opt/models
      envs:
        - name: HF_HOME
          value: /opt/models
        - name: POD_UID
          valueFrom:
            fieldRef:
              fieldPath: metadata.uid
        - name: UCX_TLS
          value: "rc_x,rc,dc_x,dc,cuda_copy,cuda_ipc"
        - name: UCX_RNDV_SCHEME
          value: "get_zcopy"
        - name: UCX_RNDV_THRESH
          value: "0"
      extraPodSpec:
        mainContainer:
          image: nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0-cuda13
          workingDir: /workspace
          imagePullPolicy: IfNotPresent
          securityContext:
            capabilities:
              add: ["IPC_LOCK"]
          resources:
            limits:
              rdma/hca_shared_devices: "1"
            requests:
              rdma/hca_shared_devices: "1"
          command: ["python3", "-m", "dynamo.vllm"]
          args:
            - --model
            - "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1"
            - "--tensor-parallel-size"
            - "1"
            - "--kv-cache-dtype"
            - "fp8"
            - "--max-num-seqs"
            - "1024"
            - --trust-remote-code
            - --is-decode-worker
EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical RDMA Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;rdma/hca_shared_devices&lt;/code&gt; resources&lt;/strong&gt;: Request RDMA resources matching tensor parallelism size (1 for both prefill and decode with TP1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;IPC_LOCK&lt;/code&gt; capability&lt;/strong&gt;: Required for RDMA memory registration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;POD_UID&lt;/code&gt; environment variable&lt;/strong&gt;: Retrieves pod UID from metadata for worker identification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UCX_TLS&lt;/code&gt; environment variable&lt;/strong&gt;: Enables RDMA transports (&lt;code&gt;rc_x&lt;/code&gt;, &lt;code&gt;dc_x&lt;/code&gt; for InfiniBand).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UCX_RNDV_SCHEME=get_zcopy&lt;/code&gt;&lt;/strong&gt;: Enables zero-copy RDMA transfers for maximum performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UCX_RNDV_THRESH=0&lt;/code&gt;&lt;/strong&gt;: Forces RDMA usage for all message sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--trust-remote-code&lt;/code&gt; flag&lt;/strong&gt;: Allows execution of custom model code from Hugging Face.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CUDA 13 runtime&lt;/strong&gt;: Uses &lt;code&gt;nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0-cuda13&lt;/code&gt; for GPU compatibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Deploy the configuration to Kubernetes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; dynamo-disagg-deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Monitor the deployment progress:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$LLM_NAMESPACE&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for all pods to reach &lt;code&gt;Running&lt;/code&gt; status. This may take several minutes as models are downloaded and loaded.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify the Kubernetes Deployment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Check pod status and resource allocation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$LLM_NAMESPACE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output shows all pods running successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                      READY   STATUS    RESTARTS   AGE
dynamo-disagg-0-frontend-77bn5            1/1     Running   0          2m42s
dynamo-disagg-0-vllmdecodeworker-gzgbw    1/1     Running   0          2m42s
dynamo-disagg-0-vllmdecodeworker-kn8xz    1/1     Running   0          2m42s
dynamo-disagg-0-vllmprefillworker-2b7hm   1/1     Running   0          2m42s
dynamo-disagg-0-vllmprefillworker-7p4mq   1/1     Running   0          2m42s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Verify RDMA is active by checking worker logs. Replace &lt;code&gt;PREFILL-WORKER-POD&lt;/code&gt; with your prefill worker pod name from the previous step:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$LLM_NAMESPACE&lt;/span&gt; PREFILL-WORKER-POD | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"UCX&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;NIXL"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output shows NIXL initialization logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-03-03T22:52:23.152441Z  INFO nixl_connector: NIXL is available
2026-03-03T22:52:23.154049Z  INFO factory.create_connector: Creating v1 connector with name: NixlConnector and engine_id: e0dc9d5a-80b5-4b06-aeb9-05958f83d6ea
(Worker pid=1241) 2026-03-03 22:52:25 NIXL INFO    _api.py:363 Backend UCX was instantiated
(Worker pid=1241) 2026-03-03 22:52:25 NIXL INFO    _api.py:253 Initialized NIXL agent: 93612abb-847f-435f-bd56-73bfc8d5f3d6
2026-03-03T22:52:25.487423Z  INFO nixl_connector.register_kv_caches: Registering KV_Caches. use_mla: False, kv_buffer_device: cuda, use_host_buffer: False
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key indicators confirm RDMA is working:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"NIXL is available"&lt;/strong&gt;: NIXL connector initialized successfully.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Backend UCX was instantiated"&lt;/strong&gt;: UCX backend (RDMA transport) is active.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Initialized NIXL agent"&lt;/strong&gt;: NIXL agent ready for KV cache transfer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Registering KV_Caches"&lt;/strong&gt;: KV cache buffers registered for GPU-to-GPU transfer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Test inference through the frontend service:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kubectl port-forward &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$LLM_NAMESPACE&lt;/span&gt; service/dynamo-disagg-frontend 8000:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a separate terminal, send a test request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1",
    "messages": [{"role": "user", "content": "Explain the importance of tensor parallelism in LLM inference."}],
    "max_tokens": 100
  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output displays the model's response in JSON format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Monitor worker metrics to verify disaggregated flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; localhost:8000/metrics | &lt;span class="nb"&gt;grep &lt;/span&gt;dynamo_frontend_requests_total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output displays the metrics showing successful request processing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight prometheus"&gt;&lt;code&gt;&lt;span class="c"&gt;# HELP dynamo_frontend_requests_total Total number of LLM requests processed&lt;/span&gt;
&lt;span class="c"&gt;# TYPE dynamo_frontend_requests_total counter&lt;/span&gt;
&lt;span class="n"&gt;dynamo_frontend_requests_total&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"chat_completions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"nvidia/llama-3.1-nemotron-nano-4b-v1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;request_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"unary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Deploy on Standalone Instances
&lt;/h2&gt;

&lt;p&gt;For standalone instance deployments (non-Kubernetes), refer to the backend-specific deployment guides that cover disaggregated serving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-deploy-inference-using-nvidia-dynamo-and-vllm#deploy-disaggregated-serving" rel="noopener noreferrer"&gt;Deploy vLLM Disaggregated Serving&lt;/a&gt;&lt;/strong&gt;: 2 prefill workers + 2 decode workers with NIXL-based KV cache transfer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-deploy-inference-using-nvidia-dynamo-and-tensorrt-llm#deploy-disaggregated-serving" rel="noopener noreferrer"&gt;Deploy TensorRT-LLM Disaggregated Serving&lt;/a&gt;&lt;/strong&gt;: TRT-LLM disaggregated configuration with KV cache optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-deploy-inference-using-nvidia-dynamo-and-sglang#deploy-disaggregated-serving" rel="noopener noreferrer"&gt;Deploy SGLang Disaggregated Serving&lt;/a&gt;&lt;/strong&gt;: SGLang disaggregated setup with bootstrap_info transfer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These guides provide complete deployment scripts with environment variable configuration, container runtime setup with GPU allocation, NIXL configuration for inter-worker KV cache transfer, and verification/troubleshooting steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Validate Performance with AIPerf
&lt;/h2&gt;

&lt;p&gt;After deployment, validate AIConfigurator's predictions against actual performance using &lt;a href="https://github.com/ai-dynamo/aiperf" rel="noopener noreferrer"&gt;AIPerf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install the AIPerf benchmarking tool:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;aiperf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Derive AIPerf parameters from the AIConfigurator output. Values used for testing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--concurrency 30&lt;/code&gt; (AIConfigurator recommends &lt;code&gt;208&lt;/code&gt; for max throughput)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--num-requests 800&lt;/code&gt; (Recommended: &lt;code&gt;8320&lt;/code&gt; = concurrency x 40 for statistical stability)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--isl 4000 --osl 500&lt;/code&gt; (Match your AIConfigurator inputs)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--extra-inputs "ignore_eos:true"&lt;/code&gt; (Ensures exact OSL tokens generated)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Run the AIPerf benchmark against the deployment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;aiperf profile &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -m nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1 \
  --endpoint-type chat \
  -u http://localhost:8000 \
  --isl 4000 --isl-stddev 0 \
  --osl 500 --osl-stddev 0 \
  --num-requests 800 \
  --concurrency 30 \
  --streaming \
  --extra-inputs "ignore_eos:true" \
  --num-warmup-requests 40 \
  --ui-type simple
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: the command above uses &lt;code&gt;--concurrency 30&lt;/code&gt; and &lt;code&gt;--num-requests 800&lt;/code&gt; for early success and faster testing. For best throughput validation, use the recommended values: &lt;code&gt;--concurrency 208&lt;/code&gt; and &lt;code&gt;--num-requests 8320&lt;/code&gt; (concurrency x 40 for statistical stability).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The benchmark runs 800 requests with 30 concurrent connections. The output displays performance metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                         NVIDIA AIPerf | LLM Metrics
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓
┃                               Metric ┃      avg ┃      min ┃      max ┃      p99 ┃      p90 ┃      p50 ┃      std ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩
│             Time to First Token (ms) │   496.77 │    55.53 │ 8,411.38 │ 6,972.51 │    64.59 │    59.78 │ 1,606.66 │
│            Time to Second Token (ms) │     0.24 │     0.00 │     4.72 │     3.55 │     0.00 │     0.00 │     0.81 │
│      Time to First Output Token (ms) │   496.77 │    55.53 │ 8,411.38 │ 6,972.51 │    64.59 │    59.78 │ 1,606.66 │
│                 Request Latency (ms) │ 2,367.45 │ 1,505.01 │ 9,807.65 │ 8,869.04 │ 1,996.11 │ 1,972.56 │ 1,528.95 │
│             Inter Token Latency (ms) │     3.75 │     2.74 │     3.90 │     3.89 │     3.85 │     3.83 │     0.25 │
│     Output Token Throughput Per User │   268.03 │   256.55 │   365.49 │   357.48 │   279.88 │   260.84 │    22.84 │
│                    (tokens/sec/user) │          │          │          │          │          │          │          │
│      Output Sequence Length (tokens) │   499.53 │   487.00 │   510.00 │   502.00 │   500.10 │   500.00 │     1.30 │
│       Input Sequence Length (tokens) │ 4,000.00 │ 4,000.00 │ 4,000.00 │ 4,000.00 │ 4,000.00 │ 4,000.00 │     0.00 │
│ Output Token Throughput (tokens/sec) │ 6,232.31 │      N/A │      N/A │      N/A │      N/A │      N/A │      N/A │
│    Request Throughput (requests/sec) │    12.48 │      N/A │      N/A │      N/A │      N/A │      N/A │      N/A │
│             Request Count (requests) │   800.00 │      N/A │      N/A │      N/A │      N/A │      N/A │      N/A │
└──────────────────────────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘

Benchmark Duration: 64.12 sec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Compare actual vs predicted performance:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;AIConfigurator Prediction (2 GPUs)&lt;/th&gt;
&lt;th&gt;AIPerf Actual (2 GPUs)&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTFT (ms)&lt;/td&gt;
&lt;td&gt;111.33&lt;/td&gt;
&lt;td&gt;59.78 (p50) / 496.77 (avg)&lt;/td&gt;
&lt;td&gt;p50 close to prediction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ITL/TPOT (ms)&lt;/td&gt;
&lt;td&gt;15.62 (1000/63.99)&lt;/td&gt;
&lt;td&gt;3.75&lt;/td&gt;
&lt;td&gt;4x better than predicted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput (tokens/s)&lt;/td&gt;
&lt;td&gt;6,135.02 (3,067.51 x 2)&lt;/td&gt;
&lt;td&gt;6,232.31&lt;/td&gt;
&lt;td&gt;101.6% - exceeds prediction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-GPU Throughput&lt;/td&gt;
&lt;td&gt;3,067.51 tokens/s/gpu&lt;/td&gt;
&lt;td&gt;3,116 tokens/s/gpu&lt;/td&gt;
&lt;td&gt;Matches prediction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The per-GPU throughput (6,232.31 / 2 GPUs = 3,116 tokens/s/gpu) closely matches AIConfigurator's prediction of 3,067.51 tokens/s/gpu. The total throughput (6,232.31 tokens/s) actually exceeds the prediction for 2 GPUs (6,135.02 tokens/s). This benchmark was run with 2 GPUs (1 prefill + 1 decode worker); scaling to 4 GPUs (2 prefill + 2 decode workers as shown in the deployment manifest) would double the throughput to approximately 12,464 tokens/s. The lower throughput compared to maximum capacity is due to using concurrency 30 instead of the recommended 208. The Inter Token Latency (3.75ms) significantly outperforms predictions, demonstrating efficient token generation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: run benchmarks inside the Kubernetes cluster (using a Job pod) to eliminate network latency. External benchmarking adds latency that affects TTFT measurements but provides realistic end-to-end performance metrics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configuration Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  GPU Allocation Strategies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prefill Workers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allocate fewer GPUs with higher memory bandwidth (memory-bound workload).&lt;/li&gt;
&lt;li&gt;Use lower tensor parallelism (TP1 or TP2) to minimize communication overhead.&lt;/li&gt;
&lt;li&gt;Deploy multiple replicas for higher prefill throughput.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decode Workers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allocate more GPUs for parallel token generation (compute-bound workload).&lt;/li&gt;
&lt;li&gt;Use higher tensor parallelism (TP4 or TP8) to distribute computation.&lt;/li&gt;
&lt;li&gt;Single replica with high batch size often performs best.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Configuration (4 GPUs):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2x Prefill workers with TP1 (2 GPUs total, 1 GPU each).&lt;/li&gt;
&lt;li&gt;2x Decode workers with TP1 (2 GPUs total, 1 GPU each).&lt;/li&gt;
&lt;li&gt;Achieves balanced prefill and decode capacity with minimal tensor parallelism overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tensor Parallelism Considerations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Worker Type&lt;/th&gt;
&lt;th&gt;Recommended TP&lt;/th&gt;
&lt;th&gt;Reasoning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prefill&lt;/td&gt;
&lt;td&gt;TP1 or TP2&lt;/td&gt;
&lt;td&gt;Minimizes all-reduce communication overhead, prefill is memory-bound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decode&lt;/td&gt;
&lt;td&gt;TP4 or TP8&lt;/td&gt;
&lt;td&gt;Distributes compute load, decode is compute-bound&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Batch Size Tuning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prefill Workers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use low batch size (1-4) for optimal TTFT.&lt;/li&gt;
&lt;li&gt;Higher batch sizes increase memory pressure and TTFT latency.&lt;/li&gt;
&lt;li&gt;Prefill throughput comes from multiple workers, not high batch size.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decode Workers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use high batch size (256-1024) for optimal throughput.&lt;/li&gt;
&lt;li&gt;Decode operations are compute-bound and benefit from high concurrency.&lt;/li&gt;
&lt;li&gt;Adjust based on available GPU memory and model KV cache requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  RDMA Requirements
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Network Requirements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;InfiniBand (preferred) or RoCE network with RDMA support.&lt;/li&gt;
&lt;li&gt;Sufficient bandwidth: 200 Gbps+ recommended for optimal performance.&lt;/li&gt;
&lt;li&gt;Low latency: &amp;lt;10μs for RDMA operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Verification:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check worker logs for "NIXL is available" and "Backend UCX was instantiated" messages.&lt;/li&gt;
&lt;li&gt;Monitor RDMA device utilization: &lt;code&gt;rdma resource show&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Measure RDMA bandwidth: &lt;code&gt;ib_write_bw&lt;/code&gt; or &lt;code&gt;rdma_bw&lt;/code&gt; tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Re-run AIConfigurator against a 32B+ model to see disaggregated serving's throughput advantage materialize.&lt;/li&gt;
&lt;li&gt;Scale prefill and decode replicas independently based on observed ISL/OSL ratios in production traffic.&lt;/li&gt;
&lt;li&gt;Run AIPerf with the full recommended concurrency (208) and request count (8320) for a production-grade validation.&lt;/li&gt;
&lt;li&gt;Review the backend-specific deployment guides (vLLM, TensorRT-LLM, SGLang) if you need a standalone, non-Kubernetes setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-build-disaggregated-inference-with-nvidia-dynamo" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>kubernetes</category>
      <category>gpu</category>
    </item>
    <item>
      <title>Creating MCP Server Using FastMCP in Python</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:17:48 +0000</pubDate>
      <link>https://dev.to/vultr/creating-mcp-server-using-fastmcp-in-python-3gg2</link>
      <guid>https://dev.to/vultr/creating-mcp-server-using-fastmcp-in-python-3gg2</guid>
      <description>&lt;p&gt;The Model Context Protocol (MCP) is an emerging standard for structuring and managing contextual information provided to Large Language Models (LLMs), enabling them to operate more intelligently and consistently across sessions, tools, and multi-agent systems. Rather than relying solely on raw prompts, MCP introduces a structured format that includes user identity, task goals, memory state, available tools, and environmental data — allowing models to behave more like persistent, context-aware agents. This guide walks through running a sample MCP server built with FastMCP, backed by an SQLite database, and querying it using natural language through an MCP client. By the end, you'll have a working FastMCP server that an MCP client can query against a local SQLite database using plain language.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Get the Server Code Running
&lt;/h2&gt;

&lt;p&gt;The code for the MCP server and the sample SQLite database is available in &lt;a href="https://github.com/vultr-marketing/code-samples/tree/main" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt;. To set up the server locally, follow the steps below in your system terminal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Clone the GitHub repository:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone https://github.com/vultr-marketing/code-samples.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Navigate into the &lt;code&gt;mcp_sqlite&lt;/code&gt; directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;code-samples/mcp/mcp_sqlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Create a &lt;code&gt;.env&lt;/code&gt; file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nano .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Add the following variables to the file, then save and close it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;DB_PATH&lt;/span&gt;=&lt;span class="n"&gt;sample&lt;/span&gt;.&lt;span class="n"&gt;db&lt;/span&gt;
&lt;span class="n"&gt;MCP_PORT&lt;/span&gt;=&lt;span class="m"&gt;8080&lt;/span&gt;
&lt;span class="n"&gt;READ_ONLY&lt;/span&gt;=&lt;span class="n"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DB_PATH&lt;/code&gt;: Path to your SQLite database file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MCP_PORT&lt;/code&gt;: Port used by the MCP server (not used in stdio mode, but good to define).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;READ_ONLY&lt;/code&gt;: Set to &lt;code&gt;true&lt;/code&gt; to block insert/update/delete queries for safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Install dependencies:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Understand the Sample MCP Server
&lt;/h2&gt;

&lt;p&gt;The server script launches a Model Context Protocol (MCP) server using &lt;code&gt;FastMCP&lt;/code&gt; and connects it to a local SQLite database. It enables structured interaction with the database using natural language, typically via an LLM interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment-Based Configuration&lt;/strong&gt;: Uses &lt;code&gt;.env&lt;/code&gt; for settings like &lt;code&gt;DB_PATH&lt;/code&gt;, &lt;code&gt;MCP_PORT&lt;/code&gt;, and &lt;code&gt;READ_ONLY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async Database Access&lt;/strong&gt;: Handles SQLite operations asynchronously with &lt;code&gt;aiosqlite&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sample DB Initialization&lt;/strong&gt;: Creates sample tables if the database doesn't exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Validation&lt;/strong&gt;: Prevents dangerous SQL operations, especially when &lt;code&gt;READ_ONLY&lt;/code&gt; is enabled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured Logging&lt;/strong&gt;: Provides detailed startup and runtime logs for debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Transport Layer: stdio
&lt;/h3&gt;

&lt;p&gt;The server uses the &lt;code&gt;stdio&lt;/code&gt; transport, allowing it to communicate over standard input and output. This is especially useful when integrating with desktop agents like Claude Desktop, enabling lightweight inter-process communication without a web server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Available Tools in the Sample MCP Server
&lt;/h3&gt;

&lt;p&gt;Tools are structured, callable functions that a language model can use to interact with external systems like databases, APIs, or services. Each tool has a defined input schema and a predictable output format, enabling the model to perform specific operations safely and effectively — like querying data, listing tables, or modifying records.&lt;/p&gt;

&lt;p&gt;Tools extend the capabilities of LLMs beyond simple text generation, allowing them to reason over structured data, fetch real-time information, or take actions based on user intent.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;execute_query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes a validated SQL &lt;code&gt;SELECT&lt;/code&gt; query and returns the result set.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_tables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists all user-defined tables in the SQLite database.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;describe_table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the schema (columns, types, constraints) of a specified table.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;count_rows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Counts and returns the number of rows in a specified table.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;insert_sample_data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inserts predefined sample data into tables (only if not read-only).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  3. Use an MCP Client to Interact
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://claude.ai/download" rel="noopener noreferrer"&gt;Download the Claude Desktop application&lt;/a&gt; — it automatically detects MCP servers defined in its configuration file and makes them accessible for use within any conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Start the Claude Desktop application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Locate the settings option in the application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Select Developer settings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Click Edit Config.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Add the following configuration snippet to your Claude Desktop config file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sqlite"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;path to uv executable&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--directory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;path to directory containing server.py&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"server.py"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Once the new configuration is in place, restart the Claude Desktop application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. To confirm the server is running, go to the Developer settings in Claude Desktop and check if the status next to the JSON file shows as running.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Make sure the MCP server tools are available to the application.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask Questions and Perform Operations
&lt;/h3&gt;

&lt;p&gt;You can run interactive queries by chatting naturally. Using the available tools, the LLM translates your requests into SQL queries, executes them on the sample database, and responds in plain language. The LLM also displays the database queries it is executing and the tools it is using to retrieve the data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: the data in the sample database is for demonstration purposes only and may differ from actual data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Point &lt;code&gt;DB_PATH&lt;/code&gt; at a real SQLite database and extend the tool list beyond &lt;code&gt;execute_query&lt;/code&gt;, &lt;code&gt;list_tables&lt;/code&gt;, &lt;code&gt;describe_table&lt;/code&gt;, and &lt;code&gt;count_rows&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Swap the &lt;code&gt;stdio&lt;/code&gt; transport for an HTTP-based transport if you need the server reachable from a remote MCP client.&lt;/li&gt;
&lt;li&gt;Add authentication and request logging before exposing an MCP server with &lt;code&gt;READ_ONLY=false&lt;/code&gt; beyond your local machine.&lt;/li&gt;
&lt;li&gt;Try connecting the same server to other MCP-compatible clients, not just Claude Desktop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-create-mcp-server-using-fastmcp-in-python" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>mcp</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Docker Image</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:17:22 +0000</pubDate>
      <link>https://dev.to/vultr/building-a-docker-image-1hl8</link>
      <guid>https://dev.to/vultr/building-a-docker-image-1hl8</guid>
      <description>&lt;p&gt;Docker is an open-source containerization platform that enables developers to package applications and all their dependencies into lightweight, portable containers that run consistently across different environments. Docker images are the blueprint for containers — they package your application code, dependencies, environment variables, and runtime configurations into a portable, reproducible format. This guide explains how to build Docker images efficiently and create an optimized Dockerfile. By the end, you'll have a tested, size-optimized Docker image built with &lt;code&gt;docker buildx&lt;/code&gt; and ready to push to a registry.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Short Answer
&lt;/h2&gt;

&lt;p&gt;If you're already familiar with Docker and just need a quick reference, use the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Build a Docker image from a Dockerfile &lt;span class="k"&gt;in &lt;/span&gt;the current directory
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker buildx build &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;image-name&amp;gt;:&amp;lt;tag&amp;gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Run a container from the image and map a port
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker container run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;host-port&amp;gt;:&amp;lt;container-port&amp;gt; &amp;lt;image-name&amp;gt;:&amp;lt;tag&amp;gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Tag the image &lt;span class="k"&gt;for &lt;/span&gt;a container registry
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker image tag &amp;lt;image-name&amp;gt;:&amp;lt;tag&amp;gt; &amp;lt;registry&amp;gt;/&amp;lt;image-name&amp;gt;:&amp;lt;tag&amp;gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Push your image to a registry &lt;span class="o"&gt;(&lt;/span&gt;e.g., Docker Hub or another container registry&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker image push &amp;lt;registry&amp;gt;/&amp;lt;image-name&amp;gt;:&amp;lt;tag&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Continue reading for detailed explanations and examples of each command.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create a Dockerfile
&lt;/h2&gt;

&lt;p&gt;To build a Docker image, start by creating a &lt;code&gt;Dockerfile&lt;/code&gt;. This file defines the environment, dependencies, and commands needed to build your application into a container image. Follow the steps below to create a &lt;code&gt;Dockerfile&lt;/code&gt; for a Node.js web application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a file named &lt;code&gt;Dockerfile&lt;/code&gt; in your project's root directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;vim Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Add the following content to the file, then save and exit:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use Node.js as the base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:22-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy dependency files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy application source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Expose application port&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# Define the startup command&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breakdown of the Dockerfile
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FROM node:22-alpine&lt;/strong&gt;: Defines &lt;code&gt;node:22-alpine&lt;/code&gt; as the base image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WORKDIR /app&lt;/strong&gt;: Sets &lt;code&gt;/app&lt;/code&gt; as the working directory inside the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COPY package*.json ./&lt;/strong&gt;: Copies dependency definition files to the working directory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RUN npm install&lt;/strong&gt;: Installs project dependencies using npm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COPY . .&lt;/strong&gt;: Copies the entire project directory into the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EXPOSE 3000&lt;/strong&gt;: Declares port 3000 as the container's exposed port.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CMD ["node", "app.js"]&lt;/strong&gt;: Defines the startup command to run the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above &lt;code&gt;Dockerfile&lt;/code&gt; follows several best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses a specific version of the base image.&lt;/li&gt;
&lt;li&gt;Copies dependency files first to take advantage of Docker's build cache.&lt;/li&gt;
&lt;li&gt;Uses a minimal Alpine-based image to reduce image size.&lt;/li&gt;
&lt;li&gt;Defines the default command to run the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Build the Docker Image
&lt;/h2&gt;

&lt;p&gt;With your &lt;code&gt;Dockerfile&lt;/code&gt; ready, use the &lt;code&gt;docker buildx build&lt;/code&gt; command to create your image. Docker Buildx enables advanced build features such as multi-platform support, build caching, and pushing to registries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Basic build with a tag:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker buildx build &lt;span class="nt"&gt;-t&lt;/span&gt; my-app:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Build with multiple tags and build arguments:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker buildx build &lt;span class="nt"&gt;-t&lt;/span&gt; my-app:1.0 &lt;span class="nt"&gt;-t&lt;/span&gt; my-app:latest &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. For a multi-platform build and push to a registry, first create and bootstrap a builder (one-time setup):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker buildx create &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --name container-builder \
  --driver docker-container \
  --bootstrap --use
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. After the &lt;code&gt;container-builder&lt;/code&gt; is running, run the multi-platform build:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker buildx build &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --platform linux/amd64,linux/arm64 \
  -t my-app:latest \
  --push .
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command Breakdown
&lt;/h3&gt;

&lt;p&gt;Explanation of key options used with &lt;code&gt;docker buildx build&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker buildx build&lt;/code&gt;: Uses Docker BuildKit's extended build command for advanced features like multi-platform builds and better caching.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-t my-app:latest&lt;/code&gt;: Tags the image with a name (&lt;code&gt;my-app&lt;/code&gt;) and version (&lt;code&gt;latest&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-t my-app:1.0&lt;/code&gt;: Adds another tag (&lt;code&gt;1.0&lt;/code&gt;) to the image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--build-arg ENV=production&lt;/code&gt;: Passes build-time variable &lt;code&gt;ENV&lt;/code&gt; with the value &lt;code&gt;production&lt;/code&gt; into the Dockerfile.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--platform linux/amd64,linux/arm64&lt;/code&gt;: Builds images for multiple CPU architectures (for example, x86_64 and ARM64).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--push&lt;/code&gt;: Pushes the built image and manifest list to the specified container registry.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--no-cache&lt;/code&gt;: Forces the build to skip caching and build each layer from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--pull&lt;/code&gt;: Always pulls the latest version of the base image instead of using the cached one.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-f Dockerfile.prod&lt;/code&gt;: Specifies a custom Dockerfile (&lt;code&gt;Dockerfile.prod&lt;/code&gt;) instead of the default Dockerfile.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt;: Sets the build context to the present directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example Scenario
&lt;/h3&gt;

&lt;p&gt;Follow this example to build and tag a Docker image for a Node.js application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Clone an example Docker Node.js application repository:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone https://github.com/docker/docker-nodejs-sample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Navigate to the cloned project directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;docker-nodejs-sample
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Create a &lt;code&gt;Dockerfile&lt;/code&gt; inside the project root directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;vim Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Add the following content to the file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:22-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "src/index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Build the Docker image:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker buildx build &lt;span class="nt"&gt;-t&lt;/span&gt; my-app:latest &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. View the newly built Docker image:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker image &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command lists all the Docker images available on your system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the Docker Build Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker processes the &lt;code&gt;Dockerfile&lt;/code&gt; in a layered fashion.&lt;/li&gt;
&lt;li&gt;Starts with the base image (&lt;code&gt;node:22-alpine&lt;/code&gt; in this case).&lt;/li&gt;
&lt;li&gt;Creates a temporary container for each instruction (for example, &lt;code&gt;COPY&lt;/code&gt;, &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Executes the instruction inside the container and commits the result as a new image layer.&lt;/li&gt;
&lt;li&gt;Removes the temporary container.&lt;/li&gt;
&lt;li&gt;Repeats the process for each instruction in the Dockerfile.&lt;/li&gt;
&lt;li&gt;Tags the final image with the names and versions you specified.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Test the Docker Image
&lt;/h2&gt;

&lt;p&gt;After building your Docker image, test it by running a container and verifying that your application behaves as intended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Run a container from your image:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker container run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="nt"&gt;--name&lt;/span&gt; check-list-app my-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker container run&lt;/code&gt;: Starts a container from an image.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt;: Runs the container in detached mode (in the background).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 3000:3000&lt;/code&gt;: Maps port 3000 of the host to port 3000 of the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name check-list-app&lt;/code&gt;: Assigns a custom name to the container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;my-app:latest&lt;/code&gt;: Specifies the image and tag to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Verify the container is running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker container &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command lists all running containers. After confirming your container is listed and running, you can access your application to ensure it behaves as expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Check the application logs for debugging purposes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker container logs check-list-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command displays the container's logs, allowing you to verify application startup messages or troubleshoot any issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Stop and remove the container:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker container stop check-list-app
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker container &lt;span class="nb"&gt;rm &lt;/span&gt;check-list-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above commands stop the running container and then remove it entirely from your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Optimize Docker Image Size
&lt;/h2&gt;

&lt;p&gt;Optimizing Docker image size leads to faster deployments, lower storage costs, and improved security. Follow these best practices to create smaller, more efficient Docker images.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Multi-Stage Builds
&lt;/h3&gt;

&lt;p&gt;Multi-stage builds let you create lean final images by separating build and runtime environments. Each &lt;code&gt;FROM&lt;/code&gt; starts a new build stage, allowing you to copy only necessary artifacts into the final image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.17&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-installsuffix&lt;/span&gt; cgo &lt;span class="nt"&gt;-o&lt;/span&gt; app .

&lt;span class="c"&gt;# Final stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk &lt;span class="nt"&gt;--no-cache&lt;/span&gt; add ca-certificates
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /root/&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/app .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final image includes only the compiled binary and runtime dependencies, excluding source code and build tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose Smaller Base Images
&lt;/h3&gt;

&lt;p&gt;Using smaller base images helps reduce Docker image size, improve security, and speed up build and deployment times. Choose a base image that includes only the essential components needed to run your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Builder stage: install dependencies&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.9-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;--target&lt;/span&gt; /app/packages

&lt;span class="c"&gt;# Final image: minimal runtime using distroless&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/python3&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/packages /packages&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONPATH=/packages&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py /&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;alpine&lt;/code&gt; or &lt;code&gt;slim&lt;/code&gt; based images as a minimal base for building dependencies.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;distroless&lt;/code&gt; for a secure, production-ready runtime image.&lt;/li&gt;
&lt;li&gt;Consider scratch images for statically compiled binaries in languages like Go or Rust.&lt;/li&gt;
&lt;li&gt;Apply multi-stage builds to separate build and runtime environments.&lt;/li&gt;
&lt;li&gt;Exclude package managers and shells from the final image.&lt;/li&gt;
&lt;li&gt;Set environment variables like &lt;code&gt;PYTHONPATH&lt;/code&gt; to locate dependencies at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reduce Layer Count and Image Size
&lt;/h3&gt;

&lt;p&gt;Each instruction in a Dockerfile creates a new layer. Fewer, optimized layers reduce image size and speed up builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of an inefficient Dockerfile (creates many layers):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bad practice - many layers&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:20.04&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3-pip
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;flask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above Dockerfile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each &lt;code&gt;RUN&lt;/code&gt; instruction creates a new layer.&lt;/li&gt;
&lt;li&gt;No cleanup of APT cache or temporary files.&lt;/li&gt;
&lt;li&gt;Slower builds and larger images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example of an optimized Dockerfile (fewer layers and smaller image):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Better practice - fewer layers&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:20.04&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3 python3-pip &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;flask &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt; apt-get clean &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above Dockerfile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Related commands are combined using &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; to reduce layers.&lt;/li&gt;
&lt;li&gt;Package caches and temporary files are cleaned up during installation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use a &lt;code&gt;.dockerignore&lt;/code&gt; File
&lt;/h3&gt;

&lt;p&gt;Exclude unnecessary files and directories from the Docker build context to reduce image size and speed up build times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;node_modules&lt;/span&gt;
&lt;span class="err"&gt;npm-debug.log&lt;/span&gt;
&lt;span class="err"&gt;.git&lt;/span&gt;
&lt;span class="err"&gt;.gitignore&lt;/span&gt;
&lt;span class="err"&gt;Dockerfile&lt;/span&gt;
&lt;span class="err"&gt;README.md&lt;/span&gt;
&lt;span class="err"&gt;.env&lt;/span&gt;
&lt;span class="err"&gt;*.pyc&lt;/span&gt;
&lt;span class="err"&gt;__pycache__&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above &lt;code&gt;.dockerignore&lt;/code&gt; file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents unneeded files from being copied into the image.&lt;/li&gt;
&lt;li&gt;Keeps the image clean and lightweight.&lt;/li&gt;
&lt;li&gt;Speeds up build time by minimizing the context sent to the Docker daemon.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Leverage Docker Build Cache
&lt;/h3&gt;

&lt;p&gt;Docker caches image layers to avoid rebuilding unchanged steps. Structure your Dockerfile to take full advantage of this.&lt;/p&gt;

&lt;p&gt;Best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Place less frequently changing instructions at the top of your Dockerfile.&lt;/li&gt;
&lt;li&gt;Copy dependency files first, install packages, then copy the application source last.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example of an efficient caching structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:16-alpine&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy dependency files first&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy the source code last&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above Dockerfile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependency layers are reused unless &lt;code&gt;package*.json&lt;/code&gt; changes.&lt;/li&gt;
&lt;li&gt;Source code changes won't invalidate the cached dependency layers.&lt;/li&gt;
&lt;li&gt;Results in faster builds and smaller diffs in CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wire &lt;code&gt;docker buildx build --push&lt;/code&gt; into a CI/CD pipeline so images rebuild and publish automatically on every commit.&lt;/li&gt;
&lt;li&gt;Scan your images for vulnerabilities with a tool like Trivy or Docker Scout before pushing to production.&lt;/li&gt;
&lt;li&gt;Add a multi-stage build to any existing Dockerfile that still ships build tools in the final image.&lt;/li&gt;
&lt;li&gt;Set up &lt;code&gt;docker buildx&lt;/code&gt; multi-platform builds if you need to support both x86_64 and ARM64 deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-build-a-docker-image" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Llama.cpp Container Image</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Thu, 22 Oct 2026 23:16:25 +0000</pubDate>
      <link>https://dev.to/vultr/building-a-llamacpp-container-image-48g9</link>
      <guid>https://dev.to/vultr/building-a-llamacpp-container-image-48g9</guid>
      <description>&lt;p&gt;Llama.cpp is a high-performance inference platform designed for Large Language Models (LLMs) like Llama, Falcon, and Mistral. It provides a streamlined development environment compatible with both CPU and GPU systems. This guide explains how to build Llama.cpp into a Docker container image for CPU and GPU systems and push the finished image to a container registry. By the end, you'll have CPU and GPU container images of Llama.cpp built, tagged, and pushed to a container registry.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A server with Docker installed (a GPU-enabled server with NVIDIA drivers and the NVIDIA Container Toolkit for the GPU build).&lt;/li&gt;
&lt;li&gt;Access to the server using SSH.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start the Docker service.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the non-root user to the Docker group. For example, &lt;code&gt;linuxuser&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker linuxuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Switch to the user:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;su - linuxuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Set Up the Project Directory
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Create a new directory to store your Llama.cpp project files:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;llama-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Switch to the directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;llama-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Clone the Llama.cpp project repository using Git:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone https://github.com/ggerganov/llama.cpp/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. List files and verify that a new &lt;code&gt;llama.cpp&lt;/code&gt; directory is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;llama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Switch to the &lt;code&gt;llama.cpp&lt;/code&gt; project directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;llama.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. List all hidden files and verify that a new &lt;code&gt;.devops&lt;/code&gt; directory is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;..                 cmake           convert.py                     flake.lock     ggml-common.h        ggml-metal.h      ggml-quantsh     .git                         llama.cpp        
CMakeLists.txt  .devops    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.devops&lt;/code&gt; directory contains the following Dockerfile resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main.Dockerfile&lt;/code&gt;: Contains the build context for CPU systems.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main-cuda.Dockerfile&lt;/code&gt;: Contains the build context for GPU systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use these resources in the next sections to build a CPU or GPU system container image.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Build a Llama.cpp Container Image for CPU Systems
&lt;/h2&gt;

&lt;p&gt;Follow the steps below to build a new Llama.cpp container image using &lt;code&gt;main.Dockerfile&lt;/code&gt;, which contains the required build context and installs all necessary dependency packages for CPU-based systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Copy the &lt;code&gt;main.Dockerfile&lt;/code&gt; from the &lt;code&gt;.devops&lt;/code&gt; directory to the main &lt;code&gt;llama.cpp&lt;/code&gt; project directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .devops/main.Dockerfile &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Build a new container image using &lt;code&gt;main.Dockerfile&lt;/code&gt; with all files in the project directory. Replace &lt;code&gt;llama-image&lt;/code&gt; with your desired image name:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker build &lt;span class="nt"&gt;-f&lt;/span&gt; main.Dockerfile &lt;span class="nt"&gt;-t&lt;/span&gt; llama-image &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. View all Docker images on the server and verify that a new &lt;code&gt;llama-image&lt;/code&gt; is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY                                  TAG       IMAGE ID       CREATED              SIZE
llama-image                                 latest    f4fcb571a2f7   About a minute ago   80.1MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Build a Llama.cpp Container Image for GPU Systems
&lt;/h2&gt;

&lt;p&gt;The Llama.cpp &lt;code&gt;main-cuda.Dockerfile&lt;/code&gt; resource contains the build context for NVIDIA GPU systems that run the latest CUDA driver packages. Follow the steps below to build a Llama container image compatible with GPU systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Copy &lt;code&gt;main-cuda.Dockerfile&lt;/code&gt; to the Llama.cpp project directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .devops/main-cuda.Dockerfile &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Build a new container image &lt;code&gt;llama-gpu-image&lt;/code&gt; using the &lt;code&gt;main-cuda.Dockerfile&lt;/code&gt; with all files in the working project directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker build &lt;span class="nt"&gt;-f&lt;/span&gt; main-cuda.Dockerfile &lt;span class="nt"&gt;-t&lt;/span&gt; llama-gpu-image &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. View all Docker images on the server and verify that the new &lt;code&gt;llama-gpu-image&lt;/code&gt; is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY                                  TAG       IMAGE ID       CREATED          SIZE
llama-gpu-image                             latest    40421f1469cf   2 minutes ago    2.08GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the Llama.cpp GPU container image, verify that your target host includes the minimum or higher CUDA version referenced by the &lt;code&gt;ARG CUDA_VERSION=&lt;/code&gt; directive within &lt;code&gt;main-cuda.Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Verify the target CUDA version:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;main-cuda.Dockerfile | &lt;span class="nb"&gt;grep &lt;/span&gt;&lt;span class="nv"&gt;CUDA_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; CUDA_VERSION=12.3.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Push the Llama.cpp Container Image to a Container Registry
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Open your container registry's management panel and copy the registry URL, username, and API key (or password) to use when accessing the registry.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Switch to your server terminal session and log in to your container registry. Replace &lt;code&gt;your-registry-url&lt;/code&gt;, &lt;code&gt;exampleregistry&lt;/code&gt;, &lt;code&gt;exampleuser&lt;/code&gt;, and &lt;code&gt;registry-password&lt;/code&gt; with your actual registry details:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker login https://your-registry-url/exampleregistry &lt;span class="nt"&gt;-u&lt;/span&gt; exampleuser &lt;span class="nt"&gt;-p&lt;/span&gt; registry-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Tag the Llama.cpp container image with your desired registry tag. For example, &lt;code&gt;your-registry-url/exampleregistry/llama-gpu-image&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker tag llama-gpu-image your-registry-url/exampleregistry/llama-gpu-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. View all Docker images on the server and verify that the new tagged image is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY                                           TAG       IMAGE ID       CREATED          SIZE
your-registry-url/exampleregistry/llama-gpu-image    latest    40421f1469cf   3 minutes ago    2.08GB
llama-gpu-image                                      latest    40421f1469cf   3 minutes ago    2.08GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Push the tagged image to your container registry repository:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker push your-registry-url/exampleregistry/llama-gpu-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Open your container registry's management panel and check the repositories list to verify that the new repository is available.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Automate the CPU and GPU builds in a CI/CD pipeline so new images are rebuilt whenever the Llama.cpp repository updates.&lt;/li&gt;
&lt;li&gt;Pull the GPU image onto a multi-GPU host and benchmark inference throughput across different quantization levels.&lt;/li&gt;
&lt;li&gt;Pin the &lt;code&gt;CUDA_VERSION&lt;/code&gt; in &lt;code&gt;main-cuda.Dockerfile&lt;/code&gt; to match the driver version installed on your target hosts.&lt;/li&gt;
&lt;li&gt;Explore Llama.cpp's server mode to expose an OpenAI-compatible API from the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-build-a-llama-cpp-container-image" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>llm</category>
      <category>ai</category>
      <category>gpu</category>
    </item>
    <item>
      <title>Configuring Smart Routing in NVIDIA Dynamo</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Wed, 14 Oct 2026 21:07:15 +0000</pubDate>
      <link>https://dev.to/vultr/configuring-smart-routing-in-nvidia-dynamo-e25</link>
      <guid>https://dev.to/vultr/configuring-smart-routing-in-nvidia-dynamo-e25</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.nvidia.com/dynamo/getting-started/quickstart" rel="noopener noreferrer"&gt;NVIDIA Dynamo&lt;/a&gt; is an open-source, high-throughput, low-latency inference framework designed to deploy large-scale generative AI and reasoning models across multi-node, multi-GPU environments. Its KV Router intelligently directs inference requests by evaluating computational costs across workers, considering both prefill and decode phases. By maximizing KV cache reuse and minimizing redundant computation, smart routing reduces time-to-first-token (TTFT) by up to 40% and increases overall throughput by 25-30% compared to round-robin load balancing. This guide covers infrastructure setup, router enablement for both aggregated and disaggregated serving patterns, parameter tuning, and monitoring for production deployments. By the end, you'll have KV-aware smart routing configured and verified across both serving patterns, with the parameters tuned for your workload.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, ensure you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have access to a GPU-enabled server with NVIDIA GPUs installed and the NVIDIA Container Toolkit configured, using a non-root user with sudo privileges.

&lt;ul&gt;
&lt;li&gt;2 GPUs for aggregated serving with smart routing.&lt;/li&gt;
&lt;li&gt;4 GPUs for disaggregated serving with smart routing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;Docker Engine&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://huggingface.co/join" rel="noopener noreferrer"&gt;Hugging Face account&lt;/a&gt; and generate an &lt;a href="https://huggingface.co/settings/tokens" rel="noopener noreferrer"&gt;access token&lt;/a&gt; for gated models like Llama.&lt;/li&gt;
&lt;li&gt;Understand basic LLM inference concepts (prefill vs decode phases, KV cache).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding KV-Aware Routing
&lt;/h2&gt;

&lt;p&gt;Traditional load balancing strategies like round-robin or random routing distribute requests evenly across workers without considering their cached state. KV-aware routing optimizes performance by directing requests to workers that already have relevant KV cache blocks, reducing redundant computation during the prefill phase.&lt;/p&gt;

&lt;h3&gt;
  
  
  How KV Cache Routing Works
&lt;/h3&gt;

&lt;p&gt;The KV Router makes routing decisions based on a cost function that balances two factors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Prefill Cost&lt;/strong&gt; — the router calculates prefill cost by determining how many tokens need processing from scratch. When a worker has cached KV blocks matching the request's input prefix, those blocks can be reused, reducing prefill computation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prefill blocks = (Total input tokens - Cached tokens) / Block size
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Decode Cost&lt;/strong&gt; — the router tracks active blocks on each worker to estimate decode capacity. Workers with fewer active sequences have more capacity for new requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Combined Cost Formula:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cost = overlap_score_weight × prefill_blocks + decode_blocks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;overlap_score_weight&lt;/code&gt; balances TTFT optimization (higher values) vs load distribution (lower values).&lt;/li&gt;
&lt;li&gt;Lower cost indicates a better routing choice.&lt;/li&gt;
&lt;li&gt;Default &lt;code&gt;overlap_score_weight&lt;/code&gt; is 1.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Worker Selection Process
&lt;/h3&gt;

&lt;p&gt;The router evaluates all available workers and selects the one with the lowest cost. Consider this example with 3 workers and &lt;code&gt;overlap_score_weight = 1.0&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F06xpcw9923a1b14ozd79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F06xpcw9923a1b14ozd79.png" alt="KV Cache Router Worker Selection Process" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Worker&lt;/th&gt;
&lt;th&gt;Cached Blocks&lt;/th&gt;
&lt;th&gt;Prefill Blocks&lt;/th&gt;
&lt;th&gt;Decode Blocks&lt;/th&gt;
&lt;th&gt;Total Cost&lt;/th&gt;
&lt;th&gt;Selected&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Worker 1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;18 (1.0 × 8 + 10)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker 2&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;10&lt;/strong&gt; (1.0 × 5 + 5)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker 3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;11 (1.0 × 2 + 9)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Worker 2 provides the best balance of cache reuse and available capacity, minimizing both prefill computation and decode load.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Benefits
&lt;/h3&gt;

&lt;p&gt;KV-aware routing provides measurable improvements over basic load balancing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TTFT Reduction&lt;/strong&gt;: 30-40% lower time-to-first-token by maximizing cache hits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput Increase&lt;/strong&gt;: 25-30% higher tokens/second by reducing redundant prefill computation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better GPU Utilization&lt;/strong&gt;: More balanced load distribution across workers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Efficiency&lt;/strong&gt;: Higher cache hit rates through intelligent request placement.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These benefits are most significant for workloads with long input prompts (&amp;gt;2000 tokens) where prefill cost dominates, similar or repetitive input prefixes across requests, and multiple inference workers with independent KV caches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clone the Dynamo Repository
&lt;/h2&gt;

&lt;p&gt;NVIDIA Dynamo provides deployment scripts, launch utilities, and runtime modules for inference workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Clone the repository:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone https://github.com/ai-dynamo/dynamo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Navigate to the repository directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;dynamo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Switch to the latest stable release:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git checkout release/0.9.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit the &lt;a href="https://github.com/ai-dynamo/dynamo/releases/" rel="noopener noreferrer"&gt;Dynamo releases page&lt;/a&gt; to find the latest stable version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify CUDA Version and Pull the Container Image
&lt;/h2&gt;

&lt;p&gt;The vLLM container requires a CUDA version match between the host driver and container runtime to prevent GPU kernel incompatibilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Check the installed CUDA version:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nvidia-smi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output displays the CUDA version in the top-right corner of the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05              Driver Version: 580.95.05      CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Pull the vLLM container image from NGC, matching the image tag to your system's CUDA version:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For CUDA 13.x:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker pull nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0-cuda13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For CUDA 12.x:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker pull nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Visit the &lt;a href="https://catalog.ngc.nvidia.com/orgs/nvidia/teams/ai-dynamo/collections/ai-dynamo/artifacts" rel="noopener noreferrer"&gt;NVIDIA NGC Catalog&lt;/a&gt; to view all available image tags and CUDA versions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. (Optional) Build the container from source instead of pulling the pre-built image:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./container/build.sh &lt;span class="nt"&gt;--framework&lt;/span&gt; VLLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates an image named &lt;code&gt;dynamo:latest-vllm&lt;/code&gt;. If you use this locally built image, replace &lt;code&gt;nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0-cuda13&lt;/code&gt; with &lt;code&gt;dynamo:latest-vllm&lt;/code&gt; in all subsequent commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Hugging Face Cache Permissions
&lt;/h2&gt;

&lt;p&gt;The container runs as UID 1000 and requires write access to the Hugging Face cache directory for model downloads. Incorrect permissions prevent the container from accessing cached model weights, causing worker initialization failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create the cache directory if it does not exist:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; container/.cache/huggingface
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Set ownership to the container user (UID 1000):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:1000 container/.cache/huggingface
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Set appropriate permissions:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 775 container/.cache/huggingface
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start Infrastructure Services
&lt;/h2&gt;

&lt;p&gt;Dynamo's KV Router requires etcd for worker discovery and NATS for KV cache event propagation. The etcd service maintains a registry of active workers and their capabilities, while NATS handles real-time KV block events (creation and eviction) that let the router track cached state across workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Start the infrastructure services using Docker Compose:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; deploy/docker-compose.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Verify the services are running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; deploy/docker-compose.yml ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output displays the running etcd and NATS containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                   IMAGE                      COMMAND                  SERVICE       CREATED          STATUS          PORTS
deploy-etcd-server-1   bitnamilegacy/etcd:3.6.1   "/opt/bitnami/script…"   etcd-server   18 seconds ago   Up 18 seconds   0.0.0.0:2379-2380-&amp;gt;2379-2380/tcp, [::]:2379-2380-&amp;gt;2379-2380/tcp
deploy-nats-server-1   nats:2.11.4                "/nats-server -c /et…"   nats-server   18 seconds ago   Up 18 seconds   0.0.0.0:4222-&amp;gt;4222/tcp, [::]:4222-&amp;gt;4222/tcp, 0.0.0.0:6222-&amp;gt;6222/tcp, [::]:6222-&amp;gt;6222/tcp, 0.0.0.0:8222-&amp;gt;8222/tcp, [::]:8222-&amp;gt;8222/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  KV Router Configuration Parameters
&lt;/h2&gt;

&lt;p&gt;The KV Router uses environment variables to control routing behavior, cache tracking, and worker selection logic. These parameters determine how the router balances Time To First Token (TTFT) optimization through cache reuse against Inter-Token Latency (ITL) through load distribution. All parameters can be set via environment variables or command-line arguments on the Dynamo frontend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Router Parameters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Recommended&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_ROUTER_MODE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables KV cache-aware routing strategy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;round_robin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set to &lt;code&gt;kv&lt;/code&gt; to enable cache-aware routing. Other modes: &lt;code&gt;round_robin&lt;/code&gt;, &lt;code&gt;random&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_ROUTER_TEMPERATURE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Controls routing randomness via softmax sampling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0.0&lt;/code&gt; = deterministic (always lowest cost). Higher values add randomness to prevent saturation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_KV_OVERLAP_SCORE_WEIGHT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Weight for prefix cache overlaps in cost calculation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Formula: &lt;code&gt;cost = weight × prefill_blocks + decode_blocks&lt;/code&gt;. Higher values prioritize TTFT over ITL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_KV_CACHE_BLOCK_SIZE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;KV cache block size in tokens (must match backend)&lt;/td&gt;
&lt;td&gt;Backend-specific&lt;/td&gt;
&lt;td&gt;&lt;code&gt;16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must match vLLM's block size. Used for prefill block calculation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_KV_EVENTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables real-time KV event tracking from workers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When disabled, router predicts cache state with TTL-based expiration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Worker Capacity Thresholds
&lt;/h3&gt;

&lt;p&gt;These parameters define when a worker is considered "busy" and should be excluded from routing decisions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Recommended&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_ACTIVE_DECODE_BLOCKS_THRESHOLD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Busy threshold based on KV cache block utilization&lt;/td&gt;
&lt;td&gt;Not set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0.85&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value between 0.0-1.0. Workers exceeding this percentage are excluded. Can update via &lt;code&gt;/busy_threshold&lt;/code&gt; API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_ACTIVE_PREFILL_TOKENS_THRESHOLD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Busy threshold based on prefill token count&lt;/td&gt;
&lt;td&gt;Not set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Absolute token count. Workers exceeding this are excluded. Uses OR logic with threshold fraction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Infrastructure Configuration
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ETCD_ENDPOINTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;etcd server URL for worker discovery&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Format: &lt;code&gt;http://localhost:2379&lt;/code&gt;. Multiple endpoints separated by comma&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NATS_SERVER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;NATS server URL for KV event propagation&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Format: &lt;code&gt;nats://localhost:4222&lt;/code&gt;. Required for KV event streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_EVENT_PLANE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Event plane backend for KV cache events&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Set to &lt;code&gt;nats&lt;/code&gt; to enable NATS-based event publishing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PYTHONHASHSEED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deterministic hash seed for KV event IDs&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Set to &lt;code&gt;0&lt;/code&gt; for reproducible KV event ID generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DYN_SYSTEM_PORT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Port for worker system status server&lt;/td&gt;
&lt;td&gt;System-dependent&lt;/td&gt;
&lt;td&gt;Used for health checks and metrics. Set per-worker to avoid conflicts (e.g., &lt;code&gt;8081&lt;/code&gt;, &lt;code&gt;8082&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Parameter Tuning Guidelines
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For TTFT Optimization (Low Latency):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increase &lt;code&gt;DYN_KV_OVERLAP_SCORE_WEIGHT&lt;/code&gt; to 1.5-2.0 (prioritize cache hits).&lt;/li&gt;
&lt;li&gt;Lower busy thresholds: &lt;code&gt;DYN_ACTIVE_DECODE_BLOCKS_THRESHOLD=0.70&lt;/code&gt;, &lt;code&gt;DYN_ACTIVE_PREFILL_TOKENS_THRESHOLD=500&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Keep &lt;code&gt;DYN_ROUTER_TEMPERATURE&lt;/code&gt; low (0.0-0.1) for deterministic selection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Throughput Optimization (High Load):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lower &lt;code&gt;DYN_KV_OVERLAP_SCORE_WEIGHT&lt;/code&gt; to 0.5-1.0 (balance cache hits with load).&lt;/li&gt;
&lt;li&gt;Higher busy thresholds: &lt;code&gt;DYN_ACTIVE_DECODE_BLOCKS_THRESHOLD=0.95&lt;/code&gt;, &lt;code&gt;DYN_ACTIVE_PREFILL_TOKENS_THRESHOLD=2000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Increase &lt;code&gt;DYN_ROUTER_TEMPERATURE&lt;/code&gt; to 0.3-0.5 for better load distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When KV Events Are Unavailable:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;DYN_KV_EVENTS=false&lt;/code&gt; to use prediction-based routing.&lt;/li&gt;
&lt;li&gt;Router tracks state via routing decisions with TTL-based expiration (default 120s).&lt;/li&gt;
&lt;li&gt;Less accurate than real-time events but works without backend KV event support.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set Up the Deployment Environment
&lt;/h2&gt;

&lt;p&gt;Both aggregated and disaggregated serving deployments use the same KV routing configuration. This section prepares the container environment and creates a shared configuration file for all deployment scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Export your Hugging Face token (replace &lt;code&gt;YOUR_HF_TOKEN&lt;/code&gt; with your actual token):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_HF_TOKEN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Run the vLLM container with GPU access and workspace mounting, using the image tag that matches your CUDA version:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./container/run.sh &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--framework&lt;/span&gt; VLLM &lt;span class="nt"&gt;--mount-workspace&lt;/span&gt; &lt;span class="nt"&gt;--image&lt;/span&gt; nvcr.io/nvidia/ai-dynamo/vllm-runtime:0.9.0-cuda13 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;HF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HF_TOKEN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starts an interactive container session with GPU support and passes the Hugging Face token to the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Inside the container, create the common environment configuration file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; ~/kv_routing.env
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;Infrastructure endpoints
&lt;/span&gt;&lt;span class="go"&gt;export ETCD_ENDPOINTS=http://localhost:2379
export NATS_SERVER=nats://localhost:4222
export DYN_EVENT_PLANE=nats

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Set deterministic &lt;span class="nb"&gt;hash &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;KV event IDs
&lt;span class="go"&gt;export PYTHONHASHSEED=0

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KV Router Configuration
&lt;span class="go"&gt;export DYN_ROUTER_MODE=kv
export DYN_ROUTER_TEMPERATURE=0.1
export DYN_KV_OVERLAP_SCORE_WEIGHT=1.5
export DYN_KV_CACHE_BLOCK_SIZE=16
export DYN_KV_EVENTS=true

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Optional: Advanced routing configuration
&lt;span class="go"&gt;export DYN_ACTIVE_DECODE_BLOCKS_THRESHOLD=0.85
export DYN_ACTIVE_PREFILL_TOKENS_THRESHOLD=10000

echo "Environment configuration loaded:"
&lt;/span&gt;&lt;span class="gp"&gt;echo "  etcd: $&lt;/span&gt;ETCD_ENDPOINTS&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="gp"&gt;echo "  NATS: $&lt;/span&gt;&lt;span class="s2"&gt;NATS_SERVER"&lt;/span&gt;
&lt;span class="gp"&gt;echo "  Router Mode: $&lt;/span&gt;DYN_ROUTER_MODE&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file contains shared configuration variables for all deployment scripts, including etcd endpoints, NATS server address, and KV routing parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure KV Routing for Aggregated Serving
&lt;/h2&gt;

&lt;p&gt;Aggregated serving combines prefill and decode phases on a single worker. KV-aware routing optimizes which worker receives each request based on cached KV blocks, improving performance even when workers handle both phases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Aggregated Serving with KV Routing
&lt;/h3&gt;

&lt;p&gt;The deployment script launches multiple vLLM workers with KV event publishing enabled and a frontend configured for KV-aware routing, handling process cleanup and providing routing configuration feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Inside the container, create the aggregated serving deployment script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; ~/nemotron_agg_kv_routing.sh
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;!/bin/bash
&lt;/span&gt;&lt;span class="go"&gt;source ~/kv_routing.env

&lt;/span&gt;&lt;span class="gp"&gt;MODEL="$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;1:-nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;pkill -f "dynamo.frontend";&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pkill &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"dynamo.vllm"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="go"&gt;set -e
trap 'kill 0' EXIT

python -m dynamo.frontend --router-reset-states &amp;amp;
sleep 10

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Worker 1 - GPU 0 &lt;span class="o"&gt;(&lt;/span&gt;ZMQ port 5557&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;DYN_SYSTEM_PORT=$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;DYN_SYSTEM_PORT1:-8081&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;CUDA_VISIBLE_DEVICES=0 python3 -m dynamo.vllm \
&lt;/span&gt;&lt;span class="gp"&gt;    --model "$&lt;/span&gt;MODEL&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="go"&gt;    --trust-remote-code \
    --connector none \
    --enforce-eager \
    --enable-prefix-caching \
    --kv-events-config '{"enable_kv_cache_events":true,"endpoint":"tcp://*:5557"}' &amp;amp;

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Worker 2 - GPU 1 &lt;span class="o"&gt;(&lt;/span&gt;ZMQ port 5558&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;DYN_SYSTEM_PORT=$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;DYN_SYSTEM_PORT2:-8082&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;CUDA_VISIBLE_DEVICES=1 python3 -m dynamo.vllm \
&lt;/span&gt;&lt;span class="gp"&gt;    --model "$&lt;/span&gt;MODEL&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="go"&gt;    --trust-remote-code \
    --connector none \
    --enforce-eager \
    --enable-prefix-caching \
    --kv-events-config '{"enable_kv_cache_events":true,"endpoint":"tcp://*:5558"}' &amp;amp;

sleep 30
&lt;/span&gt;&lt;span class="gp"&gt;echo "Aggregated serving ready at http://localhost:8000 (Model: $&lt;/span&gt;MODEL&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;wait
EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script configures aggregated serving with KV-aware routing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts the model name as a command-line argument (defaults to NVIDIA Nemotron Nano 4B).&lt;/li&gt;
&lt;li&gt;Sources the environment configuration created earlier (includes &lt;code&gt;DYN_EVENT_PLANE=nats&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;--connector none&lt;/code&gt; for aggregated serving (prefill+decode on same worker).&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;--enforce-eager&lt;/code&gt; for fast deployment without compilation.&lt;/li&gt;
&lt;li&gt;Enables prefix caching with &lt;code&gt;--enable-prefix-caching&lt;/code&gt; for KV cache reuse.&lt;/li&gt;
&lt;li&gt;Enables KV event publishing with ZMQ endpoints (ports 5557, 5558) on both workers.&lt;/li&gt;
&lt;li&gt;Configures system ports with &lt;code&gt;DYN_SYSTEM_PORT&lt;/code&gt; to avoid conflicts (8081, 8082).&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;--trust-remote-code&lt;/code&gt; to support NVIDIA Nemotron models.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;KV Event Flow Architecture:&lt;/strong&gt; vLLM natively publishes KV cache events via ZMQ. Dynamo's bridge component automatically translates these events to NATS for router consumption:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────┐
│ vLLM Worker 1       │
│ (ZMQ: tcp://*:5557) │───┐
└─────────────────────┘   │                                           ┌─────────────────────┐
                          │    ┌──────────────┐    ┌──────────────┐   │ KV Router           │
                          ├───&amp;gt;│ Dynamo Bridge├───&amp;gt;│ NATS Server  ├──&amp;gt;│ (Builds Radix Tree) │
                          │    └──────────────┘    └──────────────┘   │ Global Cache View   │
┌─────────────────────┐   │                                           └─────────────────────┘
│ vLLM Worker 2       │   │
│ (ZMQ: tcp://*:5558) │───┘
└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This architecture provides the router with a unified view of cache state across all workers while allowing vLLM to use its native ZMQ event publishing. You'll see ZMQ endpoints in worker logs, but events flow through the Dynamo bridge to NATS for router consumption.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;2. Make the script executable:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/nemotron_agg_kv_routing.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Run the aggregated serving script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/nemotron_agg_kv_routing.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script starts the frontend with KV routing enabled and launches two vLLM workers on GPUs 0 and 1. The frontend connects to NATS and begins consuming KV events from all registered workers, building a prefix (radix) tree to track cached blocks across workers.&lt;/p&gt;

&lt;p&gt;To deploy a different model, pass the model name as an argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/nemotron_agg_kv_routing.sh &lt;span class="s2"&gt;"nvidia/Llama-3_3-Nemotron-Super-49B-v1_5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify KV Routing
&lt;/h3&gt;

&lt;p&gt;The router logs provide insights into worker selection and cost calculations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Open a new terminal session and send a test request to the frontend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1",
    "messages": [
      {"role": "system", "content": "You are a helpful AI assistant."},
      {"role": "user", "content": "Explain the concept of KV cache in LLM inference."}
    ],
    "max_tokens": 100,
    "temperature": 0.7
  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output displays the model's response in JSON format with streaming tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Check the frontend logs for routing decision details, which show cost calculations for each worker:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Formula for worker_id=7587893399646425890 dp_rank=0 with 0 cached blocks: 5.188 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 2.125 + 2.000
Formula for worker_id=7587893399646425892 dp_rank=0 with 0 cached blocks: 5.188 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 2.125 + 2.000
Selected worker: worker_id=7587893399646425892 dp_rank=0, logit: 5.188, cached blocks: 0, tree size: 0, total blocks: 75379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router calculates cost per worker with &lt;code&gt;cost = overlap_weight × prefill_blocks + decode_blocks&lt;/code&gt;. In this example, both workers have 0 cached blocks (cold start), equal prefill costs (2.125 blocks weighted by 1.5), and equal decode loads (2.000). With identical costs (5.188), the router selects a worker based on temperature sampling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Send a second request with a similar prompt prefix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1",
    "messages": [
      {"role": "system", "content": "You are a helpful AI assistant."},
      {"role": "user", "content": "Explain the concept of KV cache reuse in distributed LLM serving."}
    ],
    "max_tokens": 100,
    "temperature": 0.7
  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Check the routing decision for the second request in the logs.&lt;/strong&gt; If the router detects cached blocks from the previous request with a matching prompt prefix, the logs show a reduced prefill cost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Formula for worker_id=7587893399646425890 dp_rank=0 with 0 cached blocks: 5.375 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 2.250 + 2.000
Formula for worker_id=7587893399646425892 dp_rank=0 with 1 cached blocks: 3.875 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 1.250 + 2.000
Selected worker: worker_id=7587893399646425892 dp_rank=0, logit: 3.875, cached blocks: 1, tree size: 8, total blocks: 75379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router prioritizes the worker with 1 cached block, reducing its cost from 5.375 to 3.875 — the cached block reduces prefill computation needed (1.250 blocks vs 2.250), demonstrating KV cache reuse resulting in faster TTFT for requests with overlapping prompt prefixes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Open a new terminal session on the host (outside the container) and monitor the KV event stream to verify event publishing:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; container:deploy-nats-server-1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  natsio/nats-box nats stream info namespace-dynamo-component-backend-kv-events
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output displays the JetStream statistics including message count and consumer information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Information for Stream namespace-dynamo-component-backend-kv-events
......
                Subjects: namespace-dynamo-component-backend-kv-events.*
                Replicas: 1
                Storage: File
......
State:
......
                Messages: 8
                Bytes: 2.3 KiB
        Active Consumers: 1
......
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message count increases as workers publish KV cache events — each message represents a KV cache state change, such as block creation or eviction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure KV Routing for Disaggregated Serving
&lt;/h2&gt;

&lt;p&gt;Disaggregated serving separates prefill and decode phases across different worker pools. The KV Router optimizes both prefill worker selection (based on cache hits) and decode worker assignment (based on load distribution), while coordinating KV cache transfer between phases via NIXL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Disaggregated Serving with KV Routing
&lt;/h3&gt;

&lt;p&gt;The deployment script configures the frontend with increased overlap weight for prefill optimization, launches prefill workers with low batch sizes for TTFT optimization, and decode workers with high batch sizes for throughput maximization. The NIXL connector enables efficient KV cache transfer between prefill and decode workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. If the aggregated serving script is still running from the previous section, stop it by pressing &lt;code&gt;Ctrl+C&lt;/code&gt; in the container terminal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Inside the same container, create the disaggregated serving deployment script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' &amp;gt; ~/nemotron_disagg_kv_routing.sh
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;!/bin/bash
&lt;/span&gt;&lt;span class="go"&gt;source ~/kv_routing.env

&lt;/span&gt;&lt;span class="gp"&gt;MODEL="$&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;1:-nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;pkill -f "dynamo.frontend";&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pkill &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"dynamo.vllm"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="go"&gt;set -e
trap 'kill 0' EXIT

python -m dynamo.frontend --router-reset-states &amp;amp;
sleep 10

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Decode Worker 1 - GPU 0
&lt;span class="go"&gt;CUDA_VISIBLE_DEVICES=0 python3 -m dynamo.vllm \
&lt;/span&gt;&lt;span class="gp"&gt;    --model "$&lt;/span&gt;MODEL&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="go"&gt;    --connector nixl \
    --is-decode-worker \
    --max-model-len 2048 \
    --enable-prefix-caching \
    --trust-remote-code &amp;amp;

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Decode Worker 2 - GPU 1
&lt;span class="go"&gt;VLLM_NIXL_SIDE_CHANNEL_PORT=20097 \
CUDA_VISIBLE_DEVICES=1 python3 -m dynamo.vllm \
&lt;/span&gt;&lt;span class="gp"&gt;    --model "$&lt;/span&gt;MODEL&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="go"&gt;    --connector nixl \
    --is-decode-worker \
    --max-model-len 2048 \
    --enable-prefix-caching \
    --trust-remote-code &amp;amp;

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Prefill Worker 1 - GPU 2 &lt;span class="o"&gt;(&lt;/span&gt;ZMQ port 5559&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;CUDA_VISIBLE_DEVICES=2 python3 -m dynamo.vllm \
&lt;/span&gt;&lt;span class="gp"&gt;    --model "$&lt;/span&gt;MODEL&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="go"&gt;    --connector nixl \
    --is-prefill-worker \
    --max-model-len 2048 \
    --enable-prefix-caching \
    --trust-remote-code \
    --kv-events-config '{"enable_kv_cache_events":true,"endpoint":"tcp://*:5559"}' &amp;amp;

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Prefill Worker 2 - GPU 3 &lt;span class="o"&gt;(&lt;/span&gt;ZMQ port 5560&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;VLLM_NIXL_SIDE_CHANNEL_PORT=20099 \
CUDA_VISIBLE_DEVICES=3 python3 -m dynamo.vllm \
&lt;/span&gt;&lt;span class="gp"&gt;    --model "$&lt;/span&gt;MODEL&lt;span class="s2"&gt;" &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="go"&gt;    --connector nixl \
    --is-prefill-worker \
    --max-model-len 2048 \
    --enable-prefix-caching \
    --trust-remote-code \
    --kv-events-config '{"enable_kv_cache_events":true,"endpoint":"tcp://*:5560"}' &amp;amp;

sleep 30
&lt;/span&gt;&lt;span class="gp"&gt;echo "Disaggregated serving ready at http://localhost:8000 (Model: $&lt;/span&gt;MODEL&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;echo "Architecture: 2 Decode Workers (GPU 0-1) + 2 Prefill Workers (GPU 2-3)"
wait
EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script configures disaggregated serving with KV-aware routing and NIXL for KV cache transfer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts the model name as a command-line argument (defaults to NVIDIA Nemotron Nano 4B).&lt;/li&gt;
&lt;li&gt;Sources the environment configuration created earlier (includes &lt;code&gt;DYN_EVENT_PLANE=nats&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;--connector nixl&lt;/code&gt; to enable NIXL for KV cache transfer between prefill and decode workers.&lt;/li&gt;
&lt;li&gt;Sets unique NIXL ports via &lt;code&gt;VLLM_NIXL_SIDE_CHANNEL_PORT&lt;/code&gt; for workers 2 and 4 (20097, 20099).&lt;/li&gt;
&lt;li&gt;Enables prefix caching on all workers with &lt;code&gt;--enable-prefix-caching&lt;/code&gt; for KV cache reuse.&lt;/li&gt;
&lt;li&gt;Configures decode workers on GPUs 0-1 to receive KV cache from prefill workers via NIXL.&lt;/li&gt;
&lt;li&gt;Configures prefill workers on GPUs 2-3 with ZMQ endpoints (ports 5559, 5560) for KV event publishing.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;--trust-remote-code&lt;/code&gt; to support NVIDIA Nemotron models.&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;--max-model-len 2048&lt;/code&gt; for efficient memory usage across all workers.&lt;/li&gt;
&lt;li&gt;Frontend automatically detects prefill and decode workers to enable disaggregated routing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Make the script executable:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/nemotron_disagg_kv_routing.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Run the disaggregated serving script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/nemotron_disagg_kv_routing.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script starts the frontend, two decode workers on GPUs 0-1, and two prefill workers on GPUs 2-3. The frontend automatically detects prefill workers and creates an internal prefill router for cache-aware prefill worker selection, while the main router handles decode worker assignment based on load distribution.&lt;/p&gt;

&lt;p&gt;To deploy a different model, pass the model name as an argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/nemotron_disagg_kv_routing.sh &lt;span class="s2"&gt;"nvidia/Llama-3_3-Nemotron-Super-49B-v1_5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Disaggregated KV Routing
&lt;/h3&gt;

&lt;p&gt;Disaggregated routing involves two routing decisions: prefill worker selection based on cache overlap, and decode worker assignment based on load and available capacity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Open a new terminal session and send a test request to verify the disaggregated flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1",
    "messages": [
      {"role": "system", "content": "You are a helpful AI assistant specialized in distributed systems."},
      {"role": "user", "content": "Explain how KV cache transfer works in disaggregated LLM serving using NIXL."}
    ],
    "max_tokens": 150,
    "temperature": 0.7
  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Check the frontend logs for prefill routing decisions, which show prefill worker cost calculations with the higher overlap weight (1.5):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO dynamo_llm::kv_router::scheduler: Formula for worker_id=7587893399646425930 dp_rank=0 with 0 cached blocks: 4.125 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 2.750 + 0.000
INFO dynamo_llm::kv_router::scheduler: Formula for worker_id=7587893399646425934 dp_rank=0 with 0 cached blocks: 4.125 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 2.750 + 0.000
INFO dynamo_llm::kv_router::scheduler: Selected worker: worker_id=7587893399646425930 dp_rank=0, logit: 4.125, cached blocks: 0, tree size: 0, total blocks: 75379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first (cold-start) request, both prefill workers have 0 cached blocks, resulting in equal costs (4.125). The decode blocks component is 0.0 because prefill workers only handle the prefill phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Check the decode routing decisions in the script output.&lt;/strong&gt; After prefill completion, the decode router assigns the request to a decode worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO dynamo_llm::kv_router::scheduler: Formula for worker_id=7587893399646425936 dp_rank=0 with 0 cached blocks: 2.000 = 0.0 * prefill_blocks + decode_blocks = 0.0 * 2.750 + 2.000
INFO dynamo_llm::kv_router::scheduler: Selected worker: worker_id=7587893399646425936 dp_rank=0, logit: 2.000, cached blocks: 0, tree size: 0, total blocks: 75379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prefill blocks component is 0.0 in this formula because decode workers don't compute prefills — they receive KV cache from prefill workers via NIXL transfer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Monitor NIXL transfer success in the worker logs displayed by the script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO nixl_connector._nixl_handshake: NIXL compatibility check passed (hash: cae818f9461cb277c305be4b8cd4aca3273e21a6ea98f7399d1a339560e1b3ea)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NIXL successfully transfers KV cache blocks from prefill to decode workers using RDMA for low-latency, high-bandwidth data transfer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Send a second request with a similar prompt prefix to verify cache reuse:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1",
    "messages": [
      {"role": "system", "content": "You are a helpful AI assistant specialized in distributed systems."},
      {"role": "user", "content": "Explain how KV cache reuse works in disaggregated LLM serving."}
    ],
    "max_tokens": 150,
    "temperature": 0.7
  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Check the prefill routing logs for the second request, which show improved cache hits:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO dynamo_llm::kv_router::scheduler: Formula for worker_id=7587893399646425930 dp_rank=0 with 1 cached blocks: 2.250 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 1.500 + 0.000
INFO dynamo_llm::kv_router::scheduler: Formula for worker_id=7587893399646425934 dp_rank=0 with 0 cached blocks: 3.750 = 1.5 * prefill_blocks + decode_blocks = 1.5 * 2.500 + 0.000
INFO dynamo_llm::kv_router::scheduler: Selected worker: worker_id=7587893399646425930 dp_rank=0, logit: 2.250, cached blocks: 1, tree size: 2, total blocks: 75379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prefill router now sees 1 cached block on the selected worker, reducing its cost from 3.750 to 2.250 — demonstrating KV-aware routing's ability to optimize TTFT by leveraging cached prompt prefixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure KV Routing with SGLang and TensorRT-LLM
&lt;/h2&gt;

&lt;p&gt;The KV Router environment configuration is universal across all inference backends. The same environment variables used for vLLM deployments work identically with SGLang and TensorRT-LLM, requiring only backend-specific adjustments for block size and KV event publishing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Universal Environment Variables
&lt;/h3&gt;

&lt;p&gt;These environment variables work across all backends (vLLM, SGLang, TensorRT-LLM) without modification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Infrastructure endpoints
&lt;span class="go"&gt;export ETCD_ENDPOINTS=http://localhost:2379
export NATS_SERVER=nats://localhost:4222
export DYN_EVENT_PLANE=nats
export PYTHONHASHSEED=0

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Router configuration
&lt;span class="go"&gt;export DYN_ROUTER_MODE=kv
export DYN_ROUTER_TEMPERATURE=0.1
export DYN_KV_OVERLAP_SCORE_WEIGHT=1.5
export DYN_KV_EVENTS=true

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Load balancing thresholds
&lt;span class="go"&gt;export DYN_ACTIVE_DECODE_BLOCKS_THRESHOLD=0.85
export DYN_ACTIVE_PREFILL_TOKENS_THRESHOLD=10000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backend-Specific Configuration
&lt;/h3&gt;

&lt;p&gt;While the router environment variables are universal, two aspects require backend-specific configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Block Size Configuration&lt;/strong&gt; — &lt;code&gt;DYN_KV_CACHE_BLOCK_SIZE&lt;/code&gt; must match your backend's actual block size:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;Typical Block Size&lt;/th&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SGLang&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;&lt;code&gt;export DYN_KV_CACHE_BLOCK_SIZE=16&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TensorRT-LLM&lt;/td&gt;
&lt;td&gt;64 or 128 (varies by model)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;export DYN_KV_CACHE_BLOCK_SIZE=64&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vLLM&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;&lt;code&gt;export DYN_KV_CACHE_BLOCK_SIZE=16&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Mismatched block sizes between the router and workers cause incorrect cost calculations and routing decisions. Always verify your backend's block size configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;KV Event Publishing Configuration&lt;/strong&gt; — each backend uses different command-line parameters to enable KV event publishing to the router:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Example Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SGLang&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--kv-events-config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nats://localhost:4222&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TensorRT-LLM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--publish-events-and-metrics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No value (flag only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vLLM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--kv-events-config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'{"enable_kv_cache_events":true,"endpoint":"tcp://*:5557"}'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SGLang publishes events using a NATS URL format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--kv-events-config nats://localhost:4222
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TensorRT-LLM uses a simple flag to enable event publishing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--publish-events-and-metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;vLLM uses a JSON configuration format with a ZMQ endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--kv-events-config&lt;/span&gt; &lt;span class="s1"&gt;'{"enable_kv_cache_events":true,"endpoint":"tcp://*:5557"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tuning KV Router Parameters
&lt;/h2&gt;

&lt;p&gt;The KV Router provides several parameters to optimize routing behavior for different workload characteristics. Proper tuning balances cache reuse (TTFT optimization) with load distribution (ITL optimization).&lt;/p&gt;

&lt;h3&gt;
  
  
  Overlap Score Weight
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--kv-overlap-score-weight&lt;/code&gt; parameter controls the router's preference for cache hits versus load balancing. Default value: &lt;code&gt;1.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Increase (1.5-2.0) for prefill-heavy workloads — long input prompts (&amp;gt;2000 tokens), short output generation (&amp;lt;200 tokens), and where minimizing TTFT is critical:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --kv-overlap-score-weight 1.5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decrease (0.5-0.8) for decode-heavy workloads — short input prompts (&amp;lt;500 tokens), long output generation (&amp;gt;1000 tokens), and where maximizing throughput is critical:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --kv-overlap-score-weight 0.7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set to &lt;code&gt;0.0&lt;/code&gt; to disable cache-aware routing and use pure load balancing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --kv-overlap-score-weight 0.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mode ignores cached blocks and routes based solely on worker load — useful for workloads with no prefix overlap or for baseline performance comparisons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Router Temperature
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--router-temperature&lt;/code&gt; parameter introduces randomness in worker selection to prevent saturation and improve load distribution. Default value: &lt;code&gt;0.0&lt;/code&gt; (deterministic).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Temperature = 0.0&lt;/strong&gt; (default): Always select the lowest-cost worker. Best for maximizing cache reuse in stable workloads, with the risk of oversaturating workers with the best cache state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temperature = 0.2-0.5&lt;/strong&gt;: Add slight randomness while still favoring low-cost workers. Best for production workloads with variable request patterns, giving better load distribution without sacrificing much cache efficiency:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --router-temperature 0.3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Temperature = 1.0+&lt;/strong&gt;: More uniform distribution across workers. Best for debugging, testing, or when cache reuse is less important — approaches random selection at very high temperatures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  KV Events Configuration
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--no-kv-events&lt;/code&gt; flag disables real-time KV event tracking and switches to prediction-based routing. Default: KV events enabled.&lt;/p&gt;

&lt;p&gt;When enabled, the router predicts cache state based on its own routing decisions instead of receiving events from workers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --no-kv-events \
  --router-ttl 120 \
  --router-max-tree-size 1048576 \
  --router-prune-target-ratio 0.8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional parameters for prediction mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--router-ttl 120&lt;/code&gt;: Expire cached blocks after 120 seconds (default: 120).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--router-max-tree-size 1048576&lt;/code&gt;: Maximum blocks before pruning (default: 2^20).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--router-prune-target-ratio 0.8&lt;/code&gt;: Prune to 80% of max size when threshold exceeded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use prediction mode when your backend doesn't support KV event publishing, you want lower infrastructure overhead (no NATS dependency), or your deployment is simple enough that prediction accuracy is acceptable.&lt;/p&gt;

&lt;p&gt;When using &lt;code&gt;--no-kv-events&lt;/code&gt;, configure backends to disable event publishing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;vLLM&lt;/strong&gt;: Use &lt;code&gt;--kv-events-config '{"enable_kv_cache_events": false}'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SGLang&lt;/strong&gt;: Do not use &lt;code&gt;--kv-events-config&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TRT-LLM&lt;/strong&gt;: Do not use &lt;code&gt;--publish-events-and-metrics&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Active Block Busy Detection
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--active-decode-blocks-threshold&lt;/code&gt; parameter marks workers as busy when their KV cache utilization exceeds a threshold, preventing overload. Default: disabled (no threshold).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --active-decode-blocks-threshold 0.85 \
  --active-prefill-tokens-threshold 10000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--active-decode-blocks-threshold 0.85&lt;/code&gt;: Mark worker busy at 85% KV cache utilization.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--active-prefill-tokens-threshold 10000&lt;/code&gt;: Mark worker busy when prefill tokens exceed 10,000.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Busy workers are excluded from routing decisions until their load drops below the threshold, preventing request queueing and maintaining consistent latency. Thresholds can be updated at runtime without restarting the frontend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/busy_threshold &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  -H "Content-Type: application/json" \
  -d '{
    "model": "nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1",
    "active_decode_blocks_threshold": 0.90,
    "active_prefill_tokens_threshold": 12000
  }'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response confirms the updated thresholds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nvidia/Llama-3.1-Nemotron-Nano-4B-v1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active_decode_blocks_threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active_prefill_tokens_threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Router Replica Synchronization
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--router-replica-sync&lt;/code&gt; flag enables active block state synchronization between multiple router replicas for consistent load balancing. Default: disabled.&lt;/p&gt;

&lt;p&gt;Enable it when launching multiple frontend replicas for fault tolerance and horizontal scaling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Router replica 1
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --http-port 8000 \
  --router-replica-sync

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Router replica 2
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; dynamo.frontend &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;  --router-mode kv \
  --http-port 8001 \
  --router-replica-sync
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each replica maintains its own view of active blocks (ephemeral state); replicas exchange routing decisions via NATS core messaging, and cached block state (persistent) syncs automatically via JetStream. New replicas start with zero active blocks but converge quickly through sync, giving consistent routing decisions and better load distribution across replicas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitor KV Router Performance
&lt;/h2&gt;

&lt;p&gt;To monitor KV Router performance metrics including routing decisions, cache efficiency, and worker utilization, deploy an observability stack (Grafana and Prometheus) alongside your Dynamo deployment. Add the KV Router environment variables from this guide to your deployment configuration, then use Grafana dashboards to visualize KV cache routing performance metrics and Prometheus to track TTFT improvements, cache hit rates, and worker selection patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tune &lt;code&gt;DYN_KV_OVERLAP_SCORE_WEIGHT&lt;/code&gt; and &lt;code&gt;DYN_ROUTER_TEMPERATURE&lt;/code&gt; against your real traffic patterns rather than the defaults&lt;/li&gt;
&lt;li&gt;Stand up Grafana and Prometheus dashboards to track TTFT, cache hit rate, and worker load over time&lt;/li&gt;
&lt;li&gt;Test SGLang or TensorRT-LLM backends with the same router configuration to compare cache efficiency&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;--router-replica-sync&lt;/code&gt; before scaling to multiple frontend replicas in production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-configure-smart-routing-in-nvidia-dynamo" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>llm</category>
      <category>nvidia</category>
      <category>gpu</category>
      <category>inference</category>
    </item>
    <item>
      <title>Automating Bash Scripts on Linux Using Cron and Systemd</title>
      <dc:creator>Sanskriti Harmukh</dc:creator>
      <pubDate>Wed, 14 Oct 2026 21:06:31 +0000</pubDate>
      <link>https://dev.to/vultr/automating-bash-scripts-on-linux-using-cron-and-systemd-3nie</link>
      <guid>https://dev.to/vultr/automating-bash-scripts-on-linux-using-cron-and-systemd-3nie</guid>
      <description>&lt;p&gt;Automating Bash scripts on Linux is essential for system administration tasks, such as installing applications and monitoring system logs. Cron is a job scheduler that runs tasks at specific intervals, while systemd is a service manager that handles applications through service profiles. This guide walks through writing two Bash scripts — one for log monitoring and one for backups — then automating them with a cron job and running one as a persistent systemd service. By the end, you'll have both a scheduled cron job and a managed systemd service running your own Bash scripts automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have access to a Ubuntu server as your management workstation.&lt;/li&gt;
&lt;li&gt;Access the server using SSH as a non-root sudo user.&lt;/li&gt;
&lt;li&gt;Install a LAMP stack (Apache, MySQL, PHP).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create Bash Scripts
&lt;/h2&gt;

&lt;p&gt;Bash scripts support multiple commands and functions that define how tasks run in a system. Follow the steps below to create two Bash scripts: one to back up critical web application files, and one to monitor system logs for errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Switch to your user's home directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create a new &lt;code&gt;log-monitor.sh&lt;/code&gt; script:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nano log-monitor.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Add the following contents to the &lt;code&gt;log-monitor.sh&lt;/code&gt; file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/syslog"&lt;/span&gt; 
&lt;span class="nv"&gt;ERROR_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/data/error_log.txt"&lt;/span&gt;
&lt;span class="nv"&gt;WARNING_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/data/warning_log.txt"&lt;/span&gt;

&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERROR_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;elif &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"warning"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WARNING_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and close the file. This script creates &lt;code&gt;error_log.txt&lt;/code&gt; and &lt;code&gt;warning_log.txt&lt;/code&gt; files in the &lt;code&gt;/opt/data&lt;/code&gt; directory, filtering results from &lt;code&gt;/var/log/syslog&lt;/code&gt; using a &lt;code&gt;while&lt;/code&gt; loop. It writes each new line to the respective error or warning file as it appears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Create another script named &lt;code&gt;backup.sh&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nano backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Add the following contents to the file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"new-password"&lt;/span&gt;
&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mysql"&lt;/span&gt;

&lt;span class="nv"&gt;DB_BACKUP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mysql_backup"&lt;/span&gt;
&lt;span class="nv"&gt;WEB_FILES_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"web_backup"&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB_BACKUP_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WEB_FILES_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%Y%m%d_%H%M%S"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

mysqldump &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB_USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DB_BACKUP_DIR&lt;/span&gt;&lt;span class="s2"&gt;/db_backup_&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;.sql"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"MySQL backup completed: &lt;/span&gt;&lt;span class="nv"&gt;$DB_BACKUP_DIR&lt;/span&gt;&lt;span class="s2"&gt;/db_backup_&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;.sql"&lt;/span&gt;

&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WEB_FILES_DIR&lt;/span&gt;&lt;span class="s2"&gt;/web_backup_&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; /var/www/html
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Web application backup completed: &lt;/span&gt;&lt;span class="nv"&gt;$WEB_FILES_DIR&lt;/span&gt;&lt;span class="s2"&gt;/web_backup_&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and close the file. This script connects to the MySQL database server using the &lt;code&gt;DB_...&lt;/code&gt; variables, then creates &lt;code&gt;mysql_backup&lt;/code&gt; and &lt;code&gt;web_backup&lt;/code&gt; directories to store the database and web application backups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Move the Bash scripts to a system-wide directory like &lt;code&gt;/opt&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;backup.sh log-monitor.sh /opt/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Create a new &lt;code&gt;data&lt;/code&gt; directory under the system-wide directory:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;8. Change the directory permissions to &lt;code&gt;777&lt;/code&gt; to allow all users to write files:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;777 /opt/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automate the Bash Script Using Cron
&lt;/h2&gt;

&lt;p&gt;Cron creates jobs you can use to schedule and automatically run a Bash script at specific intervals. Follow the steps below to automate the &lt;code&gt;backup.sh&lt;/code&gt; script to back up the MySQL database and web application files daily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Open the crontab editor:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select your desired text editor when prompted — for example, select &lt;code&gt;1&lt;/code&gt; to choose the &lt;code&gt;nano&lt;/code&gt; text editor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Add the following directive at the end of the file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /opt/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and close the file. This cron job runs &lt;code&gt;backup.sh&lt;/code&gt; daily at 2:00 A.M. In the job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt;: Runs the script at the start of a minute. Set a value between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;59&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt;: Runs the script at 2:00 A.M. Specify an hour between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;23&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt;: Runs the script every day of the month. Specify a day between &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;31&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt;: Runs the script every month. Specify a month between &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;12&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt;: Runs the script daily. Specify a day of the week between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;7&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. List all cron jobs and verify the new task is available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;# For more information see the manual pages of crontab(5) and cron(8)&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;# m h  dom mon dow   command&lt;/span&gt;

0 2 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; ~/backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automate the Bash Script as a System Service Using Systemd
&lt;/h2&gt;

&lt;p&gt;Systemd starts system services automatically at boot and offers options for stopping and restarting them. Follow the steps below to set up the log-monitoring script as a system service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a new system service file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/systemd/system/logmonitor.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Add the following contents to the &lt;code&gt;logmonitor.service&lt;/code&gt; file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;Unit]
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; Automatic System Logs Monitoring and Filtering
&lt;span class="nv"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;network.target

&lt;span class="o"&gt;[&lt;/span&gt;Service]
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bash /opt/log-monitor.sh
&lt;span class="nv"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always

&lt;span class="o"&gt;[&lt;/span&gt;Install]
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and close the file. This service configuration runs the &lt;code&gt;log-monitor.sh&lt;/code&gt; script from &lt;code&gt;/opt&lt;/code&gt; to monitor and filter log entries in &lt;code&gt;/var/log/syslog&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Reload systemd to apply the service file changes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Enable the new system service to automatically start at boot:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;logmonitor.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Created symlink /etc/systemd/system/multi-user.target.wants/logmonitor.service → /etc/systemd/system/logmonitor.service.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Start the system service:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start logmonitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. View the system service status and verify it's running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status logmonitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;● logmonitor.service - Automatic System Logs Monitoring and Filtering
 Loaded: loaded (/etc/systemd/system/logmonitor.service; enabled; preset: enabled)
 Active: active (running) since Wed 2024-11-06 13:20:07 UTC; 11s ago
Main PID: 14293 (bash)
   Tasks: 3 (limit: 1062)
  Memory: 784.0K (peak: 1.5M)
     CPU: 40ms
  CGroup: /system.slice/logmonitor.service
          ├─14293 bash /opt/log-monitor.sh
          ├─14308 tail -F /var/log/syslog
          └─14309 bash /opt/log-monitor.sh

Nov 06 13:20:07 Linux-server systemd[1]: Started logmonitor.service - Automatic System Logs Monitoring and Filtering.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Run the following commands to simulate new error and warning entries in &lt;code&gt;/var/log/syslog&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;logger &lt;span class="nt"&gt;-p&lt;/span&gt; user.err &lt;span class="s2"&gt;"This is a simulated error log message from MySQL"&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;logger &lt;span class="nt"&gt;-p&lt;/span&gt; user.err &lt;span class="s2"&gt;"This is a simulated warning log message from Apache"&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;logger &lt;span class="nt"&gt;-p&lt;/span&gt; user.err &lt;span class="s2"&gt;"This is a simulated error log message from PHP"&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;logger &lt;span class="nt"&gt;-p&lt;/span&gt; user.err &lt;span class="s2"&gt;"This is a simulated Warning log message from MySQL"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;8. List all files in &lt;code&gt;/opt/data&lt;/code&gt; and verify that &lt;code&gt;error_log.txt&lt;/code&gt; and &lt;code&gt;warning_log.txt&lt;/code&gt; are available:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /opt/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error_log.txt warning_log.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;9. View the &lt;code&gt;error_log.txt&lt;/code&gt; file and verify that only error entries are present:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;error.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2024-11-06T13:33:48.010096+00:00 Server linuxuser: This is a simulated error log message from MySQL
2024-11-06T13:33:48.010096+00:00 Server linuxuser: This is a simulated error log message from PHP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add more granular filters (by service name or severity) to &lt;code&gt;log-monitor.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Extend &lt;code&gt;backup.sh&lt;/code&gt; with retention logic to prune old backups automatically&lt;/li&gt;
&lt;li&gt;Ship backups to remote or off-site storage so they survive a full server loss&lt;/li&gt;
&lt;li&gt;Add alerting (email or webhook) when new entries land in &lt;code&gt;error_log.txt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the full guide with additional tips, visit the original article on &lt;strong&gt;&lt;a href="https://docs.vultr.com/how-to-automate-bash-scripts-on-linux-using-cron-and-systemd" rel="noopener noreferrer"&gt;Vultr Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>cron</category>
      <category>systemd</category>
    </item>
  </channel>
</rss>
