Notes to self

Using non-root users in Kamal

Kamal gives us an option to connect with a non-root user, but how can we create it?

Creating a non-root user

If you use cloud VM providers like Hetzner or Digital Ocean, your virtual private server with likely come with root only. And Kamal unfortunately cannot create additional users automatically.

To change root to our own user, we can take advantage of the SSH key of the root user and use it to set up a new user. We can do that with a provisioning tool, script, or issuing the following commands after logging in to the server:

$ ssh root@public-ip

As a first step, we create the user itself with a home directory where we’ll install the SSH keys:

useradd --create-home username
usermod -s /bin/bash username

We also select a default shell (here bash) which will make the future connections more comfortable.

Then we can create the ~/.ssh/authorized_keys file and move the already existing root key (installed by our cloud provider). If we do that using the just created user (using su) we’ll get the right ownership:

su - username -c 'mkdir -p ~/.ssh'
su - username -c 'touch ~/.ssh/authorized_keys'
cat /root/.ssh/authorized_keys >> /home/username/.ssh/authorized_keys
chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/authorized_keys

At the end we are finishing it up by setting the expected access rights (only read and write for this user).

Finally, we need to ensure that we can still make all the administrative tasks on the servers. So we make the user to be able get root privileges with sudo and make him part of docker group:

echo 'username ALL=(ALL:ALL) NOPASSWD: ALL' | tee /etc/sudoers.d/username
chmod 0440 /etc/sudoers.d/username
visudo -c -f /etc/sudoers.d/username
usermod -aG docker username

Note that this configuration really make the user as powerful as root. You could also directly limit the actions in the /etc/sudoers.d/username file for finer control.

The steps mentioned can be used in Ubuntu 22 and 24, and likely many other systems.

Kamal config

Changing the SSH user in Kamal simple. Providing the user under the ssh setting will make Kamal always connect with this user from now on:

# config/deploy.yml

ssh:
  user: username

Implications

While changing the user is simple, it still comes from some implications. Since Kamal doesn’t know anything about issuing commands with sudo, Kamal won’t be able to cleanly remove directories that don’t have write access for this specific users.

This can happen when creating a PostgreSQL directory which ends up using its own user as the owner:

sudo ls /home/username/app-postgres -all
drwx------ 19        999 username 4096 Oct 11 14:06 data

Now it isn’t a real problem per-se, but kamal remove will now fail trying to delete the directory.

To fix issues like this we need to create these kind of directories with write and execute bits for the user beforehand. Adding them for the group here would make this work.

Disabling root

Once the configuration works as expected, we might want to disable the root login altogether.

To do that we need to change the PermitRootLogin SSH daemon configuration and restart the service:

sed -i 's@PasswordAuthentication yes@PasswordAuthentication no@g' /etc/ssh/sshd_config;
sed -i 's@PermitRootLogin yes@PermitRootLogin no@g' /etc/ssh/sshd_config;
systemctl restart ssh

I am also disabling password authentication while I am at it. We should use only SSH keys.

Check out my book
Learn how to use Kamal to deploy your web applications with Kamal Handbook. Visualize Kamal's concepts, understand Kamal's configuration, and deploy practical life examples.
by Josef Strzibny
RSS