Following kamal deploy From SSH to Proxied Requests: Josef Strzibny's Kamal Deep Dive
Ruby on RailsJosef Strzibny calls themself a "Kamalholic." It started with deploying a few apps and writing a blog post. Then came the first book on Kamal, a video course, a magazine article, and a series of conference talks. In this Rails World session, Strzibny asks what actually happens when you type kamal deploy, and follows that command through a set of "dives": SSH and Docker, building and pushing images, registering services with Kamal Proxy, zero-downtime deploys across multiple hosts, and finally into the Go code that proxies requests.
Strzibny also mentioned joining SerpApi, which provides live search data as JSON or Markdown, and working on SerpTrail, an SEO checker that "last time I checked" still ran on Rails, not Rust, and ships with a Kamal configuration. The talk was framed by stories from a first trip to America: an In-N-Out order meant to be "a portion for one" that needed help from colleagues to finish, and an impulse drive in a Ferrari California in Hollywood after signing a waiver without reading it.
How Kamal sees an application
Before going deep, Strzibny laid out Kamal's mental model. Kamal is built around a Docker image of your application. The image runs in roles. The primary one is the web role, for example Puma serving your Rails app. Additional roles can run jobs or other processes from the same image. Accessories are separate services such as a database or a headless browser. They have different life cycles from the app roles, and the talk did not cover them.
There are two separate pieces of software involved. The Kamal CLI runs on your laptop when you type kamal deploy and issues commands to servers over SSH. Kamal Proxy runs on the servers themselves. The rest of the talk follows how the first drives the second.
Dive one: Thor, SSHKit, and commands as arrays
Strzibny said their favorite Kamal feature is that it prints every command it runs, so you can see exactly what is happening. To understand where those commands come from, they started with the CLI layer. Kamal uses Thor, the same library Rails uses for its command line. Kamal subclasses Thor and implements its commands. kamal deploy is made up of many subcommands that can also be called individually from the command line. Inside the code, they are chained through Thor's invoke method.
Actual remote execution goes through SSHKit, which Rubyists may remember from Capistrano. Kamal adds a small patch to SSHKit and a small DSL on top. SSHKit already has a DSL for running code on hosts and locally. Kamal extends it so it can target its own roles. The flow is: Kamal configuration goes in, and in the end mostly Docker commands are run on the servers. The code that produces those commands lives in Kamal's Commands namespace.
A simple example is a Docker version check to see whether Docker is installed. A more complex one is a full docker run. Strzibny pointed out that the Ruby code for these almost reads like shell, with a mix of symbols, strings, and method calls. That is because the docker helper just builds an array with the :docker symbol at the front. Kamal registers docker as an SSHKit command. According to Strzibny, this means SSHKit skips its usual /usr/bin/env wrapping, so the logs come out cleaner, even though simply shelling out to Docker would also work.
Sensitive values are handled with a dedicated "sensitive" class. When it is converted to a string, for example to go into a real command, you get the unredacted value. When it is inspected, as in log output, you get a redacted placeholder. The registry password is one value treated this way.
Because commands are arrays of tokens, combining them is just array manipulation: compacting, flattening, and joining. A shell pipe is the same idea, with the arrays joined by a | element. There are helpers for wrapping something in a new shell, similar to sh -c. Strzibny's example was building a Docker install command by piping the result of fetching the get-docker script into sh. The whole thing is still just arrays. Kamal keeps commands as arrays as long as possible and only converts them when it has to hand them to SSHKit.
When reading Kamal's source, you will see execute. This comes from SSHKit's Net::SSH backend and is what actually runs the command. The splat (*) is there because it accepts positional arguments, so the command array is passed in that way. Strzibny also explained why Kamal has a global KAMAL constant. Thor and SSHKit both use instance_exec, so self is lost inside those blocks. Kamal still needs to reach global configuration, so it exposes it through a constant.
Concurrency across hosts: the SSHKit patch and connection pooling
Running on many hosts at once brings up Kamal's SSHKit patch. Normally SSHKit runs threads and simply joins them all. Kamal still joins the threads, but it rescues exceptions from each one. Strzibny explained the goal: run the code on every host and collect the errors, rather than stopping at the first failure.
Threads are only part of the picture. TCP connections are expensive to create, so Kamal shares them. Many small commands, from docker login to docker stop, run across concurrent roles, and they can reuse connections from a pool instead of opening new ones each time.
Dive two: building and pushing images
The image itself is built by BuildKit, part of Docker or, more precisely, the Moby project that is upstream of Docker. Kamal runs docker buildx build. Strzibny called buildx "a bit weird" because it is a plugin to Docker, and that brings in builder drivers. The docker driver is the original, simple docker build. Kamal uses the docker-container driver because it needs more: multiple architectures, exports, multi-node builds, and so on.
A "builder" is a named instance of that driver. When you configure a builder in Kamal's YAML file, that is what you are naming. Strzibny admitted the naming is confusing at first, and walked through the chain. kamal deploy builds the image by running docker buildx build. That uses the buildx plugin, which starts a new container (hence the docker-container driver name). BuildKit inside that container runs your Dockerfile and produces the image. On a remote builder, the process is exactly the same. Kamal only needs Docker to be pre-installed on that host.
Registries, including a local one
Part of the deploy is uploading the image to a registry: Docker Hub, another provider, or, the case Strzibny found most interesting, a local registry. A local registry is just an extra container running the standard registry image under a Kamal-specific name. There is nothing special about it except for one question: how do remote servers reach a port on your laptop?
Strzibny compared it to local port forwarding, which you might use to expose a server's database port on your machine for inspection. Here it runs in the opposite direction, as remote forwarding, conceptually like ssh -R. Kamal does not shell out to ssh -R. It implements the forwarding in Net::SSH, but the idea is the same.
For the push, Kamal uses the same docker buildx build command, which builds and then pushes. For the pull, once the port forwarding is in place, the servers can see the laptop's registry port and pull the image directly.
Service registration with Kamal Proxy
Kamal has its YAML configuration file, but Kamal Proxy has no configuration file of its own. Each instruction to the proxy is a docker exec issued by the Kamal CLI. Strzibny described this as consistent with Kamal's imperative style. The core command is kamal-proxy deploy.
Since everything is passed in a single docker exec, every setting has to be passed as a flag: TLS, logging, buffering, and anything else. The shape of the command is docker exec kamal-proxy (run something in the proxy container), then kamal-proxy (the binary), then deploy with a service name such as web, then the flags. The most important flag is the target, the container ID and port to send traffic to.
Because configuration arrives imperatively, there is the question of what happens if the proxy goes down. Kamal Proxy keeps its state in a named configuration volume and reloads it on restart. Besides deploy, the proxy supports other service actions: removing a service, stopping it (maintenance mode), and resuming it.
Zero-downtime deploys on a single host
Strzibny called zero-downtime deployment essential to how Kamal works. The mechanism goes back to the same kamal-proxy deploy command. For a new version, Kamal runs it again with the same service name and a new target, meaning a new container ID and port. The new container is started first. Once it is running, the proxy can switch over.
Kamal uses blue-green deployment. In the simple single-host case, the new web container (Puma) boots, it is handed to Kamal Proxy, the proxy runs a health check, and once that passes, traffic switches to the new "green" container. The old container's in-flight requests are drained.
Assets need extra care because two versions are live at once. Kamal unpacks assets into the same directory and copies them into volumes for both the old and new containers. It uses a no-clobber copy so files that were copied first are not overwritten. As long as the order is right, both containers can serve both sets of assets. Strzibny noted that assets must be fingerprinted for this to work.
The deploy lock is "even easier." To stop concurrent deploys from interfering with each other, Kamal creates a directory on the server, and that directory is the lock.
Coordinating many hosts: the barrier
With multiple hosts, the same flow needs coordination. For this, Strzibny went "even deeper to the Great Barrier Reef" to look at Kamal's barrier, also called the web barrier, named for the role it protects. It lives in the health check namespace and is built on a concurrent "IVar," a variable that can be set only once. The barrier is either open or closed. When setting it, errors are ignored, because only the first set to true counts.
The process works like this. Kamal starts booting containers for the web role. The first one to boot correctly opens the barrier, and then everything else is allowed to boot. Only one healthy web container is needed before the rest proceed, and each switch happens as in the single-host case. In code, a non-web role waits at the barrier before starting its new version. A web role acts as the gatekeeper and releases the barrier.
Starting a new version does what you would expect. It uploads the environment with the new secrets, runs the new container, and, if a proxy is in use, has the proxy perform the switch with the proxy deploy step.
For rolling deploys, Kamal slices the servers into batches, which corresponds to the limit setting in the config. In code, the limit can be a number or a percentage.
Dive three: proxying requests in Go
The last dive was into Kamal Proxy, which Strzibny acknowledged is "Go territory" and not the usual subject at a Rails conference, before offering to "teach you Go just now." The two features Go was known for at launch were goroutines and channels.
Goroutines are concurrent functions scheduled by the Go runtime across available threads, on one CPU or many. They are started with the go keyword. Strzibny joked that this is maybe why the language has that name. Go's net/http package, simplified, does something like go serve for each new connection. In Kamal Proxy's own code, however, you will not find the go keyword in the request path at all. It relies on net/http, runs its middleware, and then calls a handler. That handler finds the right service for the incoming request and serves it.
Channels are how goroutines coordinate. Strzibny described them as roughly a first-in, first-out queue. Goroutines can write to them or read from them. You do not know which goroutine will pick up a given value, but when a channel is closed, every goroutine waiting on it finds out.
Health checks, buffering, draining, and maintenance mode
Health checks are the first channel-based example. A ticker sends a value on its channel (ticker.C) at the configured interval, and each tick triggers an actual check. The code creates a new ticker with the interval from the configuration, then loops forever around a select statement. Strzibny noted that select is everywhere in Go code. Each case waits for a new value on some channel. Here, a value on ticker.C means "run the check now."
Kamal Proxy also buffers requests, as a protection against slow clients. Part of a large request is held in memory and part is written to disk. Either way, the request is buffered completely and then sent on to Puma.
Draining uses the same select pattern. For each in-flight request, either the request context finishes, in which case it is allowed to complete, or a deadline is reached, in which case it is cancelled. Strzibny walked through the drain function. Hijacked in-flight requests, meaning WebSockets such as Action Cable connections, are killed immediately without further handling. For everything else, a labeled block waits in parallel for each request to either complete or hit the deadline. If the deadline is hit, execution breaks out of the labeled block, and any remaining requests are cancelled with a draining error.
Maintenance mode was the last example, and one Strzibny found especially interesting. It uses a "pause" channel. While paused, request goroutines wait on that channel. When the pause ends, the channel is closed, and every waiting goroutine sees it and continues. No value is ever sent. Closing the channel is the signal. Inside the request path, there is again a select waiting on the relevant channels.
What the dives add up to
Strzibny summarized the journey. Kamal is built on Thor and SSHKit. It keeps commands as arrays until the last moment, when they must be executed. It registers services with Kamal Proxy through imperative docker exec commands, which enables blue-green deploys, coordinated across many servers by the barrier. Kamal Proxy's request handling, health checks, draining, and maintenance mode come down to goroutines, channels, and select.
Strzibny closed by admitting they cannot stop talking about Kamal. They recalled explaining to someone at a California counter that Kamal is a simple deploy tool that can replace a Kubernetes cluster and save a lot of money, only to be told "Sir, this is a Wendy's." They ended with a promise to stick to Texas barbecue and a proper American muscle car instead of a Ferrari, and a final "long live Kamal."
Hi, I'm Josef and I'm a Kamalholic. Nothing seemed wrong at first. I just deployed a few apps and wrote a blog post. But it got worse.
I wrote the first ever book on Kamal and I even made a video course of this really close boat video. And it became a serious condition.
I was here this year published in iX magazine with my Kamal article. And I started to give Kamal talks like the one in wroclove.rb where I shared some not so known facts about Kamal.
And this brings me to Austin, Texas, to here. But my journey to Austin, Texas started earlier because I joined a new company. Of course the company is SerpApi. If you don't know what we do, we give live search data for your AI as JSON or as Markdown. You can have it your way.
I also work on an application called SerpTrail. It's like an SEO checker. And last time I checked it was still on Rails, not in Rust. You can check it out. It also comes with Kamal configuration.
And this brings me also to the topic of today, my first time in America. Now this is not an ice slope, this is two sizes of coffee in America.
Some of you know that I went to a conference, but we shall not speak its name. And it was in LA, California.
So if you were at my last talk, you know that I'm an unapologetic fan of Chef Gordon Ramsay, who is a celebrity chef. And why I mention him for my talk is because we know that America is known for one thing, and it's fast food. And there is a secret with Chef Gordon Ramsay that he actually... Yeah.
I actually don't like him for the fancy stuff and fancy dining, but for all the wrong reasons. And he likes fast food too, right? He likes In-N-Out Burgers. And because it's a chain in California, I had to go and try it.
So I ordered essentially a portion for one. And when in Rome, you should also finish. But no, I actually got help by my colleagues from SerpApi.
And after the conference, I went to Hollywood, of course, because you are in LA, right? I was walking this pavement with the stars, and then I saw that you can actually drive a Ferrari like a movie star, right? I thought, what a tourist trap. But actually after a closer look, they had Ferrari California as a model there. So I got sold, you know?
But there was one problem. They give you this very scary-looking waiver to sign, and I was like, "Josef, if you are going to read it, you are not going to sign it." And so I didn't. I didn't read it. I just signed it.
And next thing I know, I was sitting in a Ferrari for the very first time. And I realized that maybe I also have an addiction for expensive cars. But after chatting with ChatGPT about my financials, I decided I will just stay with the addiction I can afford.
And I will take you today with me to explore Kamal a little bit. So we'll do a couple of deep dives. We'll of course explore the most interesting command, which is kamal deploy. And we will go all the way from SSH to proxying requests, yeah?
Now if you are here by accident, you don't even know what Kamal is, it's important to know how Kamal sees your application with the application roles and accessories.
So Kamal of course uses Docker, so you have your Docker image, and then you would have, for example, running Puma as your web server. That would be your first role, the so-called web role, the primary role. And then you can have more roles like for jobs or for something else.
And then there are some other accessories like your database or headless browser or whatever. And they have different life cycles. So that's not what we will really talk about.
It's also important to realize that we have Kamal CLI and Kamal Proxy that will run on your server. So you are running kamal deploy on your laptop, and then we have that over SSH running some commands, Kamal Proxy of course running there.
So let's go for the first dive. That's SSH and Docker. If you already deployed with Kamal, now you're seeing the screen. It's my favorite feature of Kamal because you see everything that's happening, but even before you actually run the command.
Of course we need something to translate that command to code. So for that, we use Thor in Kamal. If that sounds familiar, yes, it's also used in Rails. So Kamal simply subclasses from Thor and then implements the commands.
Kamal deploy runs many subcommands that can also be invoked from the command line, but if it's in code, there will be this invoke method and then we go further. Everything to SSHKit also might sound familiar because we know it from Capistrano times. And that actually runs the SSH command for Kamal.
So Kamal includes a little small patch that I will talk about and a small DSL. So we have a DSL for running some code on hosts, on servers, and some code locally. But Kamal, because it has roles, we also have to have on roles, yeah? So we extended it just a little bit more.
So essentially we have this configuration for Kamal and then in the end we are running some commands. It's mostly Docker commands on the server, and the code itself for the commands comes from the Kamal commands namespace.
So for example, a very simple example would be docker -v for, well, is Docker installed? If you have something more complex, it will be full docker run. And you can notice it almost looks like a shell. There are some symbols, there are some strings, there are some methods, but essentially it looks like a shell.
That's because the docker method actually builds an array and puts the Docker symbol at the beginning. It does it because it actually registered Docker as an SSHKit command. And when it does, it means that we are skipping the /usr/bin/env binary and the logs get cleaner, right? Because it still works even by just shelling out to Docker.
I talked about methods. So for example, let's look at how sensitive data are handled. So we will have this class for sensitive. And if you delegate, well, if you need to string, right, it will be unredacted. But if you are just inspecting, you will get the redaction. So this is the example for registry password. But we are still having this string of tokens as an array.
Now we can combine the commands. We know it's an array, so this is operations on arrays, right? We can compact, collect, type. Now we want pipe, which is pipe from shell. Well, it's the same thing. Just we divide by a pipe. We can also have a new shell like sh -c. Or for get Docker, we now would use that shell method with any one of them, still just arrays, and we could create a Docker install command, which is like pipe get Docker to sh.
And finally, when we have that, Kamal keeps it as an array for as long as it's possible, but then it has to pass it to SSHKit for actually running it. If you would study the Kamal code, you would also see this execute method.
Now the execute method actually comes from the Net::SSH backend for SSHKit. And it would actually really run the command. Now the asterisk is there because it accepts positional arguments. So we will pass the command like that, and it will execute.
And we have also Kamal as a constant because now that we have both Thor and SSHKit, which also use instance_exec, we are losing the self. So we still need to be able to ask for global configuration, for all the global stuff. So that's why we have that.
And how is that concurrently on many hosts? For that, I will return to the SSHKit patch. So SSHKit would normally run threads each join, so just joining all threads, but Kamal does something a little bit different. It still joins, but it rescues for all the exceptions from all the errors. This is so that we really run all the code everywhere on every host, and we just collect the errors instead of just bailing out.
And finally, so we have threads, yes, but what about TCP connections? We also should share them because they're expensive to create. So yes, they are shared, because all these little commands from docker login to docker stop, if they are running on concurrent roles, then they can actually reuse connections. So there will be a pool, and it will get reused.
Now the second dive, building and pushing images. So of course Docker is our application, but it's BuildKit, part of Docker or the Moby project, which is the upstream for Docker, that builds your image, right? So essentially we will run docker buildx build. The buildx command is a bit weird because this is actually a plugin to Docker.
So we need to pull out Docker builder drivers. So there's docker and docker-container. Docker is the original one. That was docker build, right? The simple one. And we also have docker-container, which Kamal uses because we need a little bit more. We need multiple architectures, and we need exports, and we need multi-node, and so on.
So the builder is the named instance to run. So when we are configuring in the YAML file, maybe you are saying builder something, so that would be the instance of that docker-container thing.
And isn't it a little bit confusing? For the first time, I think yes, because of the naming. So we run kamal deploy. As part of that, we are building the image, we are running docker buildx that uses the Buildx plugin, that will spin up a new container. That's why it's called docker-container. And the BuildKit inside that container will then run your Dockerfile and actually build the image.
Now if that happens on a remote builder, it's actually completely the same. Kamal can just do it as long as Docker is pre-installed on that host. And that's it.
And that brings me to registries because part of that command is of course uploading the image to a registry. So we have the usual Docker Hub, right? Then you can have a different provider. But what's interesting is the local registry, right? We can now run local registries. So how it's done is that we really just run an extra container, which is the Kamal Docker registry. And we're really running the registry image. There's nothing special about that.
Except maybe one special thing, because what about the ports, right? How do our hosts, our servers, see a local port on our laptop? So you might know local forwarding that you might have used before when you needed to take a database port from the server and see it locally for some inspection. And so essentially here it's just the opposite. So it's remote forwarding.
Something like ssh -R. Kamal does not shell out ssh -R. Of course it's implemented in Net::SSH. But essentially this is how it works. It will be some kind of port forwarding in Kamal.
And now the push and pull, right? How the image gets on there. So for the push, we're running docker buildx build. So we've already seen that, right? Of course, because it's push, but it builds and then pushes, right? And for pull, if we sort out the port I talked about, now the servers see our port so we can just pull the image on hosts and there's no problem.
So next we need a service registration. So Kamal has this Kamal configuration file. But Kamal Proxy doesn't have a configuration specifically. Every instruction for it would be actually docker exec from Kamal CLI. Because we have this imperative way of doing things, so we need it for the proxy as well.
And on Kamal Proxy we'll essentially run kamal-proxy deploy. That's the command for the proxy. Now since we are passing everything in one docker exec, we need to pass all the flags. So from TLS to logging, buffering, whatever we need, we just pass it on.
So how it looks: we have docker exec kamal-proxy. That means the first one is like, okay, run something on this container, and then kamal-proxy, that will be the binary. Deploy our service here, such as web, and then we pass everything else. Now the target, that's also important. That will be the container ID and port.
And now of course, when we are passing it imperatively, what happens if the proxy goes down? So there's a kamal-proxy-config named volume. It will keep the configuration so Kamal Proxy can load it up. There are other service actions, so deploy, of course we can remove the service later or we can stop it, maintenance mode, and resume it.
And that brings me to zero downtime deployments that are very essential to how Kamal works and runs. So let's return to the docker exec Kamal Proxy command. Because when we are running a new version, we just run this again with the same service name. We just use a new deploy target. So we change the target, which is the container ID and the port.
And then if you are running proxy, we instruct the proxy to deploy. So first we run the container. It's running and proxy can actually do the switch.
So in Kamal we use blue-green deployment. So simple case, single host, we will be booting the new web role container, which is our Puma, that needs to be served traffic. We pass it over to Kamal Proxy, which will run a health check. And once done, it can switch over the traffic to the new green ones. And of course it has to drain the requests for the previous ones.
Now we also have to do something with assets because we have two versions at the same time. So essentially we unpack the assets to the same directory. And then we copy to volumes for both the old one and new ones. The cp -n ensures that the first ones that get copied over are not overwritten. So we have to just follow the right order and that's it. Both containers can serve both assets. They need to be fingerprinted, of course, but then everything works.
Lock is even easier. So when we would have concurrent deploys, we are kind of locking it so nothing bad happens. And this is just really creating a directory on the server and that's it.
Now what happens when we have more hosts and we need to coordinate them, right? So it's kind of the same thing, but a bit more complex. For that, we will dive even deeper to the Great Barrier Reefs. So we can explore the barrier, or what's also called the web barrier, according to the role that we are protecting.
How does it look? So it's part of the health check namespace, and we will have an IVar, which is like an immutable variable. And that's our IVar which we can set once. So the barrier is either open or it's closed. And when we set it, we don't care about errors, but only once it can be set to true.
So what it does is that we will start booting all web role containers, and the first one that boots correctly, that will open the barrier and then let everything else boot up. So only one is needed, and everything gets switched over, and the switch happens like we talked before.
How does it look in code? So we wait at barrier if we are queuer, which means we are not web role. Starting new version. And of course if we are web role, we are the gatekeeper, so we release the barrier. Then we can stop it.
The start new version: also what you would expect, we upload the environment, follow the new secrets. We run app run, which means we are starting the container, and then if we are running the proxy, we let the proxy actually do the change with app deploy.
Now for rolling deploys, of course Kamal makes it easy to run many servers. So we just slice them up. You've probably seen it in the config with the limit. And this is just in the code, so we can provide a number, digit, or percentage, and this will work.
And finally, for the final dive today, we need to learn how to proxy the request, right? This is a bit of Go territory. It's not Ruby, which should seem to be the topic of this conference. But don't worry, because I will teach you Go just now.
So if you heard about Go when it was released, it was all about goroutines and channels, right? So what are goroutines? They are concurrent functions that are scheduled by the Go scheduler, so they happen automatically all over the available threads. It can be one or many CPUs, and it will all work. So essentially there will be somewhere
In the code will be the go keyword. That's maybe why Go language is named.
And so, for example, here in the net/http package, if you are starting the new request, it will be like go serve and so on. It's simplified. But if you go to Kamal Proxy code, we will not actually find the go keyword at all, because it will use this package, then it will run the middleware there, and then this handler will be called. And part of this handler, we will simply find the right service for the request and service.
And the second feature of Go that's so important are channels. So, if you have so many running functions, maybe they want to coordinate something. So, essentially it's something like a first-in, first-out queue, let's say. The goroutines can write to channels or they can read from channels. We don't know which goroutine picks up any value in the channel. But if the channel is closed, all goroutines will know it.
So, the first thing that's implemented like that is health check, because we have ticker.C, and the values in the channels are actually just the interval, our specified interval. And when the channel gets a new value, we can go and run the check, the actual check, right? So, in code, we will have a new ticker with our interval from the configuration. And then we have this for loop that is kind of running forever.
And then we have this select, maybe. And this select is very known in Go code. It's everywhere there. So, basically these cases are waiting for some new value or some new information from channels. So, here, when we get the new value in ticker.C, we just go and check it.
Then, the Kamal proxy also does buffering. So, when the request is too big, this is kind of protection against slow clients. So, some part of that is in memory, some part is written on disk, but in any case, it will be buffered up and sent to Puma later.
Draining is maybe more interesting. So, I told about draining, we have to drain requests, but how does it look like, actually? This is the same select. I just visualized it in different ways, so we would have this request context done. So, if the request is done, we can let it finish. But if we have a deadline, then we have to cancel it.
In code, if we look at the drain function, first, there is something about in-flight requests, about hijacked in-flight requests. So, that's our WebSocket side, that's our Action Cable. We just kill it immediately. We don't bother to do anything.
But with the others, we have this wait for requests to complete, like this named block. And this will actually, for all these requests in parallel, wait for either the request is done or we are hitting a deadline. If we are hitting a deadline, we go outside of that named block, and for whatever is remaining, we just cancel with error draining. Yeah?
And maintenance mode, that's actually also very interesting. So, we have this pause channel. And when we create it, all these requests are waiting. These goroutines are waiting for the channel. And when we close it, all of them know about it and can continue. So, we don't need any particular value, it's just closed and it's done. So, such state, there will be this closed pause channel. And somewhere inside the request, we will again have the same select just waiting for our channels.
So, what we learned, we learned that Kamal is based on Thor and SSHKit, how it keeps the commands as arrays until the last moment where it has to execute it, and then the Kamal proxy comes in because we have to set it up with a command to register the service, so it can then do blue-green style deployment, maybe coordinating many servers. And then finally, we also learned Go, so maybe it's fried already. Like a chicken sandwich.
And yeah, I cannot stop talking about Kamal. This is my problem. So, when I was in California, I talked to this girl, right? I was explaining that, yeah, Kamal is this simple deploy tool you can replace your Kubernetes clusters with, and then save a bunch of money. And she was like, "Sir, this is a Wendy's."
And some say I'm doing America wrong with all these fast foods. It's potentially right, but the good news is that now I will only eat Texas barbecue. And I will also finally drive a proper American muscle car. So, not a Ferrari anymore.
So, long live Kamal. And God bless America. So, thank you.
[applause]
Enjoy the rest of Rails World.
[cheering]
[music]
Article published · Updated
