DEV Community

Cover image for Deploy your Preprod and Production Rails Application using Kamal
Pimp My Ruby
Pimp My Ruby

Posted on

Deploy your Preprod and Production Rails Application using Kamal

In most of the projects I contributed to, the deployment workflow looked like this :

Image description

We got a Preprod application that lets us experiment and do some QA. Preprod is an application that I can break without impacting users. Once I know that my Preprod application is working correctly, I can then deploy my application to the Production environment and expose new features to users.

Usually, I used Heroku for both Preprod and Prod. But… come on, we’re in 2024 right ? Heroku is old school, now we’re using Kamal 😎

Today I’ll share with you how I manage the deployment of multienvironment application using Kamal 2 in a step-by-step guide.

I won’t go that deep on how to configure kamal, I’ll just share the specificities of handling 2 environments deployment. If you need more details about a certain part, I linked useful resources that helped me and will help you for sure in the conclusion.

Prepare your Preprod environment

Before toying with Kamal, we need to create the Preprod environment.

To do so, I only need to create a new file in my environments folder

$ cp config/environments/production.rb config/environments/preprod.rb
Enter fullscreen mode Exit fullscreen mode

I want my Preprod environment to be as close as possible to my Prod environment, so I won’t edit the file.

Now that Rails knows my Preprod environment, I need to create a master.key for this environment

$ rails credentials:edit -e preprod
Enter fullscreen mode Exit fullscreen mode

One last thing, we need a dedicated Dockerfile to handle this specific environment when building the Docker image.

For the sake of this article, I will take the default Dockerfile provided by Rails when you’re using rails new, and create a dedicated one for Preprod environment :

$ cp Dockerfile Dockerfile.preprod
Enter fullscreen mode Exit fullscreen mode

The only thing I will edit will be the ENV variables set by Dockerfile.preprod:

# Dockerfile.preprod L20
ENV RAILS_ENV="preprod" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"
Enter fullscreen mode Exit fullscreen mode

That’s it, now we’re good to explore Kamal !!

Your folder architecture will look like this :

.
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ Dockerfile.preprod
└── config
    β”œβ”€β”€ credentials
    β”‚Β Β  β”œβ”€β”€ preprod.key
    β”‚Β Β  └── preprod.yml.enc
    β”œβ”€β”€ credentials.yml.enc
    β”œβ”€β”€ environments
    β”‚Β Β  β”œβ”€β”€ preprod.rb
    β”‚Β Β  └── production.rb
    └── master.key
Enter fullscreen mode Exit fullscreen mode

Install Kamal

Kamal is not a gem that you add to your Gemfile, but you can install it using gem :

$ gem install kamal
Enter fullscreen mode Exit fullscreen mode

Once it’s done, you have access to Kamal. You can use the following command to bootstrap your Kamal configuration :

$ kamal init
Enter fullscreen mode Exit fullscreen mode

kamal init will create a config/deploy.yml file and a .kamal folder containing a hooks directory and a secrets file

Setup Prod

I will keep my config/deploy.yml as simple as possible, here is what I write :

# config/deploy.yml
service: my-app
image: docker-username/my-app

servers:
  web:
    - 190.0.0.0

proxy:
  ssl: true
  host: mydomain.com
  app_port: 3000

registry:
  username: docker-username 
  password:
    - KAMAL_REGISTRY_PASSWORD

builder:
  arch: arm64
  local: true
  dockerfile: Dockerfile.production
  context: "."

env:
  secret:
    - RAILS_MASTER_KEY

ssh:
  user: ubuntu
Enter fullscreen mode Exit fullscreen mode

Let’s dive into each parameter :

  • service: The name of your application
  • image: The name of the image you will use for your registry (I’ll use a public docker registry here)
  • servers: The addresses of the servers you will deploy your application. Here I have a single host for my production. I can also add a dedicated process for jobs handling if I need to process job.
  • proxy: handle SSL certificate and point to the right domain name
  • registry: where your Docker image will be pushed / pulled
  • builder: Use Dockerfile.production and build it locally when deploying to the server. I use arm64 architecture because I’m using a macBook.
  • env: It’s a way of passing secrets to the machine. We’ll talk about it just after
  • ssh: Kamal will connect to the servers using the ubuntu user

To provide secrets, Kamal introduces it own secrets mechanism. You need to edit .kamal/secrets to add the RAILS_MASTER_KEY environment variable.

# .kamal/secrets
RAILS_MASTER_KEY=$(cat config/master.key)
KAMAL_REGISTRY_PASSWORD=mysuperpassword
Enter fullscreen mode Exit fullscreen mode

Once it’s done, you’re good to either setup your server or deploy your application.

$ kamal setup
$ kamal deploy # if you want to deploy only
Enter fullscreen mode Exit fullscreen mode

And now your application is running in production and fully accessible at your domain πŸŽ‰

At this stage, your folder architecture will look like this :

.
β”œβ”€β”€ .kamal
β”‚Β Β  └── secrets
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ Dockerfile.preprod
└── config
    β”œβ”€β”€ credentials
    β”‚Β Β  β”œβ”€β”€ preprod.key
    β”‚Β Β  └── preprod.yml.enc
    β”œβ”€β”€ credentials.yml.enc
    β”œβ”€β”€ deploy.yml
    β”œβ”€β”€ environments
    β”‚Β Β  β”œβ”€β”€ preprod.rb
    β”‚Β Β  └── production.rb
    └── master.key
Enter fullscreen mode Exit fullscreen mode

Let’s see how to setup Preprod deployment now !

Setup Preprod

To configure Preprod deployment, we need to replicate what we did for Production, and essentially add .preprod everywhere:

$ cp .kamal/secrets .kamal/secrets.preprod
$ cp config/deploy.yml config/deploy.preprod.yml
Enter fullscreen mode Exit fullscreen mode

And then in the config/deploy.preprod.yml :

# config/deploy.preprod.yml
image: docker-username/my-app-preprod

servers:
  web:
    - 190.0.0.1

proxy:
  host: preprod.mydomain.com

builder:
  dockerfile: Dockerfile.preprod
Enter fullscreen mode Exit fullscreen mode

And … that’s it.

Kamal will read the β€œprimary” config (deploy.yml) as a fallback for missing value in our deploy.preprod.yml. We’re just saying to Kamal that it must take the Dockerfile.preprod and set the SSL certificate will point to preprod.mydomain.com

The last thing to do before deploying is to edit the secrets :

# .kamal/secrets.preprod
RAILS_MASTER_KEY=$(cat config/credentials/preprod.key)
KAMAL_REGISTRY_PASSWORD=mysuperpassword
Enter fullscreen mode Exit fullscreen mode

And then you’re good to go :

$ kamal setup -d preprod
$ kamal deploy -d preprod # if you want to deploy only
Enter fullscreen mode Exit fullscreen mode

Your application can be accessed at preprod.mydomain.com !

Your folder architecture will look like this :

.
β”œβ”€β”€ .kamal
β”‚Β Β  β”œβ”€β”€ secrets
β”‚Β Β  └── secrets.preprod
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ Dockerfile.preprod
└── config
    β”œβ”€β”€ credentials
    β”‚Β Β  β”œβ”€β”€ preprod.key
    β”‚Β Β  └── preprod.yml.enc
    β”œβ”€β”€ credentials.yml.enc
    β”œβ”€β”€ deploy.preprod.yml
    β”œβ”€β”€ deploy.yml
    β”œβ”€β”€ environments
    β”‚Β Β  β”œβ”€β”€ preprod.rb
    β”‚Β Β  └── production.rb
    └── master.key
Enter fullscreen mode Exit fullscreen mode

Ressources

Conclusion

I took a lot of shortcuts here, but the point is that it’s very easy and painless to deploy multienvironment applications using Kamal.

So far, I only have good experience using Kamal. I've never had any difficult stages while debugging / monitoring, so it’s a very positive thing.

Top comments (0)