DEV Community

Akilesh
Akilesh

Posted on

Kamal Deploy A failed attempt

Part 1 : Happy ending

05-09-2025 - It workings I did manage to deploy using kamal and

Assumiung you have a rails application setup with postgres db

Replace

config/deploy.yml

# Name of your application. Used to uniquely configure containers.
service: form_build

# Name of the container image.
image: 2112akilesh/form_build

# Deploy to these servers.
servers:
  web:
    - 140.245.16.xxx
  # job:
  #   hosts:
  #     - 192.168.0.1
  #   cmd: bin/jobs

# Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server.
# Remove this section when using multiple web servers and ensure you terminate SSL at your load balancer.
#
# Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
proxy:
  ssl: true
  host: form.akilesh.in 
# Credentials for your image host.
registry:
  # Specify the registry server, if you're not using Docker Hub
  # server: registry.digitalocean.com / ghcr.io / ...
  username: 2112akilesh 

  # Always use an access token rather than real password when possible.
  password:
    - KAMAL_REGISTRY_PASSWORD

# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
  secret:
    - RAILS_MASTER_KEY
    - POSTGRES_PASSWORD
  clear:
    # Run the Solid Queue Supervisor inside the web server's Puma process to do jobs.
    # When you start using multiple servers, you should split out job processing to a dedicated machine.
    SOLID_QUEUE_IN_PUMA: true

    # Set number of processes dedicated to Solid Queue (default: 1)
    # JOB_CONCURRENCY: 3

    # Set number of cores available to the application on each server (default: 1).
    # WEB_CONCURRENCY: 2

    # Match this to any external database server to configure Active Record correctly

    # Use form_build-db for a db accessory server on same machine via local kamal docker network.
    # DB_HOST: postgresql://postgres:postgres@140.245.16.247:5432/form_build-db
    DB_HOST: form_build-db
    POSTGRES_USER: form_build
    POSTGRES_DB: form_build_production
    # Log everything from Rails
    # RAILS_LOG_LEVEL: debug

# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
aliases:
  console: app exec --interactive --reuse "bin/rails console"
  shell: app exec --interactive --reuse "bash"
  logs: app logs -f
  dbc: app exec --interactive --reuse "bin/rails dbconsole"

# Use a persistent storage volume for sqlite database files and local Active Storage files.
# Recommended to change this to a mounted volume path that is backed up off server.
volumes:
  - "form_build_storage:/rails/storage"

# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
# hitting 404 on in-flight requests. Combines all files from new and old
# version inside the asset_path.
asset_path: /rails/public/assets

# Configure the image builder.
builder:
  arch: amd64

# Use a different ssh user than root
ssh:
  user: ubuntu

# Then, configure the `postgres` accessory
accessories:
  db:
     image: postgres:latest
     host: 140.245.16.247
     # Change to 5432 to expose port to the world instead of just local network.
     port: "127.0.0.1:5432:5432"
     # options:
     #    restart: always
     env:
       clear:
         DB_HOST: form_build-db
         POSTGRES_USER: form_build
         POSTGRES_DB: form_build_production
       secret:
         - POSTGRES_PASSWORD
     files:
       - config/setup.sql:/docker-entrypoint-initdb.d/setup.sql
     volumes:
       - data:/var/lib/postgresql/data
     # directories:
     #  - data:/var/lib/postgresql/data

Enter fullscreen mode Exit fullscreen mode

config/setup.sql

CREATE DATABASE form_build_production;
CREATE DATABASE form_build_production_cache;
CREATE DATABASE form_build_production_queue;
CREATE DATABASE form_build_production_cable;
Enter fullscreen mode Exit fullscreen mode

config/database.yml

# PostgreSQL. Versions 9.3 and up are supported.
#
# Install the pg driver:
#   gem install pg
# On macOS with Homebrew:
#   gem install pg -- --with-pg-config=/usr/local/bin/pg_config
# On Windows:
#   gem install pg
#       Choose the win32 build.
#       Install PostgreSQL and put its /bin directory on your path.
#
# Configure Using Gemfile
# gem "pg"
#
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: form_build_development
  password: <%= ENV["POSTGRES_PASSWORD"] %>
  # The specified database role being used to connect to PostgreSQL.
  # To create additional roles in PostgreSQL see `$ createuser --help`.
  # When left blank, PostgreSQL will use the default role. This is
  # the same name as the operating system user running Rails.
  #username: form_build

  # The password associated with the PostgreSQL role (username).
  #password:

  # Connect on a TCP socket. Omitted by default since the client uses a
  # domain socket that doesn't need configuration. Windows does not have
  # domain sockets, so uncomment these lines.
  #host: localhost

  # The TCP port the server listens on. Defaults to 5432.
  # If your server runs on a different port number, change accordingly.
  #port: 5432

  # Schema search path. The server defaults to $user,public
  #schema_search_path: myapp,sharedapp,public

  # Minimum log levels, in increasing order:
  #   debug5, debug4, debug3, debug2, debug1,
  #   log, notice, warning, error, fatal, and panic
  # Defaults to warning.
  #min_messages: notice

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: form_build_test

# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password or a full connection URL as an environment
# variable when you boot the app. For example:
#
#   DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
#
# If the connection URL is provided in the special DATABASE_URL environment
# variable, Rails will automatically merge its configuration values on top of
# the values provided in this file. Alternatively, you can specify a connection
# URL environment variable explicitly:
#
#   production:
#     url: <%= ENV["MY_APP_DATABASE_URL"] %>
#
# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full overview on how database connection configuration can be specified.
#
production:
  primary: &primary_production
    <<: *default
    host: <%= ENV["DB_HOST"] %>
    database: form_build_production
    username: form_build
    password: <%= ENV["POSTGRES_PASSWORD"] %>
  cache:
    <<: *primary_production
    database: form_build_production_cache
    migrations_paths: db/cache_migrate
  queue:
    <<: *primary_production
    database: form_build_production_queue
    migrations_paths: db/queue_migrate
  cable:
    <<: *primary_production
    database: form_build_production_cable
    migrations_paths: db/cable_migrate

Enter fullscreen mode Exit fullscreen mode

Dockerfile

# syntax=docker/dockerfile:1
# check=error=true

# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
# docker build -t form_build .
# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name form_build form_build

# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION=3.4.2
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base

# Rails app lives here
WORKDIR /rails

# Install base packages
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y curl libjemalloc2 libvips postgresql-client && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Set production environment
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"

# Throw-away build stage to reduce size of final image
FROM base AS build

# Install packages needed to build gems
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential git libpq-dev libyaml-dev pkg-config && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
    rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
    bundle exec bootsnap precompile --gemfile

# Copy application code
COPY . .

# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/

# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile

# Final stage for app image
FROM base

# Copy built artifacts: gems, application
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails

# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
    useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER 1000:1000

# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]

# Start server via Thruster by default, this can be overwritten at runtime
EXPOSE 80
CMD ["./bin/thrust", "./bin/rails", "server"
Enter fullscreen mode Exit fullscreen mode

If you use any other database or get a config with your Docker_user_name, Application_name, serverip, Domain_name use this tool

https://dailydevtools.com/kamal_config

Part 2 : Sad Ending

Not a bid fan of kamal-deploy but the hype around it is real

You need to do some prep work before jumping in

  1. Create a VPS
  2. Add Fire wall rules (Enable port 80 & 443)
  3. Connecting VPS using SSH
  4. Add A record to your DNS
  5. Lets deploy ← In this blog

Install kamal

Assuming you have ruby installed

gem install kamal
Enter fullscreen mode Exit fullscreen mode

Setup docker in your local and VPS

Install docker locally I am using Debian refer this **link & additionally I wanted to install tis package to**

sudo apt update
sudo apt install -y [docker.io](http://docker.io/)
apt install docker-buildx-plugin
Enter fullscreen mode Exit fullscreen mode

Kamal SSH setup for non root user run the below command in your VPS

sudo apt update
sudo apt upgrade -y
sudo apt install -y docker.io curl git
sudo usermod -a -G docker app
Enter fullscreen mode Exit fullscreen mode

Start Docker server locally

sudo systemctl start docker   // Start  Docker 
sudo systemctl status docker  // Check Docker Status
sudo usermod -aG docker $USER  // Making sure kamal had access to run docker without sudo privilege
Enter fullscreen mode Exit fullscreen mode

you might also want to set permission to instance sudo usermod -aG docker ${USER}

Restart your device

inside our config/deploy
lets set thing up

add your image name

Paste your VPS ipv4 to servers:

Uncomment your ssh: line and add your ssh config it may vary depending on your case for me I need to use different user name instead of root refer doc if you have other case

Make sure your .yml file is formatter correctly otherwise you will get error similar to ERROR (Psych::SyntaxError): (<unknown>): did not find expected key while parsing a block mapping at line 2 column 1

go to your docker hub and create a access token make sure to give read and write access copy your access key and save it in a .env

KAMAL_REGISTRY_PASSWORD=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Enter fullscreen mode Exit fullscreen mode

Push your env to kamal

kamal env push
Enter fullscreen mode Exit fullscreen mode

or add it in your local linux environment (I personally went with this)

export KAMAL_REGISTRY_PASSWORD=xxxxxxxxxxxxxxxxxxxxxxxxxxxx 
Enter fullscreen mode Exit fullscreen mode

and restart your file in your project directory

kamal setup
Enter fullscreen mode Exit fullscreen mode

If you face your self with Releasing the deploy lock error run this command
kamal lock release to release the lock nice little feature

I am using postgres database so ill configure it to
update your accessories: & production: section in deploy.yml

update the init file

    files:
      - config/init.sql:/docker-entrypoint-initdb.d/setup.sql
Enter fullscreen mode Exit fullscreen mode

create a init file

I tried kamal with rails 5 and docker I somehow find myself stuck in between error even after multiple hours of googling and successfully deploying it initially the following build fails I just gave up now back with kamal and rails 8 ( I know I am not clear with my error and explanation but it is what it is fuck off)

Even while writing this post I went through a hard time to figure our what went wring and how to fix it and even for the n-th time I coudnt able to pull it out in 1st try but yes its like

Add A name record to your DNS pointing it to your VPS ip address

  • day 3 still Kamal deploy fucks me
  • I dont think you will get a perfectly polished outcome after this blog its trial and error method

in your .kamal/secret pass production.key

# RAILS_MASTER_KEY=$(cat config/master.key)
RAILS_MASTER_KEY=$(cat config/credentials/production.key)

Enter fullscreen mode Exit fullscreen mode

Pass your application host name

DB_HOST=
Enter fullscreen mode Exit fullscreen mode

31/05/2025 — Not giving up today

Starting from scratch

Use this tool to generate kamal config https://dailydevtools.com/kamal_config

Delete the database in the server if already exist

Other Tools to check https://containrrr.dev/watchtower/

Enable port 80 & 443 check https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu#step-1-making-sure-ipv6-is-enabled

Fuck Kamal I am switching to plain DOCKER implementation

29 April 2026

Use latest Ubuntu server 22.04+

  • If old Ubuntu versoin is used install docker manually

    sudo apt update
    sudo apt install -y ca-certificates curl gnupg
    
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo tee /etc/apt/keyrings/docker.asc > /dev/null
    sudo chmod a+r /etc/apt/keyrings/docker.asc
    echo \
    "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu focal stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt update
    
```jsx
sudo systemctl enable --now docker
sudo usermod -aG docker ubuntu
```
Enter fullscreen mode Exit fullscreen mode

Prepare deploy.yml

# Name of your application. Used to uniquely configure containers.
service: drop_share

# Name of the container image (use your-user/app-name on external registries).
image: 2112akilesh/drop_share

# Deploy to these servers.
servers:
  web:
    - 141.148.209.128

proxy:
  ssl: false
  host: dropshare.akilesh.in

registry:
  username: 2112akilesh

# Access token stored in `.kamal/secrets`
  password:
    - KAMAL_REGISTRY_PASSWORD

# Inject ENV variables into containers (secrets come from .kamal/secrets).
env:
  secret:
    - RAILS_MASTER_KEY
  clear:
    SOLID_QUEUE_IN_PUMA: false

# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
aliases:
  console: app exec --interactive --reuse "bin/rails console"
  shell: app exec --interactive --reuse "bash"
  logs: app logs -f
  dbc: app exec --interactive --reuse "bin/rails dbconsole --include-password"

# Use a persistent storage volume for sqlite database files and local Active Storage files.
# Recommended to change this to a mounted volume path that is backed up off server.
volumes:
  - "drop_share_storage:/rails/storage"
  - "db:/rails/db"

asset_path: /rails/public/assets
builder:
  arch: amd64

# I have stored my ssh key in <my_project>/ssh/my_ssh.key
# dont forget to add ssh to .gitignore
ssh:
  user: ubuntu
  keys:
    - ssh/ssh-key-2026-04-14.key

Enter fullscreen mode Exit fullscreen mode

Add custom domain

configure your DNS

add a A record with vps public ip addresss

Optimise VPS

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Enter fullscreen mode Exit fullscreen mode
kamal setup

kamal deploy
Enter fullscreen mode Exit fullscreen mode

Top comments (0)