
Deploy Axum fast and cheap on a VPS using Kamal. Skip Kubernetes early, keep ops simple, scalable, and production-ready.
title: "How to deploy a simple Axum app to a VPS using Kamal" description: "Deploy Axum fast and cheap on a VPS using Kamal. Skip Kubernetes early, keep ops simple, scalable, and production-ready." tags: ["rust", "systemdesign"] date: "2026-01-10" cover_image: https://kamal-deploy.org/assets/images/kamal.webp
When we first deploy a small or medium-sized Axum app, the priority is to get it running as fast and as cheaply as possible but starting with a complex infrastructure for a project that is still not live or is not big enough is usually a waste of time and resources.
Tools or platforms like Kubernetes, AWS, Google Cloud, Terraform... they all have a learning curve and usually come with a high cost.
In this article, I will show you how to deploy to your own VPS, which is a great option for small or medium-sized projects. It’s cheap, easy to set up, and can be scaled up or down as needed. For this example, we will use Hetzner as our VPS provider, but you can use any VPS provider you like (DigitalOcean, Vultr, Linode...) as long as it supports SSH access.
Kamal is a deployment tool created by DHH, the creator of Ruby on Rails. As stated in their website:
Kamal offers zero-downtime deploys, rolling restarts, asset bridging, remote builds, accessory service management, and everything else you need to deploy and manage your web app in production with Docker. Originally built for Rails apps, Kamal will work with any type of web app that can be containerized.
Many people dismiss Kamal because it was originally built as a deployment tool for Rails apps. However, when inspected more closely, it’s a powerful tool that can be used for any type of web application that can be containerized. It’s a great option for small or medium-sized projects that need a simple, easy-to-use deployment tool, regardless of the framework or language used.
You are looking at one of the articles on my personal blog, please support me by visiting it, thanks!
This is not an article about replacing Kubernetes or Terraform.
Kamal is not trying to solve the same problems as Kubernetes, and this post is not arguing that you should abandon complex platforms for large or highly distributed systems. Kubernetes, Terraform, and similar tools exist for good reasons and are the right choice in many scenarios.
This article focuses on a different problem: deploying a small or medium-sized web application in a simple, cost-effective way, without taking on unnecessary operational complexity before it’s actually needed.
For this article there will be some assumptions:
For the Axum app, any application will work as long as it exposes a health endpoint. We will use Kamal’s default ("/up"), but any other endpoint can be used if it’s correctly configured in the kamal.yml file.
Let’s start by running the following commands from the root of the project:
gem install kamal
kamal init
These commands create a config/deploy.yml file and a .kamal/secrets.yml file with the default configuration (we will ignore hooks for now).
Your .kamal/secrets.yml file can read secrets from environment variables or a password manager (it works with 1Password by default). For this example, we will use environment variables, but keeping your secrets in a password manager is the recommended approach. We will keep the image private on Docker Hub, so we’ll uncomment the following line in the file:
# Option 1: Read secrets from the environment
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
The fun starts in the config/deploy.yml file. Let’s start editing the parts we need for this example.
First, we need to define the service name, the image name, and the IP of the server we want to deploy to:
service: axum-post-example # -> The name of your service, just use the same name you have on your Cargo.toml file
image: my-docker-hub-user/axum-post-example # -> The username on Docker Hub and the name of the image that will be created and stored
servers:
web:
- 116.111.111.111 # -> The ip of the server created on your VPS platform
Then, we need to define the URL where the application will be deployed:
proxy:
ssl: true
host: example.marccamara.com # -> The url where you want your app to be deployed, remember to setup the DNS records
healthcheck:
path: /up # -> /up is the default endpoint, so no need to add this line
After that, we need to define where the newly created image will live, along with the password (if you want your images to be private):
registry:
username: my-docker-hub-user
password:
- KAMAL_REGISTRY_PASSWORD
And that's all!
Now that everything is ready, we can do our first deployment by running:
kamal setup
Wait for the deployment to finish, then check if everything is working by visiting https://your-website.com/up.
Subsequent deployments can be done by running:
kamal deploy
As you may have noticed, Kamal has handled the SSL certificate and proxy configuration for us, which is pretty cool.
We’ve successfully deployed our application. While this setup looks simple, Kamal can do much more: it can deploy databases, deploy to multiple servers, perform zero-downtime deploys, and support multiple environments (for example, by adding a .config/deploy.staging.yml file), among other things.
I’m currently using Kamal for two different projects: one medium-sized setup with Postgres, Valkey and two servers in different locations, and this very personal page running on Leptos with caching.
I may talk about these setups in a future post, but for now, I hope you enjoyed this article and learned something new.
gemmaI ported the whole Gemma-4 family — E2B, E4B, 12B, 31B, and the 26B-A4B MoE — to run on...
communityHey DEV, I'm Tobore. Let's actually connect. I've been on here for a while now, mostly writing and...
ai(yep, kinda clickbait, just for the funsies 😊) At the beginning of the year, I relaunched my...
aiMy laptop was sitting idle with the fan at full tilt. Nothing was running that I knew of. The culprit...
githubactionsI Built a Thing! TL;DR — Google Gemini-based Pull Request reviews and Issue Triaging for...
aiI've been hearing the word "harness" thrown around a lot lately. I assumed it just meant "the IDE" or...
Workflows from the Neura Market marketplace related to this DeepSeek resource