Kamal Deployment
Kamal is a deployment tool that deploys Docker containers to any server via SSH — no special cloud infrastructure required. It handles zero-downtime deploys, SSL via its built-in kamal-proxy, and rolling updates.
For other deployment options
For Docker Compose on a single server, see the Docker Compose guide. For managed PaaS hosting, see the Heroku guide.
Prerequisites
- One or more servers running Ubuntu 22.04+ (or compatible Linux)
- SSH access to your server(s) as a non-root user with sudo privileges
- A domain name with DNS pointing to your server
- Docker installed on your local machine (Kamal installs Docker on remote servers automatically)
- Ruby installed locally (for the Kamal gem), or use the Kamal Docker image
Install Kamal
gem install kamal
Or use the Docker wrapper without installing Ruby:
alias kamal='docker run -it --rm -v "${PWD}:/workdir" -v "${SSH_AUTH_SOCK}:/ssh-agent" -e "SSH_AUTH_SOCK=/ssh-agent" -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/basecamp/kamal:latest'
Configuration
Create a config/deploy.yml at the root of your project:
service: open-chat-studio
image: your-registry/open-chat-studio
servers:
web:
hosts:
- your-server-ip
options:
expose: "8000"
workers:
hosts:
- your-server-ip
cmd: celery -A config worker -l INFO --pool gevent --concurrency 100
beat:
hosts:
- your-server-ip
cmd: celery -A config beat -l INFO
ssh:
user: deploy # non-root user with sudo privileges
builder:
arch: amd64
driver: docker # avoids https://github.com/docker/buildx/issues/1519
registry:
server: your-registry-server
username: your-registry-username
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
DJANGO_SETTINGS_MODULE: config.settings_production
DJANGO_ALLOWED_HOSTS: yourdomain.com
CSRF_TRUSTED_ORIGINS: https://yourdomain.com
secret:
- SECRET_KEY
- DATABASE_URL
- REDIS_URL
- CRYPTOGRAPHY_KEY
- CRYPTOGRAPHY_SALT
- MAILGUN_API_KEY
accessories:
postgres:
image: pgvector/pgvector:pg16
host: your-server-ip
directories:
- data:/var/lib/postgresql/data
env:
clear:
POSTGRES_USER: open_chat_studio
POSTGRES_DB: open_chat_studio
secret:
- POSTGRES_PASSWORD
redis:
image: redis:7
host: your-server-ip
directories:
- data:/data
proxy:
ssl: true
host: yourdomain.com
app_port: 8000
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Secret management
Store secrets in a .kamal/secrets file (do not commit this to git):
SECRET_KEY=your-secret-key
DATABASE_URL=postgres://open_chat_studio:yourpassword@open-chat-studio-postgres:5432/open_chat_studio
REDIS_URL=redis://open-chat-studio-redis:6379
CRYPTOGRAPHY_KEY=your-cryptography-key
CRYPTOGRAPHY_SALT=your-cryptography-salt
MAILGUN_API_KEY=your-mailgun-api-key
POSTGRES_PASSWORD=yourpassword
KAMAL_REGISTRY_PASSWORD=your-registry-password
Add .kamal/secrets to your .gitignore.
Accessory hostnames
Kamal accessories are reachable from your app containers using the hostname <service>-<accessory> — e.g. open-chat-studio-postgres and open-chat-studio-redis. Use these in your DATABASE_URL and REDIS_URL.
See Configuration Reference for all available environment variables.
Add a Database Migration Hook
Kamal supports deploy hooks. Create .kamal/hooks/pre-deploy to run migrations before each deploy:
#!/bin/bash
set -e
kamal app exec --reuse 'python manage.py migrate --noinput'
Make it executable:
chmod +x .kamal/hooks/pre-deploy
First Deploy
# Bootstrap servers (installs Docker, sets up kamal-proxy, starts accessories)
kamal setup
# Create a superuser after the first deploy
kamal app exec -i 'python manage.py createsuperuser'
Then log in at https://yourdomain.com/admin/ and create a Team.
Subsequent Deploys
kamal deploy
This builds a new image, pushes it to your registry, and performs a rolling restart with zero downtime.
Useful Commands
# View logs
kamal app logs
kamal app logs --roles workers
# Open a console
kamal app exec -i 'python manage.py shell'
# Run a management command
kamal app exec 'python manage.py <command>'
# Check container status
kamal app details
# Restart a specific role
kamal app restart --roles beat
# Accessory management
kamal accessory boot postgres
kamal accessory logs redis
Multi-server Setup
To spread processes across multiple servers, list additional hosts under each role:
servers:
web:
hosts:
- 10.0.0.1
- 10.0.0.2
workers:
hosts:
- 10.0.0.3
Warning
Run the beat role on exactly one host. Multiple instances will cause duplicate scheduled tasks.