{"id":1885,"date":"2025-05-09T16:39:00","date_gmt":"2025-05-09T13:39:00","guid":{"rendered":"https:\/\/upcloud.com\/global\/us\/resources\/tutorials\/kamal-a-beginners-guide-to-deployment-and-setup-on-upcloud\/"},"modified":"2025-05-09T16:39:00","modified_gmt":"2025-05-09T13:39:00","slug":"kamal-a-beginners-guide-to-deployment-and-setup-on-upcloud","status":"publish","type":"tutorial","link":"https:\/\/upcloud.com\/global\/resources\/tutorials\/kamal-a-beginners-guide-to-deployment-and-setup-on-upcloud\/","title":{"rendered":"Kamal: A Beginner\u2019s Guide to Deployment and Setup on UpCloud"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Kamal is an open-source deployment tool that automates the process of deploying and managing containerized web applications on target servers. These target servers can be provisioned anywhere whether in an on-premises environment, Cloud environment, hosted VPS, or even a simple virtual machine that you create on your own. Kamal was originally created for Rails applications, but it can actually deploy any type of web application that can be containerized.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Kamal makes the application deployment as seamless as possible by abstracting the underlying details of the process and providing a simple configuration to set and a CLI interface to work with. Kamal reads this deployment configuration and applies it according to the required CLI action or command that is provided. After Kamal finishes its work, you\u2019ll have a web application that is running inside a Docker container and ready to receive client requests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Moreover, Kamal offers some out-of-the-box features, including zero-downtime deployments, SSL certificate generation, and multi-arch image builds. It also provides an easy way to deploy additional components \u2013 called accessories \u2013 required by your web application, like databases or message queues.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">While Kamal takes care of the deployment process, you still need a scalable and performant infrastructure layer that hosts and runs your Kamal deployments, and this is what UpCloud does best.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With the industry-leading <a href=\"https:\/\/upcloud.com\/global\/docs\/products\/cloud-servers\/system-architecture\/\">server components<\/a> and extreme performance <a href=\"https:\/\/upcloud.com\/global\/blog\/going-beyond-ssd-with-maxiops\/\">storage<\/a>, you can provision on-demand Cloud servers that satisfy your application requirements. By using Kamal with UpCloud, you get a complete platform for deploying and managing your web applications in a fast, reliable, and scalable way.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this guide, we\u2019ll explain the concepts of Kamal and how it works. We\u2019ll start by installing and configuring Kamal on a Cloud server using UpCloud, and then we\u2019ll go through the step-by-step process of deploying a web application through Kamal. Throughout this guide, you\u2019ll be able to practice the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Install Kamal on a Linux server<\/li>\n\n\n\n<li>Create a deployment configuration for your web app<\/li>\n\n\n\n<li>Deploy your web app using Kamal<\/li>\n\n\n\n<li>Scale your deployment by adding more target servers<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To follow along with this guide, you\u2019ll need to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Create an <\/strong><a href=\"https:\/\/signup.upcloud.com\/\"><strong>UpCloud account<\/strong><\/a><strong>:<\/strong> This gives you access to different UpCloud services using the GUI control panel, <a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/upcloud-command-line-interface\/\">command-line interface<\/a>, or the <a href=\"https:\/\/upcloud.com\/global\/resources\/tools\/category\/api-clients\/\">API<\/a>.<\/li>\n\n\n\n<li><a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/use-ssh-keys-authentication\/\"><strong>Generate an SSH key pair<\/strong><\/a> to use it for authentication to the UpCloud server.<\/li>\n\n\n\n<li><a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/deploy-server\/\"><strong>Deploy a Cloud server on UpCloud<\/strong><\/a><strong>:<\/strong> This is going to be our control server where we\u2019ll install Kamal.<\/li>\n\n\n\n<li><strong>Create a <\/strong><a href=\"https:\/\/docs.docker.com\/accounts\/create-account\/\" target=\"_blank\" rel=\"noopener\"><strong>Docker hub account<\/strong><\/a><strong>:<\/strong> Kamal uses container registries to push and pull images for deployment, the default registry for Kamal is Docker Hub.<\/li>\n\n\n\n<li><strong>Familiarity with using the CLI:<\/strong> We\u2019ll be using some basic commands throughout this guide to install our tools, navigate project directories, and work with SSH.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">What is Kamal?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before diving into the practical scenario, let\u2019s first explain in more detail what Kamal is and how it works.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Kamal enables you to deploy your web app to one or more target servers in an automated way. All you need to do is provide a set of configuration through a YAML file which includes details about how and where you want to execute your deployment. Kamal takes it from there and connects to each of the target servers to start deploying your web app.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Kamal uses containerization for the application deployments, meaning that it will package and deploy your web app to the target server as a container, and not run it directly on the target server OS. You\u2019ll need to provide a Dockerfile along with your web app so that Kamal understands how to build the required container image.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Under the hood, Kamal uses Docker to execute the container deployment steps, including building, pushing, pulling the image, and starting a container from it on the target server. However, Kamal abstracts all the underlying Docker commands so that you don\u2019t have to worry about directly interacting with Docker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After the web app is deployed to the target server, Kamal uses a lightweight HTTP proxy \u2013 called Kamal proxy \u2013 to route traffic for the web app. The specific configuration for the proxy and how it handles the routing is also provided through the YAML file of the deployment configuration. This proxy server is the main component that makes it possible to implement zero-downtime deployments. It checks the health of any new deployed container instance and starts forwarding traffic to it when it\u2019s up and healthy. If the deployment is a new version of a previously deployed instance, it will gradually shift the traffic from the old to the new instance, making sure no outstanding requests are dropped during the process.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/1-kamal-control-target-servers.png\" alt=\"-\" class=\"wp-image-59060\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">It\u2019s worth mentioning that Docker and Kamal proxy are installed automatically by Kamal on the target servers, you only need to install Docker manually on the control server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When comparing Kamal to other containerization platforms like Kubernetes, we can say that Kamal fits small-scale deployments while prioritizing simplicity and abstracting the complexities of the deployment, so you can fast-track running a functional web app in production. While Kubernetes provides more control and customization for every component and fits large-scale deployments, at the cost of adding more complexity and time for the configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Another deployment tool that is usually compared to Kamal is Capistrano. They both have a lot in common, like automated deployment to multiple target servers, support for zero-downtime deployments, rolling restarts, and rollback. However, a key difference between them is that Kamal uses containerization and Docker, while Capistrano relies on native deployments to the target server without using containers. This distinction enables you to decide upon which tool should match your deployment requirements, depending on whether your application is containerized or not.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installing Kamal on UpCloud Servers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now that we\u2019ve an understanding of Kamal and how it works, let\u2019s start our practical scenario by installing Kamal. We\u2019ll be using an Ubuntu Cloud server from UpCloud for the installation. Throughout the rest of this guide we\u2019ll refer to this server as the <strong>control server<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once you\u2019ve got the server running, use the following steps to install Kamal:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1. Connect to the server using SSH. Replace <strong><em>&lt;key-file&gt;<\/em><\/strong> with the path to the private key file that you generated previously, and the <strong><em>&lt;server-ip&gt;<\/em><\/strong> with the IP address of the Cloud server.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ssh -i &lt;key-file&gt; root@&lt;server-ip&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">2. Install Ruby and the required dependencies on the server. Kamal is distributed as a Ruby gem, so you\u2019ll need to have Ruby installed before installing Kamal.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">apt update -y\napt install -y ruby-full build-essential libssl-dev zlib1g-dev make<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">3. Verify the Ruby installation.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ruby -v<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the command output shows the version, this means the installation was successful.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4. Install Kamal using RubyGems.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">gem install kamal<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">5. Verify the Kamal installation.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal version<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The above command output will show the installed Kamal version.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6. <a href=\"https:\/\/docs.docker.com\/engine\/install\/ubuntu\/\" target=\"_blank\" rel=\"noopener\">Install Docker<\/a> on the server. Kamal requires Docker on the control server to build the image, log in to the registry, and push the image.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">7. Verify the Docker installation.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">docker --version<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Configure Application Deployment with Kamal<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After installing Kamal, the next step is to configure our application deployment. Kamal can deploy any application that can be containerized. For our scenario, we\u2019ll use a <a href=\"https:\/\/github.com\/Amr-tmorot\/spring-boot-hello-world\" target=\"_blank\" rel=\"noopener\">Spring Boot<\/a> project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1. Clone the project repository to the control server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">git clone https:\/\/github.com\/Amr-tmorot\/spring-boot-hello-world<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The project is a simple Hello world application, it contains the source code under the src\/ directory, a Dockerfile for containerizing the app, and the pom.xml file for the project dependencies.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/2-kamal-hello-world-files.png\" alt=\"-\" class=\"wp-image-59062\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">2. To start using Kamal with the project, initialize the Kamal configuration with the following command (use the command from within the project root directory):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal init<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will create a default configuration file for the deployment under config\/deploy.yml<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/3-kamal-init-configuration.png\" alt=\"-\" class=\"wp-image-59065\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">3. <a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/deploy-server\/\">Create another UpCloud server<\/a> for our deployment. This is the server which Kamal will connect to and deploy the app container. We\u2019ll refer to this server through the rest of the guide as the <strong>target server<\/strong>. You can use the same SSH key that you created previously in your UpCloud account.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4. Open the <strong><em>config\/deploy.yml<\/em><\/strong> file with a text editor and replace its contents with the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">service: hello-world\n\nimage: &lt;Docker Hub username&gt;\/&lt;repository name&gt;\n\nservers:\n  web:\n    - &lt;target server ip&gt;\n\nproxy:\n  ssl: false\n  host: myapp.spring.com\n  app_port: 8080\n  healthcheck:\n    path: \/\n\nregistry:\n  username: &lt;Docker Hub username&gt;\n  password:\n    - KAMAL_REGISTRY_PASSWORD\n\nbuilder:\n  arch: amd64\n\nssh:\n  user: root\n  keys: [ \"~\/.ssh\/myKey\" ]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s explain the above fields that we\u2019ve added to the file:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>service:<\/strong> this is an arbitrary name that you give to your service. Kamal will use this in the container name that it will create. <strong>image:<\/strong> Kamal will build the container image using the Dockerfile in the project, then it will push it to this repository on Docker Hub. You\u2019ll need to <a href=\"https:\/\/docs.docker.com\/docker-hub\/repos\/create\/\" target=\"_blank\" rel=\"noopener\">create a repository<\/a> on Docker Hub for the image, then replace <strong><em>&lt;Docker Hub username&gt;<\/em><\/strong> with your Docker Hub user and the <strong><em>&lt;repository name&gt;<\/em><\/strong> with the repository you created. <\/li>\n\n\n\n<li><strong>servers:<\/strong> This is a list of servers that Kamal will deploy the app to. The <strong><em>web<\/em><\/strong> key is the role of these servers, which is a way to categorize each group of servers according to which functionality they provide. For example, a web app can contain worker instances that execute background tasks, besides the main web app instances that serve client traffic. In this case, we can create two roles, one for the web and another for the worker servers. Under the role, we provide the IP addresses of the servers for the deployment. Here we only have our target server under the web role, so replace the <strong><em>&lt;target server ip&gt;<\/em><\/strong> with its IP address. <\/li>\n\n\n\n<li><strong>proxy:<\/strong> This is the configuration for the Kamal proxy and how it should handle traffic routing. The <strong><em>ssl<\/em><\/strong> option specifies whether we want to use SSL or not for client traffic. If we choose to use SSL, Kamal will automatically generate the certificate using <a href=\"https:\/\/letsencrypt.org\/\" target=\"_blank\" rel=\"noopener\">Let\u2019s encrypt<\/a>. The <strong><em>host<\/em><\/strong> option specifies a hostname to be used for routing traffic to the destination app container, this means that when the proxy receives traffic that has this hostname provided in the request header, it will forward it to the container. This allows running different app containers on the same target server by separating them with different hostnames. You\u2019ll need to set this hostname in a DNS server and map it to the IP address of the target server so that it can be resolved correctly. The <strong><em>app_port<\/em><\/strong> is the port to which the Kamal proxy will forward the traffic to on the destination app container. This is typically the port number that the application will listen on inside the container. Here we use <strong><em>8080<\/em><\/strong>, which is the default port for spring boot. The <strong><em>healthcheck<\/em><\/strong> option is used by the proxy to verify the health of the app container before starting to send traffic to it. By default, the proxy uses <strong><em>\/up<\/em><\/strong> endpoint to check the app health, but since we only have a single endpoint in our spring boot app, which is the root<strong><em> <\/em>\/,<em> <\/em><\/strong>we\u2019ll use it for the health check instead. <\/li>\n\n\n\n<li><strong>registry:<\/strong> This is the container image registry that Kamal will use to push and pull the images. Kamal uses Docker Hub by default, but you can specify another registry URL if you want. Replace the <strong><em>&lt;Docker Hub username&gt;<\/em><\/strong> with your Docker Hub user. For the <strong><em>password<\/em><\/strong> option, Kamal retrieves the value from the <strong><em>.kamal\/secrets<\/em><\/strong> file. This file is created automatically when we initialize the project with the <strong><em>kamal init<\/em><\/strong> command. It contains variables that shouldn\u2019t be passed directly as plain text in the deployment config file.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/4-kamal-registry-secrets.png\" alt=\"-\" class=\"wp-image-59066\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Here we can see that the value of the <strong><em>KAMAL_REGISTRY_PASSWORD<\/em><\/strong> is retrieved from an environment variable with the same name. This means that we need to set this environment variable on the Kamal control server with the value of the Docker Hub registry password so that Kamal can authenticate to the registry. We can use the command <strong><em>export KAMAL_REGISTRY_PASSWORD=&lt;Docker Hub password&gt; <\/em><\/strong>to set this environment variable. Replace <strong><em>&lt;Docker Hub password&gt;<\/em><\/strong> with your account password or personal access token. It\u2019s always recommended to <a href=\"https:\/\/docs.docker.com\/security\/for-developers\/access-tokens\/\" target=\"_blank\" rel=\"noopener\">use tokens<\/a> instead of passwords for a more secure access to the registry, additionally, using personal access tokens is a must if you have <a href=\"https:\/\/docs.docker.com\/security\/for-developers\/2fa\/\" target=\"_blank\" rel=\"noopener\">two-factor authentication<\/a> enabled.<\/li>\n\n\n\n<li><strong>builder:<\/strong> Kamal supports building container images for multi-architectures like amd64 or arm64, so this option specifies which architecture we want to build our image for.<\/li>\n\n\n\n<li><strong>ssh:<\/strong> Kamal connects to the target servers for deployment using SSH, this option specifies the SSH configuration for the connection like the user and SSH key. You\u2019ll need to provide the path to the SSH private key of your <strong><em>target server<\/em><\/strong>. If you generated the key pair on a local machine and it doesn\u2019t exist on the Kamal control server, you can copy it to the control server using <a href=\"https:\/\/upcloud.com\/global\/resources\/tutorials\/secure-linux-cloud-server\/\">SCP<\/a>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">5. Now that we\u2019ve added our configuration to the file, let\u2019s run our deployment with the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal setup <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This command will connect to the target server, install Docker if it\u2019s not already installed there, deploy the Kamal proxy, and then deploy the app container after building and pushing the image to the registry.<br><br>If we check the image repository on Docker Hub, we\u2019ll see the new image pushed there by Kamal:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/5-kamal-image-docker-hub.png\" alt=\"-\" class=\"wp-image-59068\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now let\u2019s connect directly to the target server and verify the actions that Kamal executed:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/6-kamal-connect-target-server.png\" alt=\"-\" class=\"wp-image-59069\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In the above image, we can see that we\u2019ve Docker installed, although we didn\u2019t install it manually, so this was done by Kamal. Also, we can see that we\u2019ve two containers running, the app container, which contains <strong><em>hello-world-web<\/em><\/strong> in its name, which is derived from the service name and the role in the deployment configuration, and the other container is the <strong><em>kamal-proxy<\/em><\/strong> container. We can also notice that the Kamal proxy listens on ports 80 and 443, while the app container listens on port 8080.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6. Now let\u2019s try to connect to our app from the control server using the hostname we provided in the deployment configuration:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/7-kamal-app-test-connection.png\" alt=\"-\" class=\"wp-image-59070\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">As we can see, we get the <strong>\u201cHello World From Kamal\u201d<\/strong> response, which is set in our Spring Boot source code.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/8-kamal-hello-world-example-code.png\" alt=\"-\" class=\"wp-image-59073\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">So now our app is running and serving traffic successfully!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Running Multiple Applications on the Same Server<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Using the <strong><em>host<\/em><\/strong> option in the Kamal proxy configuration enables us to run multiple applications on the same server. The Kamal proxy will rely on the host header in the request to route the traffic to the appropriate container app.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s try this by using another app, this time it\u2019s a Python Flask app:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1. Clone the project repo on the control server.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">git clone https:\/\/github.com\/Amr-tmorot\/python-flask-hello-world.git<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">2. Initialize Kamal configuration from the project root.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">cd python-flask-hello-world\/\nkamal init<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">3. Modify the config\/deploy.yml with the following contents.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">service: flask-hello-world\n\nimage: &lt;Docker Hub username&gt;\/&lt;New repository name&gt;\n\nservers:\n  web:\n    - &lt;target server ip&gt;\n\nproxy:\n  ssl: false\n  host: myapp.flask.com\n  app_port: 5000\n  healthcheck:\n    path: \/\n\nregistry:\n  username: &lt;Docker Hub username&gt;\n  password:\n    - KAMAL_REGISTRY_PASSWORD\n\nbuilder:\n  arch: amd64\n\nssh:\n  user: root\n  keys: [ \"~\/.ssh\/myKey\" ]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So this is almost similar to the previous app deployment configuration, with the exception of using a different <strong>service name<\/strong>, using a different <strong>image repository<\/strong> for this app image, and using a different <strong>host<\/strong> and <strong>app_port<\/strong> in the proxy configuration. Again make sure you set this hostname in the DNS server and map it to the target server IP address.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">4. Deploy the application using Kamal.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal deploy<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We should see Kamal starting the new application deployment.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/9-kamal-deploy-python-flask.png\" alt=\"-\" class=\"wp-image-59075\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">5. Let\u2019s verify the new container that is running on the target server.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/10-kamal-docker-running-containers.png\" alt=\"-\" class=\"wp-image-59077\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We can see that our Flask app container is running now and listening on port 5000.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">6. Again, let\u2019s use our configured Flask app hostname to test connectivity to our application.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/11-kamal-test-app-output.png\" alt=\"-\" class=\"wp-image-59078\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We can see that we\u2019re getting the <strong>\u201cHello World From another Kamal Application\u201d<\/strong> response that is set in the source code of the Flask app.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/12-kamal-test-app-repository.png\" alt=\"-\" class=\"wp-image-59079\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We\u2019re also able to get the response from the spring boot app depending on which hostname we use in the request, while using the same target server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scaling the Application Deployment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Scaling deployments with Kamal is fairly simple, we only need to provision new target servers and add their IP address to the deployment configuration, then Kamal will prepare them with the required tools and deploy an instance of the app container there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now let\u2019s try to scale our Spring Boot application:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1. <a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/deploy-server\/\">Provision a new UpCloud Cloud server<\/a>. We\u2019ll refer to this one as target server02. Again, you can use the same SSH key you created previously.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2. Modify the Spring Boot deployment file under configure\/deploy.yml by adding the IP address of target server02 to the server list under the web role.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">service: hello-world\n\nimage: &lt;Docker Hub username&gt;\/&lt;repository name&gt;\n\nservers:\n  web:\n    - &lt;target server ip&gt;\n    - &lt;target server02 ip&gt;\n\nproxy:\n  ssl: false\n  host: myapp.spring.com\n  app_port: 8080\n  healthcheck:\n    path: \/\n\nregistry:\n  username: &lt;Docker Hub username&gt;\n  password:\n    - KAMAL_REGISTRY_PASSWORD\n\nbuilder:\n  arch: amd64\n\nssh:\n  user: root\n  keys: [ \"~\/.ssh\/myKey\" ]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">3. Prepare the new target server02 and deploy the app with Kamal.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal setup<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">4. Now if you change the DNS record for <strong><em>myapp.spring.com<\/em><\/strong> to point to target server02 IP address, you should be able to connect to the app container on the new target server02.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/13-kamal-test-app-new-connection.png\" alt=\"-\" class=\"wp-image-59081\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Our target server02 is now running an additional instance of our app container. However, we\u2019re pointing our DNS entry to only one of the target servers at a time.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we want to use both target servers at the same time, we\u2019ll need to provision a load balancer in front of them as follows:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create an <a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/get-started-upcloud-load-balancer\/\">UpCloud Managed Load Balancer<\/a>. Make sure you set up the load balancer in the same location as the target servers.<\/li>\n\n\n\n<li><a href=\"https:\/\/upcloud.com\/global\/docs\/guides\/configure-sdn-private-networks\/\">Create a private SDN network<\/a> to attach the load balancer and the target servers to it.<\/li>\n\n\n\n<li>Modify the DNS entry to point the hostname of <strong><em>myapp.spring.com<\/em><\/strong> to the load balancer hostname, which you can find in the load balancer overview tab in the UpCloud GUI control panel. (This DNS entry type should be an alias or CNAME record.)<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/14-kamal-managed-load-balancer.png\" alt=\"-\" class=\"wp-image-59084\" \/><\/figure>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li>Test the connectivity again to the application.<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/15-kamal-test-app-verbose-output.png\" alt=\"-\" class=\"wp-image-59085\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now our load balancer will distribute the traffic across the two target servers, achieving higher throughput for handling requests and improving availability in case any of the target servers goes down.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Monitoring and Managing Applications with Kamal<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Monitoring is a key aspect of deploying production applications. We always need to track how our application is performing and whether it\u2019s meeting user requirements or not. Depending on the information we get from the monitoring, we might also need to execute operational tasks on our applications. Luckily, Kamal provides some built-in commands that we can use to view information about our running apps and maybe take necessary actions if required.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>To show the app containers that are running on the target servers, we can use the following command:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal app containers <\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/16-kamal-app-containers.png\" alt=\"-\" class=\"wp-image-59088\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We need to issue this command from inside the specific app directory so that Kamal knows which app we are retrieving the information for. As we can see in the above image, the command is showing the Spring Boot app containers on the two target servers (referred to as App Host in the output), it\u2019s also showing older versions of the containers (the ones with Exit Status 137) that have been superseded by a new deployment.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If we discover an issue with a deployment and want to roll back, we can use an older version from the ones that appear in the previous output:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal rollback &lt;older-version&gt;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/17-kamal-roll-back-solution.png\" alt=\"-\" class=\"wp-image-59090\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now, if we check the containers again, we\u2019ll see the latest one was terminated and an additional container is deployed, which is actually the same as the older version:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/18-kamal-list-running-containers.png\" alt=\"-\" class=\"wp-image-59091\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If we want to view only the current running containers, we can use the following command:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal app details<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/19-kamal-hello-world-app-details.png\" alt=\"-\" class=\"wp-image-59093\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We can also view the logs of our app containers on each server with the following command:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal app logs<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/20-kamal-spring-app-output.png\" alt=\"-\" class=\"wp-image-59096\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This is similar to things like <strong><em>kubectl logs<\/em><\/strong> in Kubernetes, which allows us to view the logs generated by our application running inside the container.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Another common task that we might need is to execute some arbitrary commands inside our app containers. We can achieve this by the following Kamal command:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal app exec &lt;command&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will run the specified command inside the app container on all the target servers.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/21-kamal-update-ssh.png\" alt=\"-\" class=\"wp-image-59097\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In the above image, we\u2019re able to get the Java version inside each of our app containers.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>To see the current app version running on the target servers, we can use the following command:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal app version<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/22-kamal-app-version.png\" alt=\"-\" class=\"wp-image-59098\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The random ID in the above output <strong><em>61f3ec000003feba8d2e71e98f16cc7679b849dc<\/em><\/strong> is the app version.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We can stop our running app containers on the target servers with the following command:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal app stop<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If we check our containers now, we\u2019ll see all of them have exited:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/23-kamal-app-containers.png\" alt=\"-\" class=\"wp-image-59101\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We can start them again with the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">kamal app start<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And the latest version should be running again:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"https:\/\/upcloud.com\/media\/24-kamal-app-running-containers.png\" alt=\"-\" class=\"wp-image-59103\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Kamal is an open-source tool that automates the deployment of web applications. It relies on Docker to build a container image for the app and deploy it to multiple target servers. Kamal makes the deployment process as simple and efficient as possible by abstracting the underlying complexity and providing an easy-to-use CLI interface along with a configuration file where you declare how the deployment process should work.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We can use UpCloud servers with Kamal to get a reliable and scalable infrastructure for our deployments. The UpCloud enterprise grade SSD storage and industry leading server components enable extreme performance that satisfy application requirements, making it a perfect fit for Kamal deployments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deploy your web application in minutes now by trying <a href=\"https:\/\/signup.upcloud.com\/\">UpCloud servers<\/a> with Kamal and making use of the scalable and performant infrastructure provided for your container environments.<\/p>\n","protected":false},"author":85,"featured_media":53014,"comment_status":"open","ping_status":"closed","template":"","community-category":[250],"class_list":["post-1885","tutorial","type-tutorial","status-publish","has-post-thumbnail","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/1885","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial"}],"about":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/types\/tutorial"}],"author":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/users\/85"}],"replies":[{"embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/comments?post=1885"}],"version-history":[{"count":0,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/tutorial\/1885\/revisions"}],"wp:attachment":[{"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/media?parent=1885"}],"wp:term":[{"taxonomy":"community-category","embeddable":true,"href":"https:\/\/upcloud.com\/global\/wp-json\/wp\/v2\/community-category?post=1885"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}