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
- A domain name and ability to change DNS records
- Recommend using Cloudflare with SSL termination
- A server, using Hetzner Cloud server at €4.55. Be sure to set up SSH access via public/private key
- You'll need the IP address to the server for Kamal config.
- A container registry account, Docker Hub offers 1 free private repository.
- You'll need to set up a personal access token for docker hub access
- A static website to deploy - source should be within a git repository.
- Docker installed on your machine
- Ruby installed on your local machine (not a requirement to use Kamal, but is how I'll explain using Kamal)
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.
FROM nginx
- Tells docker to use thenginx
imageCOPY ./src /usr/share/nginx/html
- Copies our static site files into the webserver directory nginx serves files fromEXPOSE 80
- Describe which ports your application is listening on
Setting up Kamal
In the root of your repository
- Create a
.ruby-version
file with ruby version -echo "ruby-3.2.2" >> .ruby-version
- Create a
Gemfile
file -bundle init
- Install kamal -
bundle add kamal
- Initialize kamal -
kamal init
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.
service
- a unique name of your applicationimage
- The container image as per docker hub -{docker-username}/{service}
servers.web
- The IP address to the server you want to deploy toproxy.ssl
- Set this tofalse
as we are using Cloudflare to handle SSLproxy.host
- The URL of the site - e.g.my-static-site.com
registry.username
- Your docker hub usernameenv
- Updateenv
config to make the environment variableKAMAL_REGISTRY_PASSWORD
available to container
# 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.