19
.
03
.
2025
19
.
03
.
2025
Backend

Kamal Hooks - How to track Kamal deployments on GitHub

Cezary Kłos
Ruby Developer
Kamal Hooks - How to track Kamal deployments on GitHub

GitHub offers numerous features. One of these is tracking of the deployments. When a new deployment is made, information about this is visible at first glance. It makes figuring out what is currently deployed easy and accessible.

In order for GitHub to show this information, we have to notify it about a deployment. Every PaaS worth paying for has this feature. Since we are not paying Basecamp for using Kamal, we get none of this. So let's make it ourselves!

The plan

Use the Kamal Hooks mechanism to call GitHub's API using the official client: octokit.

Creating a GitHub access token

Before implementation, we will need to create a GitHub token that will allow Kamal to use the API. Following the least privilege principle, we are using a fine-grained token scoped to a single repository with minimal required permissions.

Keep in mind that an organization can disallow using fine-grained personal access tokens or “classic” tokens. In this case you won’t be able to select repository.

To generate a token go to GitHub Settings > Developer Settings > Personal access tokens > Fine-grained tokens and click on “Generate new token”.

Give it a meaningful name. Select a repository and set repository permissions for Deployments to Read and write. The overview section on the bottom of the form should look like this:

Note the token down, it will not be displayed again. I have added the token to .env file in the project as DEPLOYMENTS_GITHUB_TOKEN. If you do not use dotenv you can set it in .bashrc or however you config your environment.

Kamal hooks mechanism

During deployment process Kamal executes hooks. A hook is a script in a file matching a hook name. The file should have no extension and an execute file permission. When Kamal is added to the project, it creates sample hooks in <app root>/.kamal/hooks/ folder. At the time of writing this article there are 9 hooks available.

Hooks have access to deployment metadata via set of environment variables:

KAMAL_RECORDED_AT  - timestamp
KAMAL_PERFORMER    - output of `whoami`
KAMAL_VERSION      - commit SHA
KAMAL_HOSTS        - list of IPs
KAMAL_COMMAND      - in post-deploy these can be: rollback, redeploy, deploy
KAMAL_ROLE         - value of --roles flag if set
KAMAL_DESTINATION  - value of --destination flag if set

You can read more about hooks in the documentation.

Implementing the hook

With the plan, access and new knowledge we can start working on the implementation of the feature.

We are going to use a post-deploy hook so we have to remove .sample from the filename.

In the post-deploy hook we will:

  1. Install required octokit and faraday-retry gems.
  2. Create a Deployment on GitHub API. New deployments always have state queued. The methods take two positional arguments: repository name and commit ref (branch, tag, or SHA).
  3. Create a Deployment Status on GitHub API.It will update the Deployment with status success.

Final /.kamal/hooks/post-deploy file:

#!/usr/bin/env ruby

require "bundler/inline"

# true = install gems so this is fast on repeat invocations
gemfile(true, quiet: true) do
  source "https://rubygems.org"

  gem "octokit"
  gem "faraday-retry"
end

# Exit silently if there is an error
# failure here is not critical and should not block the deployment
def exit_with_error(message)
  $stderr.puts message
  exit 0
end

$stdout.sync = true

begin
  # gh_repo should be replaced with your information
  gh_repo = "GH_user/GH_reposiotry_name"
  github_client = Octokit::Client
                    .new(access_token: ENV["DEPLOYMENTS_GITHUB_TOKEN"])

  # Create a deployment
  deployment = github_client.create_deployment(
    gh_repo,
    ENV["KAMAL_VERSION"],
    task: ENV["KAMAL_COMMAND"],
    auto_merge: false,
    required_contexts: [],
    environment: ENV["KAMAL_DESTINATION"] || "production"
  )

  # Create a `deployment status` to update the `deployment` status to success
  github_client.create_deployment_status(deployment.url, "success")
rescue Octokit::ClientError => error
   exit_with_error "GitHub API error: #{error.message}"
end

Finale

With the hook set up we can finally deploy our application with:

dotenv kamal deploy

Notice the dotenv - it will load .env and pass DEPLOYMENTS_GITHUB_TOKEN .

When the deploy ends you should see the deployment on GitHub repository.

With this simple ruby script deployments with Kamal are one step closer to the premium platform as a service experience.

Cezary Kłos
Ruby Developer

Check my Twitter

Check my Linkedin

Did you like it? 

Sign up To VIsuality newsletter

READ ALSO

Ruby MCP Client in Rails by Paweł Strzałkowski

MCP Client in Rails using ruby-mcp-client gem

11
.
06
.
2025
Paweł Strzałkowski
LLM
Ruby on Rails
Actionmcp in Ruby on Rails by Paweł Strzałkowski

MCP Server with Rails and ActionMCP

11
.
06
.
2025
Paweł Strzałkowski
LLM
Ruby on Rails
Banner - MCP Server with FastMCP and Rails by Paweł Strzałkowski

MCP Server with Rails and FastMCP

11
.
06
.
2025
Paweł Strzałkowski
LLM
Ruby
Ruby on Rails

Ruby on Rails and Model Context Protocol

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
LLM
Title image

Highlights from wroclove.rb 2025

11
.
06
.
2025
Kaja Witek
Conferences
Ruby
Jarosław Kowalewski - Migration from Heroku using Kamal

Migration from Heroku using Kamal

11
.
06
.
2025
Jarosław Kowalewski
Backend
store-vs-store_accessor by Michał Łęcicki

Active Record - store vs store_accessor

11
.
06
.
2025
Michał Łęcicki
Ruby
Ruby on Rails
How to become a Ruby Certified Programmer Title image

How to become a Ruby Certified Programmer

11
.
06
.
2025
Michał Łęcicki
Ruby
Visuality
Vector Search in Ruby - Paweł Strzałkowski

Vector Search in Ruby

11
.
06
.
2025
Paweł Strzałkowski
ChatGPT
Embeddings
Postgresql
Ruby
Ruby on Rails
LLM Embeddings in Ruby - Paweł Strzałkowski

LLM Embeddings in Ruby

11
.
06
.
2025
Paweł Strzałkowski
Ruby
LLM
Embeddings
ChatGPT
Ollama
Handling Errors in Concurrent Ruby, Michał Łęcicki

Handling Errors in Concurrent Ruby

11
.
06
.
2025
Michał Łęcicki
Ruby
Ruby on Rails
Tutorial
Recap of Friendly.rb 2024 conference

Insights and Inspiration from Friendly.rb: A Ruby Conference Recap

11
.
06
.
2025
Kaja Witek
Conferences
Ruby on Rails

Covering indexes - Postgres Stories

11
.
06
.
2025
Jarosław Kowalewski
Ruby on Rails
Postgresql
Backend
Ula Sołogub - SQL Injection in Ruby on Rails

The Deadly Sins in RoR security - SQL Injection

11
.
06
.
2025
Urszula Sołogub
Backend
Ruby on Rails
Software
Michal - Highlights from Ruby Unconf 2024

Highlights from Ruby Unconf 2024

11
.
06
.
2025
Michał Łęcicki
Conferences
Visuality
Cezary Kłos - Optimizing Cloud Infrastructure by $40 000 Annually

Optimizing Cloud Infrastructure by $40 000 Annually

11
.
06
.
2025
Cezary Kłos
Backend
Ruby on Rails

Smooth Concurrent Updates with Hotwire Stimulus

11
.
06
.
2025
Michał Łęcicki
Hotwire
Ruby on Rails
Software
Tutorial

Freelancers vs Software house

11
.
06
.
2025
Michał Krochecki
Visuality
Business

Table partitioning in Rails, part 2 - Postgres Stories

11
.
06
.
2025
Jarosław Kowalewski
Backend
Postgresql
Ruby on Rails

N+1 in Ruby on Rails

11
.
06
.
2025
Katarzyna Melon-Markowska
Ruby on Rails
Ruby
Backend

Turbo Streams and current user

11
.
06
.
2025
Mateusz Bilski
Hotwire
Ruby on Rails
Backend
Frontend

Showing progress of background jobs with Turbo

11
.
06
.
2025
Michał Łęcicki
Ruby on Rails
Ruby
Hotwire
Frontend
Backend