KVM VPS from Rs 149/mo. Mumbai, Noida and Jaipur nodes.

24×7 infrastructure operations Sales +91 98297 14343
vpswala.in
VPSWala 3 min read

Ruby on Rails VPS Deployment with Kamal 2: deploy.yml, kamal-proxy and Automatic SSL

Kamal turns a plain Linux VPS into a Rails deploy target with zero-downtime releases and Let's Encrypt SSL. Here is the setup, the config that matters and the errors you will meet first.

To deploy a Rails app to a VPS with Kamal 2, you need a Linux VPS you can reach over SSH as root with a key, a domain pointing at it, ports 80 and 443 open, and a container registry account. Run kamal init in the app, fill in config/deploy.yml and .kamal/secrets, then run kamal setup once and kamal deploy for every release after. Kamal installs Docker on the server if it is missing, and kamal-proxy handles zero-downtime switches and Let's Encrypt certificates.

If you prefer a traditional setup without containers, our earlier guide covers Rails on a VPS with Puma, Nginx, PostgreSQL and systemd.

Prerequisites

  • A Linux KVM VPS with SSH key access as root. Kamal connects as root by default, and its automatic Docker install needs root.
  • Ports 80 and 443 free on the server for kamal-proxy. Do not run another web server on them.
  • DNS: an A record for your app's hostname pointing at the VPS before the first deploy, so the certificate request can succeed.
  • A container registry such as Docker Hub or GitHub Container Registry, and an access token that can push.
  • Ruby on your workstation to install Kamal as a gem (Kamal can also run from a Docker container, with some limitations).
  • A Dockerfile in the app. New Rails apps generate a production Dockerfile; older apps need one added.

Step 1: Install Kamal and generate the config

gem install kamal
cd your-rails-app
kamal init

kamal init creates config/deploy.yml, which describes the service, image, servers, registry, builder and environment, and .kamal/secrets, which maps secret names to values pulled from your environment or a password manager. Keep real values out of the repository.

Step 2: Write config/deploy.yml

service: shopapp
image: yourname/shopapp

servers:
  web:
    - 203.0.113.10

proxy:
  host: shop.example.in
  ssl: true
  app_port: 80

registry:
  username: yourname
  password:
    - KAMAL_REGISTRY_PASSWORD

builder:
  arch: amd64

env:
  secret:
    - RAILS_MASTER_KEY
    - DATABASE_URL

A few lines do most of the work. proxy.host is the domain kamal-proxy routes to your app. ssl: true turns on automatic HTTPS through Let's Encrypt; the Kamal docs say this needs a single server with host set, the host pointing at that server, and port 443 open. app_port is the port the container listens on and defaults to 80. Set builder.arch to match the VPS (amd64 for x86 servers) when you build on an ARM laptop.

Step 3: Fill in .kamal/secrets

KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
RAILS_MASTER_KEY=$(cat config/master.key)
DATABASE_URL=$DATABASE_URL

Export the registry token and database URL in your shell before deploying. The master key lets the production app read encrypted credentials.

Step 4: First deploy

kamal setup

Use kamal setup the first time; it prepares the servers (installing Docker via get.docker.com where it is missing) and then deploys. From then on, every release is:

kamal deploy

According to the Kamal docs, a deploy logs in to the registry locally and on the servers, builds the image, pushes it and pulls it onto the servers, makes sure kamal-proxy is running on ports 80 and 443, starts a new container tagged with the current Git version, waits for it to answer GET /up with 200 OK, switches traffic, stops the old container and prunes old images and stopped containers so the disk does not fill.

Step 5: Verify

  • Open https://your-host/up; recent Rails versions generate this health check route, and it should return 200.
  • Check the certificate in the browser; it should be issued by Let's Encrypt for your host.
  • Read logs with kamal app logs and see running containers with kamal app details.

Common errors

  • Health check never passes. kamal-proxy checks /up once a second until the deploy timeout, with a 5-second timeout per request. If your app listens on 3000 and app_port is still 80, or /up redirects to a login page, the deploy fails. Fix the port, or change proxy.healthcheck.path.
  • SSL does not come up. The DNS record is not pointing at the server yet, or a firewall blocks 443. Fix DNS, wait for it to propagate, and deploy again.
  • Something already on port 80 or 443. Stop and disable any Nginx or Apache installed earlier.
  • More than one web server. Kamal's built-in SSL is single-server only, and multiple servers need a load balancer in front. Use custom certificates or terminate SSL at the load balancer.
  • Bootstrap fails. Usually SSH: confirm ssh root@your-server works with your key and no password prompt.

Choosing the VPS

A Rails app, its background jobs and a small database can start on one KVM VPS and split later. VPSWala's 10 KVM VPS plans run from 1 vCPU and 1 GB RAM (Rs 149/month) up to 48 vCPU and 128 GB RAM, excluding GST, on RAID NVMe, and every plan includes full root access, which is what Kamal expects. For asset compilation and busy Sidekiq queues, the Ryzen 9 9950X VPS plans offer 4 to 10 vCPU with 16 to 64 GB DDR5. Plans are unmanaged unless agreed in writing, and off-server backups are not included, so back up the database separately from the app containers.


Sources

Not sure which size?

Send the stack, get a size.

Tell us the operating system, application stack, current traffic, database size and where it hurts today. You get a sizing recommendation, the matching plan and a price.

Related

More on this.

Get sizing help See VPS plans