Notes to self

Reading Kamal configuration

If you configured your config/deploy.yml file for Kamal, you might thought about working with this very configuration outside Kamal. Here’s how.

Kamal config

Reading Kamal’s configuration from Ruby is pretty straight-forward. We just initialize Kamal::Configuration object from a passed config file:

config_file = Pathname.new(File.expand_path("config/deploy.yml"))
config = Kamal::Configuration.create_from(config_file: config_file)

puts config.inspect

Now we can access the whole YAML configuration as Kamal sees it:

> config.image
=> "strzibnyj/template"
> config.service
=> "template"

Registry

Registry is a Hash of username and password as you would expect:

> config.registry
=> {"username"=>"strzibnyj", "password"=>["KAMAL_REGISTRY_PASSWORD"]}

SSH

The SSH configuration can be accessed at config.ssh. Here’s how to read the SSH user:

> config.ssh
=> #<Kamal::Configuration::Ssh:0x0000000107688f28 @config={"user"=>"app"}>
> config.ssh.user
=> "app"

Traefik

Traefik configuration is under config.traefik and is just a Hash:

> config.traefik
=> 
{"options"=>{"publish"=>["443:443"], "volume"=>["/letsencrypt/acme.json:/letsencrypt/acme.json"], "network"=>"private"},
 "args"=>
  {"entryPoints.web.address"=>":80",
   "entryPoints.websecure.address"=>":443",
   "certificatesResolvers.letsencrypt.acme.email"=>"info@template.com",
   "certificatesResolvers.letsencrypt.acme.storage"=>"/letsencrypt/acme.json",
   "certificatesResolvers.letsencrypt.acme.httpchallenge"=>true,
   "certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint"=>"web"}}

Servers and roles

Same for servers:

> config.servers.first
=> 
["web",
 {"hosts"=>["170.64.149.226"],
  "labels"=>
   {"traefik.http.routers.template.rule"=>"Host(`app.template.com`)",
    "traefik.http.routers.template.entrypoints"=>"web",
    "traefik.http.routers.template_secure.entrypoints"=>"websecure",
    "traefik.http.routers.template_secure.rule"=>"Host(`app.template.com`)",
    "traefik.http.routers.template_secure.tls"=>true,
    "traefik.http.routers.template_secure.tls.certresolver"=>"letsencrypt"},
  "options"=>{"network"=>"private"},
  "port"=>"3000:3000"}]

To get IP addresses for out web and job roles we can do:

> config.servers["web"]["hosts"]
=> ["150.64.149.221"]

We can also work with servers as roles:

> config.roles.first
=> 
#<Kamal::Configuration::Role:0x0000000107db2a38
 @config=
  #<Kamal::Configuration:0x0000000107db34d8
   @accessories=
    [#<Kamal::Configuration::Accessory:0x0000000106f7d5f8
      @config=#<Kamal::Configuration:0x0000000107db34d8 ...>,
      @name="postgres",
      @specifics=
       {"image"=>"postgres:15",
        "host"=>"...",

ENVs

The list of environments are under config.env:

> config.env
=> 
{"clear"=>
  {"HOST"=>"app.template.com",
   "REDIS_URL"=>"redis://:someuniquepass453@template-redis:6379/1",
   "DB_HOST"=>"170.64.149.226",
   "RAILS_SERVE_STATIC_FILES"=>true,
   "RAILS_LOG_TO_STDOUT"=>true,
   "ENABLE_FREE_PLAN"=>true},
 "secret"=>["RAILS_MASTER_KEY", "POSTGRES_PASSWORD"]}

Accessories

You can access accessories under config.accessories. You’ll get an array of the Kamal::Configuration::Accessor objects. Here’s an example:

> accessory = config.accessories.first
> accessory.name
=> "postgres"
> config.accessories.first.specifics
=> 
{"image"=>"postgres:15",
 "host"=>"170.64.149.226",
 "port"=>5432,
 "env"=>{"clear"=>{"POSTGRES_USER"=>"template", "POSTGRES_DB"=>"template_production"}, "secret"=>["POSTGRES_PASSWORD"]},
 "files"=>["config/init.sql:/docker-entrypoint-initdb.d/setup.sql"],
 "directories"=>["data:/var/lib/postgresql/data"],
 "options"=>{"network"=>"private"}}

Volumes

Perhaps surprisingly, volumes are different. There is no config.volumes. Instead, we can ask for the volume arguments:

> config.volume_args
=> ["--volume", "/storage:/rails/storage"]

Hope that helped!

Work with me

I have some availability for contract work. I can be your fractional CTO, a Ruby on Rails engineer, or consultant. Write me at strzibny@strzibny.name.

RSS