Kamal
Table of Contents
Deploy Rails applications
I recently deployed a Rails application to Hetzner and AWS China1. Deploying on Hetzner is simple, requiring just two steps: launching a server and deploying. In contrast, deploying on AWS China involves extra steps because Docker Hub is not accessible from servers located in China.
The steps are:
- Launch an Amazon EC2 instance
- Create Amazon ECR repositories to store Docker Hub images
- Create an IAM user and assign only the “AmazonEC2ContainerRegistryFullAccess” permission
- Push Docker Hub images to Amazon ECR on local machine
- Pull images from Amazon ECR on Amazon EC2
- Deploy
Push Docker Hub images to Amazon ECR on local machine
-
Login ECR
aws configure aws ecr get-login-password | docker login --username AWS --password-stdin <aws-ecr-domain> -
Push
basecamp/kamal-proxyimagedocker pull basecamp/kamal-proxy:v0.9.0 --platform linux/amd64 docker tag basecamp/kamal-proxy:v0.9.0 <aws-ecr-domain>/basecamp/kamal-proxy:v0.9.0 docker push <aws-ecr-domain>/basecamp/kamal-proxy:v0.9.0 -
Push
pgvector/pgvectorimagedocker pull pgvector/pgvector:pg17 --platform linux/amd64 docker tag pgvector/pgvector:pg17 <aws-ecr-domain>/pgvector/pgvector:pg17 docker push <aws-ecr-domain>/pgvector/pgvector:pg17
Pull images from Amazon ECR on Amazon EC2
-
Install Docker
sudo apt update sudo apt install -y docker.io sudo usermod -aG docker $USER -
Install AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" sudo apt install -y unzip unzip awscliv2.zip sudo ./aws/install -
Login ECR
aws configure aws ecr get-login-password | docker login --username AWS --password-stdin <aws-ecr-domain> -
Pull
basecamp/kamal-proxyimagedocker pull <aws-ecr-domain>/basecamp/kamal-proxy:v0.9.0 docker tag <aws-ecr-domain>/basecamp/kamal-proxy:v0.9.0 basecamp/kamal-proxy:v0.9.0 -
Pull
pgvector/pgvectorimagedocker pull <aws-ecr-domain>/pgvector/pgvector:pg17
Deploy
- Run
kamal setupthe first time to setup everything. - Run
kamal deployfor subsequent deployments.
Self-hosting
Since this pr, you can use kamal-proxy to forward requests to accessories, it’s time for self-hosting!
For example, I use Atuin2 for shell history, and it supports syncing shell history through a sync server. Thankfully, the sync server can be self-hosted using Docker, and there is a Docker Compose example. The task is to transform the example to a Kamal configuration:
service: atuin
image: atuin
accessories:
db:
image: postgres:14
host: &host hetzner
env:
clear:
POSTGRES_DB: atuin
POSTGRES_USER: atuin
secret:
- POSTGRES_PASSWORD
directories:
- ./data:/var/lib/postgresql/data/
server:
image: ghcr.io/atuinsh/atuin:v18.10.0
host: *host
cmd: server start
env:
clear:
ATUIN_HOST: 0.0.0.0
ATUIN_OPEN_REGISTRATION: false
RUST_LOG: info,atuin_server=debug
secret:
- ATUIN_DB_URI
proxy:
host: atuin.yejun.dev
app_port: 8888
ssl: true
healthcheck:
interval: 1
timeout: 1
path: "/"
The highlights adds the proxy configuration for the server accessory, telling kamal-proxy to forwards the HTTP requests to the server container’s app_port.
kamal-services includes all my self-hosting services.
Tips
kamal-proxy
You could ssh into the host with a running kamal-proxy, and execute the following command to see the proxied services:
docker exec kamal-proxy kamal-proxy list
For example, let’s see the Atuin service:
$ docker exec kamal-proxy kamal-proxy list
Service Host Path Target State TLS
atuin-server atuin.yejun.dev / 41fda841ea5c:8888 running yes
$ docker ps --filter "name=atuin*" --format "table {{.ID}}\t{{.Image}}\t{{.Names}}"
CONTAINER ID IMAGE NAMES
41fda841ea5c ghcr.io/atuinsh/atuin:v18.10.0 atuin-server
551f7d61d1ac postgres:14 atuin-db