Home Tutorials Deploy a simple web app with Kamal

Deploy a simple web app with Kamal

Last updated on Feb 06, 2025

Alert: American Cloud doesn't allow 'root' as default. Only users that have a full understanding of their environments and the security implications should use this guide.

Create an Instance with Start Up Script

1.) In the left navigation pane select "Cloud Compute"

2.) Select "CREATE AN INSTANCE"

3.) Select the project to build in and select "Proceed"

4.) Select between "US-West-0" and "US-West-1"

  • If wishing to build on our premium stack select "US-West-0"

5.) Select between Standard or Premium in "US-West-0" and Standard in "US-West-0"

6.) Select the desired OS/Marketplace App.

7.) Provision your VM with our custom or default options.

8.) Select "Add a new Startup Script"

9.) Add this startup script to the block. Ensure to replace "mypubkey" with your actual pubkey leaving the quotations in place.

#!/bin/bash
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
SSH_KEY_CONTENT="mypubkey"
echo "$SSH_KEY_CONTENT" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
chown root:root /root/.ssh/authorized_keys
systemctl restart sshd
echo "SSH configuration for root updated. Root login now permitted with specified key."

10.) Select "Add startup script"

11.) Give the instance a customized hostname and label.

12.) Select "Deploy Now".

Confirm Root SSH is enabled

ssh using root to confirm access

ssh root@'publicip'

Install Kamal

13.) Now that root ssh access is established on the VM. It's time to install Kamal. There are a couple of prerequisites prior.

  • Docker and buildx is required on your machine. This tutorial is built on Mac so brew install docker and brew install docker-buildx was utilized. If docker and buildx is not installed a failure will occur during kamal setup. This step may be different depending on OS. It a relatively quick lookup.

  • The private key matching the public one on your VM should be added to your ssh-agent, you can ensure this is the case by running ssh-add ~/.ssh/kamal_privkey (whatever your key is)

14.) Install Kamal locally by running gem install kamal or set up an alias to run in docker.

  • If issues arise in step 14, you'll probably need to update ruby and set the ruby environment.

15.) Choose your container registry (it can be public or private), and create a personal access token with write:packages scope in order to push images to it. We are going to use ghcr.io and a private registry for this example.

16.) Select user menu in top right corner.

17.) Select "Settings"

18.) Scroll to the bottom of the menu and select "Developer settings"

19.) In the next menu select "Personal access tokens". Then in the dropdown select "Tokens (classic)"

20.) Select "Generate New Token" followed by "Generate new token (classic)" from the dropdown.

21.) In the section provide a name for the token and at a minimum select "write:packages"

22.) Select "Generate token"

23.) Copy the key to be utilized in the next steps.

24.) Set your personal access token as KAMAL_REGISTRY_PASSWORD using the export command below:

export KAMAL_REGISTRY_PASSWORD=ghp_12345abcde

25.) Create a directory for kamal in the location you'd like to run it. For my example I'm simply creating on my ~/Desktop utilizing mkdir kamal

26.) Set up your code, if you haven't already. Make sure you include a Dockerfile and that your app returns a 200 ok on the path /up

To test we can use some sample code. Inside the kamal directory create two files Dockerfile and server.ts

27.) server.ts

const server = Deno.listen({ port: 80 });
console.log("Server running on http://localhost:80");
for await (const conn of server) {
  handleConnection(conn);
}
async function handleConnection(conn: Deno.Conn) {
  for await (const requestEvent of Deno.serveHttp(conn)) {
    const url = new URL(requestEvent.request.url);
    requestEvent.respondWith(new Response("Hello, Kamal!", { status: 200 }));
  }
}

28.) Dockerfile

FROM denoland/deno:latest
WORKDIR /app
COPY server.ts .
EXPOSE 80
CMD ["deno", "run", "--allow-net", "server.ts"]

29. (Optional) Skip this step if you are already using git. If your code is not already committed with git, you can continue by simply using git locally by running these commands

git init
git add .
git commit -m "Initial commit"

30.) Initialize kamal by running kamal init from the kamal directory.

31.) Update your newly created config/deply.yml file located in the kamal directory with the below code. Consult the Kamal docs for more options. Ensure to change line 4&13 to reflect the username of the repository. Line 8 will change to server Public IP.

# Name of your application. Used to uniquely configure containers.
service: kamal-demo
# Name of the container image.
image: github-username/kamal-demo
# Deploy to these servers.
servers:
  web:
    - 192.168.0.0 #<-- Put your VM's public IP here
# Credentials for your image host.
registry:
  # Specify the registry server, if you're not using Docker Hub
  server: ghcr.io
  username: github-username
  # Always use an access token rather than real password (pulled from .kamal/secrets).
  password:
    - KAMAL_REGISTRY_PASSWORD
# Configure builder setup. Make sure you use this if you are building on a Mac.
builder:
  arch: amd64

This mapping should already be present but to double-check that your .kamal/secrets file includes this mapping run: KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD

32.) Commit your file changes to git

  • That's it! You're ready to deploy your app.

33.) Run kamal setup to begin the build on the host machine.

34.) Verify your app is running on your VM(s) by logging into your VM and checking the app

curl -X GET "http://localhost:80/"

This should return a Hello, Kamal!

It's important before making your app public to utilize system hardening techniques as they're not installed by default. Click here for a good article of reference.