<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ ktor - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ ktor - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 18 Jun 2026 16:16:52 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/ktor/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a Kotlin Microservice to AWS with App Runner ]]>
                </title>
                <description>
                    <![CDATA[ By Piotr Wolak Hello, everyone. In this step-by-step tutorial, I would like to show you how to deploy a Kotlin Microservice using Docker and AWS App Runner.  Together, we will learn: what exactly is AWS App Runner?  how to configure AWS Command Line... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/kotlin-aws-app-runner/</link>
                <guid isPermaLink="false">66d4608bd1ffc3d3eb89de34</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kotlin ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ktor ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 15 Apr 2022 06:34:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/kotlin-aws-app-runner.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Piotr Wolak</p>
<p>Hello, everyone. In this step-by-step tutorial, I would like to show you how to deploy a Kotlin Microservice using Docker and AWS App Runner. </p>
<p>Together, we will learn:</p>
<ul>
<li>what exactly is AWS App Runner? </li>
<li>how to configure AWS Command Line Interface on your local machine </li>
<li>how to push Docker images to Amazon Elastic Container Registry (ECR)</li>
<li>and finally, how to deploy our containerized application with AWS App Runner</li>
</ul>
<p>I know, it might sound like a tremendous amount of work. But I am convinced that you will find out how simple it can be with the above tech stack.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure that you have <strong>Docker</strong> already installed on you local machine. We'll need to containerize our application. </p>
<p>If you don't have Docker, then the official <a target="_blank" href="https://docs.docker.com/engine/install/">Docker documentation</a> will help you set it up in a few minutes. </p>
<h2 id="heading-what-exactly-is-aws-app-runner">What Exactly is AWS App Runner?</h2>
<p>First, let's take minute to understand what exactly <strong>AWS App Runner</strong> is.</p>
<p>To put it simply, it is a fully managed service which allows you to build and deploy containerized web applications and APIs with ease. </p>
<p>It takes care of plenty of things, like traffic load balancing, or scaling, which helps developers like you and me focus on the code. </p>
<p><strong>AWS App Runner</strong> oftentimes is a great choice when creating a demo or proof of concept, but it's also worth considering for smaller teams without a dedicated person working on infrastructure. </p>
<h2 id="heading-how-to-create-asimple-kotlin-microservice">How to Create  aSimple Kotlin Microservice</h2>
<p>With that being said, let's prepare a simple REST API using Kotlin and Ktor. </p>
<p>If you are not interested in the Ktor implementation, then you can simply clone <a target="_blank" href="https://github.com/codersee-blog/ktor-app-runner-skeleton">this GitHub repository</a> and proceed to the <em>How to Build the Docker Image</em> step. </p>
<p>If you are using the IntelliJ IDEA Ultimate Edition, then you can create a Ktor project using the app. Otherwise, you can use the <a target="_blank" href="https://start.ktor.io/">Ktor Project Generator</a> tool and download project to your local machine.</p>
<p>Regardless of your choice, make sure to import the following plugins: </p>
<ul>
<li>ContentNegotiation</li>
<li>kotlinx.serialization</li>
<li>Routing</li>
</ul>
<h3 id="heading-how-to-configure-serialization">How to Configure Serialization</h3>
<p>After you've imported the project, create the <code>Serialization.kt</code> file and register <code>application/json</code> content type to the ContentNegotiation feature:</p>
<pre><code class="lang-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> Application.<span class="hljs-title">configureSerialization</span><span class="hljs-params">()</span></span> {
    install(ContentNegotiation) {
        json()
    }
}
</code></pre>
<p>In simple words, with this code snippet we will be able to serialize Kotlin objects into JSON (and deserialize the JSON into objects, as well). </p>
<h3 id="heading-how-to-create-a-dto">How to Create a DTO</h3>
<p>Now let's implement a <code>MessageDto</code> data class like this:</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Serializable</span>
<span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MessageDto</span></span>(<span class="hljs-keyword">val</span> message: String)
</code></pre>
<p>Basically, we will use this generic class to provide messages for our API consumers. </p>
<h3 id="heading-how-to-expose-endpoints">How to Expose Endpoints</h3>
<p>As the next step, let's create a <code>Routing.kt</code> file and expose a new endpoint: </p>
<pre><code class="lang-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> Application.<span class="hljs-title">configureRouting</span><span class="hljs-params">()</span></span> {
    routing {
        helloWorldRoute()
    }
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> Routing.<span class="hljs-title">helloWorldRoute</span><span class="hljs-params">()</span></span> {
    route(<span class="hljs-string">"/hello"</span>) {
        <span class="hljs-keyword">get</span> {
            call.respond(HttpStatusCode.OK, MessageDto(<span class="hljs-string">"Hello World!"</span>))
        }
    }
}
</code></pre>
<p>As you can see, our application will respond with a <strong>200 OK</strong> status code to each <code>GET</code> request to the <code>/hello</code> path.</p>
<h3 id="heading-how-to-configure-the-app">How to Configure the App</h3>
<p>Now, let's combine everything inside the <code>Application.kt</code> file: </p>
<pre><code class="lang-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    embeddedServer(Netty, port = <span class="hljs-number">8080</span>, host = <span class="hljs-string">"0.0.0.0"</span>) {
        configureRouting()
        configureSerialization()
    }.start(wait = <span class="hljs-literal">true</span>)
}
</code></pre>
<p>As you can see, our Kotlin microservice will be a <strong>Netty embedded server</strong> running on <code>localhost:8080</code>. </p>
<p>I highly encourage you to run the application and verify that everything is working properly:</p>
<pre><code>GET localhost:<span class="hljs-number">8080</span>/hello

<span class="hljs-attr">Status</span>: <span class="hljs-number">200</span> OK
Response Body: 
{
    <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello World!"</span>
}
</code></pre><h3 id="heading-how-to-implement-the-dockerfile">How to Implement the Dockerfile</h3>
<p>Finally, let's add the <code>Dockerfile</code> to the root directory of our project:</p>
<pre><code>FROM openjdk:<span class="hljs-number">8</span>-jdk
EXPOSE <span class="hljs-number">8080</span>:<span class="hljs-number">8080</span>
RUN mkdir /app
COPY ./build/install/com.codersee.ktor-app-runner/ <span class="hljs-regexp">/app/</span>
WORKDIR /app/bin
CMD [<span class="hljs-string">"./com.codersee.ktor-app-runner"</span>]
</code></pre><p>Make sure that the directory specified for the <code>COPY</code> and <code>CMD</code> commands matches the value of <code>rootProject.name</code> inside the <code>settings.gradle.kts</code> file. If the project's name is <code>xyz</code> , then these commands should reflect that:</p>
<pre><code>...
COPY ./build/install/xyz/ <span class="hljs-regexp">/app/</span>
...
CMD [<span class="hljs-string">"./xyz"</span>]
</code></pre><h2 id="heading-how-to-build-the-docker-image">How to Build the Docker Image</h2>
<p>At this point, we have everything we need to build our <strong>Docker Image</strong>, which we will use later for the <strong>AWS App Runner</strong> deployment.</p>
<h3 id="heading-run-the-gradle-command">Run the Gradle Command</h3>
<p>As the first step, let's run the <code>installDist</code> command with Gradle Wrapper: </p>
<pre><code>./gradlew installDist
</code></pre><p>The above command is responsible for assembling the distribution content and installing it on the current machine. Although it might sound difficult, it will simply create necessary files inside the <code>./build/install/{project-name}/</code> directory. </p>
<h3 id="heading-build-the-docker-image">Build the Docker Image</h3>
<p>As the next step, let's build a Docker Image:</p>
<pre><code> docker build -t ktor-aws-runner .
</code></pre><p>As you can see, we named our desired image <code>ktor-aws-runner</code> with the <code>-t</code> option (a shortcut for <code>--tag</code>).</p>
<h3 id="heading-verify-docker-configuration">Verify Docker Configuration</h3>
<p>Finally, let's run our container to make sure that our Kotlin microservice is working properly:</p>
<pre><code>docker run -p <span class="hljs-number">8080</span>:<span class="hljs-number">8080</span> ktor-aws-runner
</code></pre><p>As a word of explanation, the <code>-p</code> flag (<code>--port</code>)  is responsible for publishing the container's <code>8080</code> port to the host <code>8080</code> port. </p>
<p>With that being done, after a few seconds we should see the following message in logs: </p>
<pre><code>Application started <span class="hljs-keyword">in</span> <span class="hljs-number">0.078</span> seconds
</code></pre><p>Similarly, we can perform a GET request to check if the exposed endpoint is responding correctly.</p>
<h2 id="heading-how-to-create-and-configure-an-aws-user">How to Create and Configure an AWS User</h2>
<p>With all of that being done, we can finally start working with AWS. But before we're able to push our Docker Image, we need to make sure that we have <strong>AWS CLI</strong> installed on our local machine. </p>
<p>We can do that easily with the below command: </p>
<pre><code> aws --version

 # Result:
 aws-cli/<span class="hljs-number">2.5</span><span class="hljs-number">.3</span> Python/<span class="hljs-number">3.9</span><span class="hljs-number">.11</span> Windows/<span class="hljs-number">10</span> exe/AMD64 prompt/off
</code></pre><p>The above result indicates that everything is setup correctly. Nevertheless, if we would like to install or update the CLI, then AWS ships with a really good article on that in their <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">official documentation</a>. </p>
<p>Additionally, we have to have the access to AWS Cloud from our computer – and that's what we are going to set up in this step.</p>
<p>In order to set up the access, let's sign in to the <strong>AWS Management Console</strong> and navigate to the <strong>Users</strong> feature of the <strong>IAM Console</strong>. We can do that easily with the search bar at the top: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/1-1.png" alt="Image shows search results for users query in AWS Management Console" width="600" height="400" loading="lazy"></p>
<p>On the next page, let's click the <strong>Add users</strong> button: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/2-1.png" alt="Image shows empty list of Users in AWS IAM Console" width="600" height="400" loading="lazy"></p>
<p>Then we'll specify the preferred <strong>User name</strong> along with the <strong>Access key</strong> – <strong>Programmatic access</strong> credential type: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/3-1.png" alt="Image shows user name and AWS access type options for new user" width="600" height="400" loading="lazy"></p>
<p>With these settings, we will be able to access AWS using a combination of access key and secret.</p>
<p>With that being done, let's hit the Next button. On this page, we have to select the group for our user. For the purpose of this tutorial, let's create a new one, using the button visible below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/4-2.png" alt="Image shows permissions and permissions boundary settings for new user" width="600" height="400" loading="lazy"></p>
<p>Nextly, let's specify a <strong>Group name</strong> ( <code>admin-group</code> in my case) in the modal and select the <strong>AdministratorAccess</strong>:</p>
<p>For simplicity, we are going to use the AdministratorAccess. But in real-life scenarios, we should always stick to The Principle of Least Privilege.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/5-2.png" alt="Image shows new group name with seleced policy- AdministratorAccess" width="600" height="400" loading="lazy"></p>
<p>After the group is created, let's hit the Next button once again: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/6-2.png" alt="Image shows existing groups and selected admin-group" width="600" height="400" loading="lazy"></p>
<p>On the next page, we have the option of adding <strong>custom tags</strong> as key-value pairs. </p>
<p>But we won't need them today, so let's simply skip this page: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/7-2.png" alt="Image shows add tags (optional) form" width="600" height="400" loading="lazy"></p>
<p>Finally, we will be redirected to the <strong>Review</strong> page, where we can validate our previous steps:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/8-1.png" alt="Image presents Review new user details" width="600" height="400" loading="lazy"></p>
<p>As you can see, everything looks good, so let's click <strong>Create user</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/9-1.png" alt="Image shows created user " width="600" height="400" loading="lazy"></p>
<p>The user was created successfully, so we can finally import our access and secret keys. </p>
<p>Keep in mind that access and secret keys are highly confidential data and you should never share them with anyone!</p>
<p>Let's click the <strong>Download .csv</strong> button and fetch the file. Personally, I named it <code>some_user_credentials.csv</code> , but feel free to pick whatever name you like (and remember it :) ).</p>
<p>Next, let's navigate to the download directory and run the following command:</p>
<pre><code> aws configure <span class="hljs-keyword">import</span> --csv file:<span class="hljs-comment">//some_user_credentials.csv</span>

 # Result:
 Successfully imported <span class="hljs-number">1</span> profile(s)
</code></pre><p>Given the above message, we can expect that everything was set up correctly. Additionally, we can verify that a new file called <code>credentials</code> has been created (or updated) inside the <code>.aws</code> directory. </p>
<p>If you are using Windows then your path will be  <code>C:\Users\[your_user_name]\.aws</code>:</p>
<pre><code>[some-admin]
aws_access_key_id = [your access key id] 
aws_secret_access_key = [your secret]
</code></pre><h2 id="heading-how-to-push-the-docker-image-to-ecr">How to Push the Docker Image to ECR</h2>
<p>At this point, our CLI is properly prepared, so we can learn how to push our local Docker Image to the <strong>Elastic Container Registry</strong>.</p>
<p>As the first step, let's get back to the <strong>Management Console</strong> and type <strong>container registry</strong> in the search bar: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/10-1.png" alt="Image shows search results for 'container reqistry' query" width="600" height="400" loading="lazy"></p>
<p>Let's click on the <strong>Elastic Container Registry</strong> and on the next page, the <strong>Create repository</strong> button. On the next page, let's select a Private repository and specify a name for it:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/11-1.png" alt="Image shows create repository general settings" width="600" height="400" loading="lazy"></p>
<p> For the rest of settings, let's leave the default values, just like below: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/12.png" alt="Image shows Image scan and encryption settings" width="600" height="400" loading="lazy"></p>
<p>Finally, let's hit the <strong>Create repository</strong> button.</p>
<p>After that, we will be redirected to the Private repositories list, which now contains our newly created repository: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/13.png" alt="Image presents private repositories with one item- my-ktor-registry on the list" width="600" height="400" loading="lazy"></p>
<p>Let's copy the URI and specify the following command in the terminal on our local machine: </p>
<pre><code>docker tag ktor-aws-runner:latest [your_registry_uri]:latest

# Example: docker tag ktor-aws-runner:latest <span class="hljs-number">111111111111.</span>dkr.ecr.us-east<span class="hljs-number">-1.</span>amazonaws.com/my-ktor-registry:latest
</code></pre><p>Why do we need that? Well, basically, when working with Docker <strong>we need to tag images with registry host and port</strong> (if necessary) in order to push them to any private repository. </p>
<p>With that being done, let's authenticate to the Amazon ECR registry:</p>
<pre><code>aws ecr get-login-password --profile some-admin --region us-east<span class="hljs-number">-1</span> | docker login --username AWS --password-stdin [your registry URI]

# Result:
Login Succeeded
</code></pre><p>After that, we can run the <code>git push</code> command in order to push the image to ECR: </p>
<pre><code>docker push [your_tagged_image] 

# Example: docker push <span class="hljs-number">111111111111.</span>dkr.ecr.us-east<span class="hljs-number">-1.</span>amazonaws.com/my-ktor-registry:latest
</code></pre><p>Depending on your connection it can take some time, but finally, we should see the update list in our repository: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/14.png" alt="Image shows my-ktor-registry images with one item- latest on the list" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-deploy-the-application-to-aws-app-runner">How to Deploy the Application to AWS App Runner</h2>
<p>Now we have everything we need to share our Kotlin microservice with the world :) </p>
<p>Let's get back to the Management Console and search for app runner: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/15.png" alt="Image presents search results for app runner query" width="600" height="400" loading="lazy"></p>
<p>On the next page, let's hit the <strong>Create service</strong> button.</p>
<p>For the Source configuration, let's choose the <strong>Container registry</strong> along with <strong>Amazon ECR</strong>: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/16.png" alt="Image presents source  and deployment settings for new deployment" width="600" height="400" loading="lazy"></p>
<p>As you might have noticed, AWS App Runner can deploy services directly from a source code repository. If you are interested in such a configuration, just get in touch with me via email (contact[at]codersee[dot]com).</p>
<p>Next, let's click Browse and select <strong>previously created image</strong>: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/17.png" alt="Image presents selected image reposiotory and image tag" width="600" height="400" loading="lazy"></p>
<p>Let's click continue and for the Deployment settings let's choose <strong>Manual</strong> and <strong>Create new service role:</strong> </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/18.png" alt="Image shows deployment trigger, ECR Access role and Service role name" width="600" height="400" loading="lazy"></p>
<p>The role name is not important in this tutorial, so we can specify any value. </p>
<p>As the next step, let's click Next and on the next page, let's provide a <strong>Service name</strong> along with <strong>CPU</strong>, <strong>Memory</strong> and <strong>Port</strong> information:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/19.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image presents Service name, cpu, memory, environment variables, port and additional configuration settings</em></p>
<p>As you can see, we have chosen the minimum available combination (and that's what I suggest you do as well).</p>
<p>If we would like to specify some additional environment variables or a custom Start command, then this page allows us to do so. But we won't need any environment variables and we have already added a start command to our Docker Image, so let's leave it as it is.</p>
<p>On the Auto scaling page, select the <strong>Custom configuration</strong>: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/20.png" alt="Image shows auto scaling settings with custom configuration set " width="600" height="400" loading="lazy"></p>
<p>Next, let's create a new configuration called <strong>my-configuration</strong>: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/21.png" alt="Image shows add custom auto scaling configuration modal" width="600" height="400" loading="lazy"></p>
<p>As I have mentioned in the beginning, AWS App Runner takes care of plenty of things out of the box. One of them is auto scaling. Although it is a great feature, we have to limit it to our preferences and always remember that more resources means higher costs. </p>
<p>As you can see above, this example configuration <strong>will not scale our Kotlin Microservice</strong>. However, if we increase the Maximum size, then a new instance will be created each time the number of simultaneous request increases by 10. </p>
<p>Let's add the above config and leave the rest of items with their defaults. After we click Next, we will see the Review page with deployment summary. </p>
<p>On this page, let's click <code>Create and Deploy</code> button, which will start the deployment process: </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/22.png" alt="Image presents started AWS App Runner deployment process" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/23.png" alt="Image presents deployment logs event called Create service" width="600" height="400" loading="lazy"></p>
<p>And again, this process can take a few minutes. After it finishes, the status will change from <code>Operation in progress</code> to <code>Running</code> and we will be able to test our Ktor REST API.</p>
<p>Just like previously, let's test the <code>GET /hello</code> endpoint. But this time, as a host name of our microservice, we need to use the value from <code>Default domain</code>:</p>
<pre><code>#Example: 

GET https:<span class="hljs-comment">//aaaaaaaaaa.us-east-1.awsapprunner.com/hello</span>

Status: <span class="hljs-number">200</span> OK
Response Body: 
{
    <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello World!"</span>
}
</code></pre><h2 id="heading-important-notice">🛑 Important Notice</h2>
<p>Please remember to <strong>delete all the resources we've created today</strong>, so that you won't be charged for them. </p>
<p>It's really easy to forget about all the things we create when learning AWS Cloud and at some point, you may exceed your free quota. Thus, it is a good practice to remove all of that. </p>
<h2 id="heading-summary">Summary</h2>
<p>And that's all for this tutorial on how to deploy Kotlin Microservice to AWS Cloud with AWS App Runner. I really hope that after following along with this guide, you're able to easily deploy your applications to Amazon Cloud. </p>
<p>If you enjoyed this material, then you might want to check my <a target="_blank" href="https://codersee.com/articles/">other articles</a>. On my blog, I cover plenty of topics related to Kotlin, Ktor, and Spring Boot. </p>
<p>If you would like to ask me about anything, please reach out to me at <em>contact[at]codersee[dot]com</em>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
