<?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: Kai Ren</title>
    <description>The latest articles on DEV Community by Kai Ren (@kairen_ops).</description>
    <link>https://dev.to/kairen_ops</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4071526%2F2710b965-2d4c-45eb-977b-7d228bd06831.png</url>
      <title>DEV Community: Kai Ren</title>
      <link>https://dev.to/kairen_ops</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kairen_ops"/>
    <language>en</language>
    <item>
      <title>Docker WAF vs single-binary WAF — how I choose for a single VPS</title>
      <dc:creator>Kai Ren</dc:creator>
      <pubDate>Mon, 10 Aug 2026 23:43:01 +0000</pubDate>
      <link>https://dev.to/kairen_ops/docker-waf-vs-single-binary-waf-how-i-choose-for-a-single-vps-5g9c</link>
      <guid>https://dev.to/kairen_ops/docker-waf-vs-single-binary-waf-how-i-choose-for-a-single-vps-5g9c</guid>
      <description>&lt;p&gt;Homelab and small-prod threads keep landing on the same question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I want a self-hosted WAF. SafeLine/Docker, or something smaller?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There’s no universal winner. Here’s the decision frame I use after running both styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two honest shapes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A) Docker / appliance WAF
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt; SafeLine, BunkerWeb-style stacks, ModSecurity-in-compose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wins&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Polished dashboards&lt;/li&gt;
&lt;li&gt;Familiar deploy (&lt;code&gt;docker compose up&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Often opinionated defaults for homelab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More containers, volumes, upgrades&lt;/li&gt;
&lt;li&gt;Another network path to reason about&lt;/li&gt;
&lt;li&gt;Host already busy? You feel it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  B) Single-binary reverse proxy + WAF
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt; Caddy + Coraza yourself, or a packaged product like &lt;a href="https://www.tiyisec.com/" rel="noopener noreferrer"&gt;Tiyi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wins&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One process, one upgrade artifact&lt;/li&gt;
&lt;li&gt;Fits “I already think in reverse proxies”&lt;/li&gt;
&lt;li&gt;Easy to put on a tiny VPS / metal box&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Costs&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI may be simpler than a full appliance&lt;/li&gt;
&lt;li&gt;You still need to understand CRS false positives&lt;/li&gt;
&lt;li&gt;Not a substitute for L3/L4 scrubbing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Decision checklist
&lt;/h2&gt;

&lt;p&gt;Pick &lt;strong&gt;Docker appliance&lt;/strong&gt; if most of these are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re fine operating Compose long-term&lt;/li&gt;
&lt;li&gt;You want a rich security console first&lt;/li&gt;
&lt;li&gt;Multiple teammates will click through the UI daily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick &lt;strong&gt;single binary&lt;/strong&gt; if most of these are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;code&gt;scp&lt;/code&gt; / package / one systemd unit&lt;/li&gt;
&lt;li&gt;Caddy (or Caddy-like) is already your front door&lt;/li&gt;
&lt;li&gt;You’re optimizing for fewer failure domains on one node&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I run when constraints are “one box, CRS, low glue”
&lt;/h2&gt;

&lt;p&gt;I use Tiyi: Caddy + Coraza + OWASP CRS 4 + admin UI/CLI in one Go binary, SQLite, free on a single node.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Site: &lt;a href="https://www.tiyisec.com/" rel="noopener noreferrer"&gt;https://www.tiyisec.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;90s demo: &lt;a href="https://www.tiyisec.com/assets/video/tiyi-demo.mp4" rel="noopener noreferrer"&gt;https://www.tiyisec.com/assets/video/tiyi-demo.mp4&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/zzmzm/tiyi" rel="noopener noreferrer"&gt;https://github.com/zzmzm/tiyi&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not arguing it’s “more secure than SafeLine.” It’s a &lt;strong&gt;packaging&lt;/strong&gt; choice: less stack, more “proxy with CRS.”&lt;/p&gt;

&lt;h2&gt;
  
  
  False friends
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;“WAF = anti-DDoS”&lt;/strong&gt; — no. Different layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“CRS with defaults forever”&lt;/strong&gt; — you’ll tune. Budget time for false positives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;“More containers = more secure”&lt;/strong&gt; — sometimes just more surface.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical recommendation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Write down the threat: scanners/SQLi vs volumetric floods vs credential stuffing.&lt;/li&gt;
&lt;li&gt;Pick the packaging that matches how you already operate.&lt;/li&gt;
&lt;li&gt;Prove a block (SQLi probe + log) before you polish dashboards.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you comment your stack (Caddy/Nginx/Traefik, Docker yes/no, one node vs cluster), I’ll suggest the boring default for that case.&lt;/p&gt;

</description>
      <category>docker</category>
    </item>
    <item>
      <title>Self-hosted WAF without Docker — Caddy + Coraza + CRS in one binary</title>
      <dc:creator>Kai Ren</dc:creator>
      <pubDate>Mon, 10 Aug 2026 23:42:42 +0000</pubDate>
      <link>https://dev.to/kairen_ops/self-hosted-waf-without-docker-caddy-coraza-crs-in-one-binary-5c6h</link>
      <guid>https://dev.to/kairen_ops/self-hosted-waf-without-docker-caddy-coraza-crs-in-one-binary-5c6h</guid>
      <description>&lt;p&gt;If you already reverse-proxy with Caddy (or you’re tired of ModSecurity glue), there’s a boring option that works well on a single VPS: put &lt;strong&gt;OWASP CRS&lt;/strong&gt; in front of your apps and keep the ops surface small.&lt;/p&gt;

&lt;p&gt;This post is the path I use when I want app-layer filtering (SQLi/XSS/scanners) &lt;strong&gt;without&lt;/strong&gt; a compose stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you actually need
&lt;/h2&gt;

&lt;p&gt;For most small/medium self-hosted setups:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;What does &lt;em&gt;not&lt;/em&gt; solve it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Edge / scrubbing&lt;/td&gt;
&lt;td&gt;Volumetric DDoS, big botnets&lt;/td&gt;
&lt;td&gt;A reverse-proxy WAF alone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App-layer WAF&lt;/td&gt;
&lt;td&gt;SQLi, XSS, scanner junk, bad bots that look like browsers&lt;/td&gt;
&lt;td&gt;Geo-IP blocks forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth / rate limits&lt;/td&gt;
&lt;td&gt;Credential stuffing, noisy clients&lt;/td&gt;
&lt;td&gt;CRS by itself&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Don’t sell yourself a WAF as a DDoS product. Use CRS for the application junk; keep rate limits / challenges / an upstream scrubber for the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The packaging tradeoff
&lt;/h2&gt;

&lt;p&gt;Common self-hosted WAF paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Docker WAFs&lt;/strong&gt; (SafeLine and friends) — great UI, more moving parts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roll your own&lt;/strong&gt; — Caddy/Nginx + Coraza/ModSecurity + CRS + dashboards — flexible, glue-heavy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single binary&lt;/strong&gt; — reverse proxy + Coraza + CRS + admin UI in one process.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ve been using option 3 via &lt;a href="https://www.tiyisec.com/" rel="noopener noreferrer"&gt;Tiyi&lt;/a&gt;: one Go binary, SQLite, local admin UI/CLI, free on a single node. Demo (install → site → block SQLi):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tiyisec.com/assets/video/tiyi-demo.mp4" rel="noopener noreferrer"&gt;https://www.tiyisec.com/assets/video/tiyi-demo.mp4&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install (Linux)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://www.tiyisec.com/install.sh | sh
tiyi run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the admin UI, set the admin password on first run, then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add an upstream (your real app)&lt;/li&gt;
&lt;li&gt;Add a site (hostname → upstream)&lt;/li&gt;
&lt;li&gt;Keep CRS enabled (default path)&lt;/li&gt;
&lt;li&gt;Hit the site with a trivial SQLi probe and confirm a block + security log&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Exact CLI flags change with versions — the site/quickstart is the source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  A minimal “did it work?” check
&lt;/h2&gt;

&lt;p&gt;Once the site is live on the proxy port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# should be blocked / challenged by CRS&lt;/span&gt;
curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"http://127.0.0.1:18080/?q=1%27%20OR%20%271%27%3D%271"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then check the security/events view in the UI (or CLI logs). If nothing shows up, you’re usually testing the wrong host/port or bypassing the proxy.&lt;/p&gt;

&lt;h2&gt;
  
  
  When &lt;em&gt;not&lt;/em&gt; to use this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You need a full cloud WAAP / bot management suite&lt;/li&gt;
&lt;li&gt;You must stay on Nginx/Apache modules only&lt;/li&gt;
&lt;li&gt;You want multi-region active-active with a shared control plane on day one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a homelab or a single production node where you already like Caddy semantics, the “one binary + CRS” shape is hard to beat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Site: &lt;a href="https://www.tiyisec.com/" rel="noopener noreferrer"&gt;https://www.tiyisec.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/zzmzm/tiyi" rel="noopener noreferrer"&gt;https://github.com/zzmzm/tiyi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo video: &lt;a href="https://www.tiyisec.com/assets/video/tiyi-demo.mp4" rel="noopener noreferrer"&gt;https://www.tiyisec.com/assets/video/tiyi-demo.mp4&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re comparing this to SafeLine/BunkerWeb/ModSecurity stacks, comment with your constraints (Docker OK? Caddy required? UI needed?) — happy to map tradeoffs.&lt;/p&gt;

</description>
      <category>go</category>
    </item>
  </channel>
</rss>
