Server provisioning options for Kamal

Kamal is a brand new deployment tool to effortlessly run typical web applications involving web containers, workers, and auxiliary services.

But deploy tools are usually just one part of the story when it comes to self-hosting. Servers usually need to be provisionined before a first deploy.

Do we need another tool to provision Kamal servers? Or is Kamal enough?

What is server provisioning anyway?

Since the deploy step is about delivering the application to servers, provisioning is all the work that has to happen before we can do that.

The provisioning step is about setting up and configuring servers to be able to run our application in the first place.

This usually involves hardware, operating systems, network, storage, and security set up as well as other requirements like monitoring.

Cloud instance provisioning

Most people today run their applications on cloud instances which are half-provisioned already with an operating system, SSH access, and sometimes even with a monitoring agent. This leaves us with things like installing additional system packages, configuring firewalls, and creating swap space.

Kamal provisioning

Kamal is first and foremost a deploy tool. However, it can automatically provision Docker on cloud instances so you can deploy without any additional provisioning steps.

This neat idea is likely based on the assumption that you might not even need anything else when deploying on a private network.

But if you do need more, Kamal won't help you much as there isn't even a proper provisioning hook.

The only thing you can use is the docker-setup hook which runs after Docker is installed and can help you set up Docker networks beforehand.

What to provision

Here's some things you might want to provision:

- Additional users and groups for switching away from the default root. Kamal let's you use a different deploy user under the ssh section of your config/deploy.yml and so you might want to disable root access altogether. Just make sure to give the user sudo privileges and add it to the docker group.

- Local storage and directories to handle Let's Encrypt challenges or to save files on the local filesystem instead of uploading them to object storage.

- Dedicated swap space to give smaller boxes some breathing room when it comes to limited RAM. Remember that deploying new versions of the application will need twice as much memory as usual and it's better to deploy slowly then not at all.

- System firewalls like UFW, firewalld, or iptables to control access to specific ports and from desired networks. This can be complemented by cloud providers firewalls and VPNs.

- Brute force protection systems like fail2ban to help fight abuse and unwelcomed introducers.

- Application and system monitoring collectors to send live performance data to 3rd party systems.

- Log collectors to collect and send logs to 3rd-party systems so they can be viewed together across all servers.

Remember that these are just some examples and you should always think about your specific needs.

Provisioning options

Simple deployments might get away with a little bit of Bash or a simple script while more demanding systems should rely on specific provisioning software.

- Custom Bash and/or Ruby provisioning script similar to one from Kamal Handbook is a quick start for small to medium deployments. Ruby is optional but would let you parse config/deploy.yml as Kamal would see it and iterate through application or auxiliary servers without repeating this information.

- Consist is a new basic server scaffolder originally created as a good companion to Kamal. It also features a specific single-server example.

- Ansible is long-standing project in server configuration and deployment ans someone already made Kamal quickstart.

- Terraform is likely the most popular provisioning tool today and someone already created a Hetzner example that can be used with Kamal.

Kamal configuration for provisioning

You can easily automate reading the server configuration out of config/deploy.yml.

As an example let's see how to read application servers IP addresses as well as those from accessories.

Let's read the configuration file and assign all application hosts to app_hosts:

...
config_file = Pathname.new(File.expand_path("config/deploy.yml"))
config = Kamal::Configuration.create_from(config_file: config_file)
app_hosts = config.roles.map(&:hosts).flatten.uniq
...

And now let's assign all db servers to db_hosts:

...
db_hosts = config.accessories.find { |a| a.name == "db" }.hosts
...

And finally let's find out who is the deploy user:

...
user_name = config.ssh.user
...

Fin

Kamal doesn't support much when it comes to provisioning servers so it can stay a sharp opinionated tool for deploys. And that's a good thing.

We have other good options to choose from. Just keep it simple.