<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mooktakim Ahmed</title>
    <description>The latest articles on DEV Community by Mooktakim Ahmed (@mooktakim).</description>
    <link>https://dev.to/mooktakim</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F402496%2Fd296bd07-5077-4030-b2d5-b7310dc114eb.jpg</url>
      <title>DEV Community: Mooktakim Ahmed</title>
      <link>https://dev.to/mooktakim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mooktakim"/>
    <language>en</language>
    <item>
      <title>Building Multi-Tenant SaaS with Rails 8, Caddy, and Kamal</title>
      <dc:creator>Mooktakim Ahmed</dc:creator>
      <pubDate>Tue, 31 Mar 2026 14:01:59 +0000</pubDate>
      <link>https://dev.to/mooktakim/building-multi-tenant-saas-with-rails-8-caddy-and-kamal-2fcn</link>
      <guid>https://dev.to/mooktakim/building-multi-tenant-saas-with-rails-8-caddy-and-kamal-2fcn</guid>
      <description>&lt;p&gt;After publishing the Kamal deployment guide, the most common question was: "How do I handle multiple tenant domains with automatic SSL?"&lt;/p&gt;

&lt;p&gt;kamal-proxy can't do it — every new domain means editing &lt;code&gt;deploy.yml&lt;/code&gt; and redeploying. So I wrote a guide that solves it end-to-end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it covers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why kamal-proxy's SSL doesn't scale for multi-tenant apps&lt;/li&gt;
&lt;li&gt;Running Caddy as a Kamal accessory for on-demand TLS&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;/internal/tls/verify&lt;/code&gt; endpoint — Caddy asks Rails before issuing any cert&lt;/li&gt;
&lt;li&gt;Subdomain data model, validation, and auto-generation&lt;/li&gt;
&lt;li&gt;Constraint-based routing (&lt;code&gt;TenantConstraint&lt;/code&gt; / &lt;code&gt;MainConstraint&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Resolving the current tenant from the request hostname&lt;/li&gt;
&lt;li&gt;Custom domain model with DNS instructions for users&lt;/li&gt;
&lt;li&gt;Full &lt;code&gt;Caddyfile&lt;/code&gt; and &lt;code&gt;deploy.yml&lt;/code&gt; configuration&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;config.hosts.clear&lt;/code&gt; is safe when Caddy gates traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture is based on a real production app (&lt;a href="https://dinehere.ai" rel="noopener noreferrer"&gt;Dinehere&lt;/a&gt; — AI restaurant website builder) where each tenant gets a subdomain and can connect their own domain.&lt;/p&gt;

&lt;p&gt;This is Part 2 of the series. Part 1: &lt;a href="https://mooktakim.com/blog/deploying-rails-with-kamal/" rel="noopener noreferrer"&gt;The Complete Guide to Deploying Rails 8 with Kamal on Hetzner&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>saas</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Complete Guide to Deploying Rails 8 with Kamal on Hetzner</title>
      <dc:creator>Mooktakim Ahmed</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:27:56 +0000</pubDate>
      <link>https://dev.to/mooktakim/the-complete-guide-to-deploying-rails-8-with-kamal-on-hetzner-586e</link>
      <guid>https://dev.to/mooktakim/the-complete-guide-to-deploying-rails-8-with-kamal-on-hetzner-586e</guid>
      <description>&lt;p&gt;I wrote the end-to-end deployment guide I wish existed when I first set up Rails 8 with Kamal.&lt;/p&gt;

&lt;p&gt;Most tutorials cover bits and pieces - here's one that covers everything from a bare server to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it covers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ordering and provisioning a Hetzner dedicated server&lt;/li&gt;
&lt;li&gt;Ansible provisioning with &lt;a href="https://github.com/guillaumebriday/kamal-ansible-manager" rel="noopener noreferrer"&gt;kamal-ansible-manager&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The production Dockerfile (jemalloc, Thruster, multi-stage build)&lt;/li&gt;
&lt;li&gt;Kamal &lt;code&gt;deploy.yml&lt;/code&gt; — every section explained&lt;/li&gt;
&lt;li&gt;The full Solid stack: Solid Queue, Solid Cache, Solid Cable with 4 separate SQLite databases&lt;/li&gt;
&lt;li&gt;ActiveStorage in proxy mode for Cloudflare caching&lt;/li&gt;
&lt;li&gt;First deploy with &lt;code&gt;kamal setup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cloudflare DNS, SSL (Full mode), and CDN caching&lt;/li&gt;
&lt;li&gt;Hetzner Storage Box for off-server backups via Samba/CIFS&lt;/li&gt;
&lt;li&gt;Netdata for server monitoring&lt;/li&gt;
&lt;li&gt;Litestream for continuous SQLite replication&lt;/li&gt;
&lt;li&gt;docker-volume-backup for daily storage snapshots&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;No Postgres. No Redis. No PaaS. Everything runs on a single server for ~36 EUR/month.&lt;/p&gt;

&lt;p&gt;Client → Cloudflare → kamal-proxy → Thruster → Puma → Rails 8&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the full guide here:&lt;/strong&gt; &lt;a href="https://mooktakim.com/blog/deploying-rails-with-kamal/" rel="noopener noreferrer"&gt;The Complete Guide to Deploying Rails 8 with Kamal on&lt;br&gt;
Hetzner&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer any questions in the comments.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>docker</category>
      <category>kamal</category>
    </item>
  </channel>
</rss>
