Static Site(s) with Kamal

Kamal is an open source tool created by 37signals as part thier cloud exit. That as per their tag line allows you to Deploy web apps anywhere. Quite the statement! But, they also say;

Originally built for Rails apps, Kamal will work with any type of web app that can be containerized.

As you may of noticed they use the term web app. This is great if you have a web app to deploy, but what if you only have a static website to deploy. Is Kamal of no use? You maybe right in thinking its not the tool for you, but you'd be wrong. Kamal is a wrapper around docker, and if you can containerize your static website you can use Kamal to deploy it.

Is Kamal overkill for a static site - possibly. But remember Kamal allows you to deploy anywhere, and which Kamal 2 you can host multiple sites on a single server. You can remove the need to use services like Netlify, Vercel and the host of other services out there. Now don't get me wrong, these services are very easy to use and great for hosting static sites, they have some great developer experience (DX) around them to connect various services together to make deployments happen at the click of a button or with seamless automation.

Moving from one of these services to uisng Kamal does require more work upfront and a different mindset. You are trading a third party hosting your website to hosting your website on your own server.

I used the following steps to deploy a very basic static site, you can see here - https://staticaml.tealeaves.dev.

Prerequisites

For the remainder of this post I am going to assume you already have a static website to deploy, this allows us to focus on setting up Kamal and containerizing the static site.

Containerizing the static site

As per the Kamal website, it will work with any type of web app that can be containerized. Before we get into setting up Kamal we first need containerize our static site. For a static site to be deployed we need to use a web server. There are a number of web servers available, we are going to use nginx.

For the next part I am assuming within your repository you have your static site files within a src directory. Having all the site files within a sub directory will make things a little easier.

In the root of your static site repository create a Dockerfile with the following

FROM nginx
COPY ./src /usr/share/nginx/html

EXPOSE 80

A very basic and minimal docker container.

  1. FROM nginx - Tells docker to use the nginx image
  2. COPY ./src /usr/share/nginx/html - Copies our static site files into the webserver directory nginx serves files from
  3. EXPOSE 80 - Describe which ports your application is listening on

Setting up Kamal

In the root of your repository

You will now have two new folders added to your repository .kamal and config. Within the config folder there will be a file deploy.yml. This file is where all the configuration settings are set, you will be updating this file with your own settings.

Update config/deploy.yml with correct values based on your own accounts.

# Name of your application. Used to uniquely configure containers.
service: staticaml

# Name of the container image.
image: matthewroach/staticaml

# Deploy to these servers.
servers:
  web:
    - 123.456.78.987

# Set ssl: false if using something like Cloudflare to terminate SSL (but keep host!).
proxy:
  ssl: false
  host: my-static-site.com

# Credentials for your image host.
# Default to using docker hub.
registry:
  username: matthewroach
  password:
    - KAMAL_REGISTRY_PASSWORD

# Configure builder setup.
builder:
  arch: arm64

# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
  secret:
    - KAMAL_REGISTRY_PASSWORD

For the environment variable KAMAL_REGISTRY_PASSWORD we need this to be set as an environment variable. To do this we can use .env file to set the secret in. Make sure you add the file to your gitigore to avoid having your secret in git.

Create a .env file with key KAMAL_REGISTRY_PASSWORD and the value being your docker hub personal access token

KAMAL_REGISTRY_PASSWORD="{DOCKER PERSONAL ACCESS TOKEN}"

To get the environment variable available to Kamal config we need to update the config/deploy.yml file to load the environment file using dotenv - add the following to the first line of config/deploy.yml

<% require "dotenv"; Dotenv.load(".env") %>

Be sure to commit all your changes, and then we are ready to deploy our site

Kamal Health Check

As part of the Kamal tooling it requires a health check endpoint (which can be configured), by default it uses /up URL of your site to check if things are health.

Be sure to add a route to your static site. Simply add an index.html file that is served at my-static-site.com/up

Deploying Site

For the first deployment we run kamal setup from the root of our repository. Kamal will do its thing now, running through a number of docker steps, and setting up your server with a proxy and your static site.

For following deployments you'll need to make changes and be sure to commit them with git, and then you can run kamal deploy.

My demo site deployed using Kamal - https://staticaml.tealeaves.dev.


I've already used the same steps on a number of my other static sites to deploy them, and with Kamal 2 I am able to deploy them all to a single server.