From one app to a fleet
The basics course got one application onto one server: kamal setup, a config/deploy.yml with a service, an image, a list of servers, a proxy with SSL, and a database accessory. That configuration is honest, and it stops being enough as soon as real requirements show up.
They usually show up as one of these:
- A second application needs to live on the same server, because a second server costs money you would rather not spend.
- Background jobs outgrow the web process and need their own containers with their own memory limits.
- You want a staging environment that shares the production configuration instead of forking it.
- The build takes eight minutes on your laptop and you would like it to take one.
- A deploy goes wrong at 23:40 and you need the previous version back in under a minute.
None of these need a different tool. Almost all of them are keys in the same file, and this course is about those keys.
The shape of a production host
A Kamal host is more than "your app". It is a small, predictable set of containers:
flowchart TB
I["Internet :443"] --> P["kamal-proxy"]
P -->|"host: nor.app"| A1["nor-web"]
P -->|"host: blog.nor.app"| A2["blog-web"]
A1 --> W1["nor-worker (no proxy)"]
A1 --> DB[("postgres accessory")]
A2 --> DB
One proxy owns ports 80 and 443. Everything else is an app container that the proxy routes to, a role container that nothing routes to, or an accessory that Kamal starts once and leaves alone. Understanding which of those three a container is answers most questions about why it behaves the way it does.
The one file, from above
Almost every capability in this course is a top-level key in config/deploy.yml. The two exceptions sit next to it: a destination adds a small config/deploy.<destination>.yml that is merged over this file, and hooks are scripts in .kamal/hooks. Here is the whole map first:
service: nor # container name prefix — the app's identity on the host
image: gxolivei/nor # what gets pushed to the registry
servers: # roles: which containers run where, with what command
web:
hosts: [ "203.0.113.10" ]
worker:
hosts: [ "203.0.113.10" ]
cmd: bin/jobs
proxy: # routing: which hostname reaches this app
host: nor.app
ssl: true
builder: # how the image is built, and where
arch: amd64
accessories: # services Kamal runs but does not deploy
db:
image: postgres:17
registry: ... # where images live
env: ... # clear and secret variables
aliases: ... # your own commands
The basics course used service, image, servers as a flat list, proxy, registry, env and accessories. This course goes back to servers and makes it roles, back to proxy and makes it multi-app, and adds builder, destinations, hooks and rollback.
What Kamal is still not
Two limits stay true no matter how much of the configuration you learn.
First, Kamal does not schedule. You name hosts. If a host is down, Kamal does not move its containers elsewhere: the deploy to that host fails and the others succeed. At this scale that is a feature, because the deployment stays easy to understand. Scheduling is what you give up when you choose Kamal over Kubernetes.
Second, Kamal does not manage your data. An accessory is a container Kamal starts with a volume. Backups, replication and upgrades of that PostgreSQL are yours. Many teams run the app under Kamal and the database on a managed service for this reason, and that is a legitimate configuration.
What this course covers
- From one app to a fleet (you are here).
- Two apps, one host.
- Roles: web, workers, and the ones with no traffic.
- Destinations: staging and production from one file.
- Builds that do not waste your afternoon.
- Hooks, rollbacks, and the deploy that goes wrong.
- Putting it together: a production configuration.
By the end you will have read and written a deploy.yml that runs two applications and a worker on one host, with a staging destination, a remote builder and a pre-deploy gate. You will know which line does what.
Key takeaways
- The basics configuration is correct and runs out at a second app, a worker, a second environment, slow builds, or a bad deploy.
- A Kamal host is a proxy, app containers, role containers and accessories. Knowing which is which explains most behavior.
- Almost every capability in this course is a top-level key in
config/deploy.yml; destinations add an overlay file, and hooks are scripts in.kamal/hooks. - Kamal does not schedule across hosts and does not manage your data; both are deliberate limits.
Keep going with lesson 2
Two apps, one host
Runs two applications on one host through kamal-proxy, using distinct service names, hostnames, path prefixes and SSL certificates.
A free account also gives you the quizzes, your progress and notes, and the discussion.