<?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: Asim Hayat</title>
    <description>The latest articles on DEV Community by Asim Hayat (@asim07).</description>
    <link>https://dev.to/asim07</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%2F779983%2F3f527244-4345-4615-9698-6b7e6925e1b1.jpg</url>
      <title>DEV Community: Asim Hayat</title>
      <link>https://dev.to/asim07</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/asim07"/>
    <language>en</language>
    <item>
      <title>OpenClaw WhatsApp: How to Lock Your Bot to One Specific Group</title>
      <dc:creator>Asim Hayat</dc:creator>
      <pubDate>Sat, 07 Mar 2026 02:41:06 +0000</pubDate>
      <link>https://dev.to/asim07/openclaw-whatsapp-how-to-lock-your-bot-to-one-specific-group-16pb</link>
      <guid>https://dev.to/asim07/openclaw-whatsapp-how-to-lock-your-bot-to-one-specific-group-16pb</guid>
      <description>&lt;p&gt;The first time I added OpenClaw to a WhatsApp group, it replied to every single message. My friend sent "lol" and the bot wrote a three-paragraph response. Someone shared a meme and it started analyzing the image. My phone was blowing up.&lt;/p&gt;

&lt;p&gt;Then people started DMing my number directly because OpenClaw uses &lt;em&gt;your&lt;/em&gt; WhatsApp account, not a separate bot number. The bot was happily chatting with complete strangers.&lt;/p&gt;

&lt;p&gt;I turned it off within 10 minutes.&lt;/p&gt;

&lt;p&gt;The fix took another 10 minutes once I figured out the config. Here's exactly how to lock your OpenClaw bot so it only responds in one specific group, only when mentioned, and ignores everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: OpenClaw Talks to Everyone
&lt;/h2&gt;

&lt;p&gt;OpenClaw doesn't have its own WhatsApp number. It piggybacks on yours. That means every DM to your number hits the bot, every group you're in becomes a potential chat room, and the bot eats API tokens on messages you never wanted it to see.&lt;/p&gt;

&lt;p&gt;If you're running Anthropic's API, that "lol" response probably cost you $0.02. Multiply that by 50 messages in an active group and you're burning through credits for nothing.&lt;/p&gt;

&lt;p&gt;The config file that controls all of this lives at &lt;code&gt;~/.openclaw/openclaw.json&lt;/code&gt;. Every change requires a restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's lock it down layer by layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: Lock DMs With allowFrom
&lt;/h2&gt;

&lt;p&gt;First, stop random people from chatting with your bot. The &lt;code&gt;allowFrom&lt;/code&gt; field controls who can send DMs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"whatsapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only numbers in this array can DM the bot. Everyone else gets ignored. No response, no error message, nothing. The bot just silently drops the message.&lt;/p&gt;

&lt;p&gt;Use full international format with the &lt;code&gt;+&lt;/code&gt; prefix. If you want to add a second person:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"allowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+15559876543"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick tip: if you don't want to hardcode numbers, use &lt;code&gt;dmPolicy: "pairing"&lt;/code&gt; instead. This makes unknown senders receive a pairing code that you have to approve before they can chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"whatsapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dmPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pairing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pairing requests expire after 1 hour and are capped at 3 pending requests per channel, so nobody can flood your bot with approval requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: Set Group Policy
&lt;/h2&gt;

&lt;p&gt;Now for groups. OpenClaw has two independent filtering layers for groups, and this is where most people get confused.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;groupPolicy&lt;/code&gt; controls &lt;em&gt;who&lt;/em&gt; can trigger the bot in groups. The &lt;code&gt;groups&lt;/code&gt; block controls &lt;em&gt;which&lt;/em&gt; groups the bot responds in. They work together but they're configured separately.&lt;/p&gt;

&lt;p&gt;Start with &lt;code&gt;groupPolicy&lt;/code&gt;. You have two options:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"allowlist"&lt;/code&gt; means only senders listed in &lt;code&gt;groupAllowFrom&lt;/code&gt; can trigger the bot in any group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"whatsapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"groupPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allowlist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"groupAllowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;"open"&lt;/code&gt; means anyone in an allowed group can trigger the bot (with mention gating):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"whatsapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"groupPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which one should you use? If it's a personal bot and only you should trigger it, go with &lt;code&gt;"allowlist"&lt;/code&gt; and put your number in &lt;code&gt;groupAllowFrom&lt;/code&gt;. If it's a team or client bot where multiple people need access, use &lt;code&gt;"open"&lt;/code&gt; and rely on group-level controls plus mention gating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: Target One Specific Group
&lt;/h2&gt;

&lt;p&gt;This is the part everyone wants. You need the group's JID (Jabber ID), which looks like &lt;code&gt;120363407119785089@g.us&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To find your group JID, run this command and then send a message in the target group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw logs &lt;span class="nt"&gt;--follow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch the logs for a line like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inbound message 120363407119785089@g.us -&amp;gt; +15551234567 (group)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That long number before &lt;code&gt;@g.us&lt;/code&gt; is your group JID. Copy it.&lt;/p&gt;

&lt;p&gt;Now configure the &lt;code&gt;groups&lt;/code&gt; block to only allow that specific group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"whatsapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dmPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pairing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"groupPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"groups"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"120363407119785089@g.us"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"requireMention"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the &lt;code&gt;groups&lt;/code&gt; block exists and doesn't include a &lt;code&gt;"*"&lt;/code&gt; wildcard, it acts as an allowlist. Only the groups listed here will get responses. Every other group is silently ignored.&lt;/p&gt;

&lt;p&gt;Setting &lt;code&gt;requireMention: true&lt;/code&gt; means someone has to @mention the bot or reply to one of its messages before it responds. Without this, the bot replies to every message in the group.&lt;/p&gt;

&lt;p&gt;If you want the bot to reply to everything in that one group without needing a mention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"120363407119785089@g.us"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requireMention"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only do this for small, private groups. In a 50-person group with &lt;code&gt;requireMention: false&lt;/code&gt;, your API bill will be ugly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Config: One Group, Locked Down
&lt;/h2&gt;

&lt;p&gt;Here's the complete config that locks DMs, targets one group, and requires mentions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"whatsapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dmPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pairing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"groupPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"groups"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"120363407119785089@g.us"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"requireMention"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ackReaction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"emoji"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"👀"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"direct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mentions"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sendReadReceipts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"groupChat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"mentionPatterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"@openclaw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openclaw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@bot"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"historyLimit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me break down the extras.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ackReaction&lt;/code&gt; makes the bot react with 👀 when it receives your message, so you know it's processing. In groups, it only reacts on mentions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sendReadReceipts&lt;/code&gt; shows the blue ticks so you know the bot read the message.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mentionPatterns&lt;/code&gt; is the list of words that trigger the bot in groups. Add whatever your team will naturally type. Replying to a bot message also counts as a mention automatically.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;historyLimit: 50&lt;/code&gt; means the bot sees the last 50 messages in the group for context, even ones it didn't respond to. Useful for "catch up" questions like "summarize what everyone said."&lt;/p&gt;

&lt;h2&gt;
  
  
  Known Bug: groupPolicy "allowlist" Can Block Everything
&lt;/h2&gt;

&lt;p&gt;Here's something that tripped me up and has caught others too. If you use &lt;code&gt;groupPolicy: "allowlist"&lt;/code&gt; but leave &lt;code&gt;groupAllowFrom&lt;/code&gt; empty, &lt;strong&gt;all group messages are blocked&lt;/strong&gt; even if you've configured specific groups in the &lt;code&gt;groups&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;This happens because OpenClaw checks &lt;code&gt;groupAllowFrom&lt;/code&gt; first. If it's empty, the message is dropped before it ever reaches the group-level config.&lt;/p&gt;

&lt;p&gt;There's an open &lt;a href="https://github.com/openclaw/openclaw/issues/6558" rel="noopener noreferrer"&gt;GitHub issue (#6558)&lt;/a&gt; about this. The workaround is simple: use &lt;code&gt;groupPolicy: "open"&lt;/code&gt; and let the &lt;code&gt;groups&lt;/code&gt; block handle which groups are allowed. That's what I use in my config above.&lt;/p&gt;

&lt;p&gt;If you specifically need sender-level filtering in groups where only certain people can trigger the bot, you need to set both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"groupPolicy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allowlist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"groupAllowFrom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"+15551234567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+15559876543"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"groups"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"120363407119785089@g.us"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"requireMention"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing It
&lt;/h2&gt;

&lt;p&gt;After saving your config and running &lt;code&gt;openclaw restart&lt;/code&gt;, test each layer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. DM test.&lt;/strong&gt; Have a friend who is not in your &lt;code&gt;allowFrom&lt;/code&gt; list DM your WhatsApp number. The bot should not respond.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Wrong group test.&lt;/strong&gt; Send a message in a group that is not in your &lt;code&gt;groups&lt;/code&gt; config. The bot should ignore it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Right group, no mention.&lt;/strong&gt; Send a normal message in the target group without mentioning the bot. If &lt;code&gt;requireMention&lt;/code&gt; is true, the bot should stay quiet. The message still gets stored for context though.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Right group, with mention.&lt;/strong&gt; Send "&lt;a class="mentioned-user" href="https://dev.to/openclaw"&gt;@openclaw&lt;/a&gt; what's the weather?" in the target group. The bot should respond.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Check logs.&lt;/strong&gt; Run &lt;code&gt;openclaw logs --follow&lt;/code&gt; and watch for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Blocked group message (group not in allowlist)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Skipped (no mention)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These confirm each layer is working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Run the doctor.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This surfaces risky or misconfigured DM policies and group settings. If anything looks off, it'll tell you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you want&lt;/th&gt;
&lt;th&gt;Config&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Only I can DM the bot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allowFrom: ["+1555..."]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown people get pairing flow&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dmPolicy: "pairing"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bot only active in one group&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;groups: { "JID@g.us": { ... } }&lt;/code&gt; (no &lt;code&gt;"*"&lt;/code&gt; entry)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bot requires @mention in group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;requireMention: true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anyone in the group can trigger&lt;/td&gt;
&lt;td&gt;&lt;code&gt;groupPolicy: "open"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Only specific people can trigger&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;groupPolicy: "allowlist"&lt;/code&gt; + &lt;code&gt;groupAllowFrom&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bot reacts when processing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ackReaction: { emoji: "👀" }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In the next article, I'll cover how to switch AI models in OpenClaw without losing your conversation history. Useful when you want Claude for complex tasks and Gemini Flash for quick replies to save on API costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Need Help Setting This Up?
&lt;/h2&gt;

&lt;p&gt;I deploy and maintain OpenClaw professionally. AWS, Docker, Nginx, Telegram, WhatsApp, multi-agent, the works.&lt;/p&gt;

&lt;p&gt;150+ projects delivered. 100% job success rate. Top Rated Plus on Upwork.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://www.upwork.com/freelancers/muhammadasimh2" rel="noopener noreferrer"&gt;Hire me on Upwork&lt;/a&gt; → Email: &lt;a href="mailto:chaudhryasim5@gmail.com"&gt;chaudhryasim5@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>tutorial</category>
      <category>automation</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Stop Exposing Port 18789: How I Secure OpenClaw on AWS EC2 with Nginx and SSL</title>
      <dc:creator>Asim Hayat</dc:creator>
      <pubDate>Sat, 28 Feb 2026 01:09:27 +0000</pubDate>
      <link>https://dev.to/asim07/stop-exposing-port-18789-how-i-secure-openclaw-on-aws-ec2-with-nginx-and-ssl-do9</link>
      <guid>https://dev.to/asim07/stop-exposing-port-18789-how-i-secure-openclaw-on-aws-ec2-with-nginx-and-ssl-do9</guid>
      <description>&lt;p&gt;I've set up OpenClaw on EC2 multiple times now — for myself and for clients. And every single time I take over someone else's setup, I find the same thing: port 18789 wide open to the internet, no SSL, no reverse proxy, running as root.&lt;/p&gt;

&lt;p&gt;Bitsight found over 30,000 exposed OpenClaw instances. Attackers aren't even bothering with prompt injection — they're connecting directly to the gateway WebSocket and getting full access.&lt;/p&gt;

&lt;p&gt;This is how I deploy OpenClaw properly. Gateway on loopback, Nginx in front, SSL via Let's Encrypt, and EC2 security groups that only allow what's necessary. Takes about 30 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 0.0.0.0 Binding Will Get You Hacked
&lt;/h2&gt;

&lt;p&gt;When you run through OpenClaw's onboarding wizard, it asks you about the gateway bind mode. A lot of people pick "LAN" because they want to access the dashboard from their browser. That binds the gateway to &lt;code&gt;0.0.0.0&lt;/code&gt; — meaning every network interface on the machine.&lt;/p&gt;

&lt;p&gt;On an EC2 instance, that means anyone on the internet can hit port 18789 directly. Your gateway is now a public endpoint. Even with token auth enabled, you're one misconfiguration away from someone controlling your AI agent, reading your sessions, and executing commands on your server.&lt;/p&gt;

&lt;p&gt;The OpenClaw docs are clear about this: &lt;em&gt;never expose the gateway unauthenticated on 0.0.0.0&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's what we're going to do instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet → Port 443 (HTTPS) → Nginx → 127.0.0.1:18789 → OpenClaw Gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gateway only talks to localhost. Nginx handles SSL termination and proxies requests. Nothing else is exposed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An AWS EC2 instance running Ubuntu 24.04 (t3.medium or bigger — OpenClaw needs at least 4GB RAM for comfortable operation)&lt;/li&gt;
&lt;li&gt;A domain name pointed to your EC2 instance's public IP (I'll use &lt;code&gt;openclaw.yourdomain.com&lt;/code&gt; as an example)&lt;/li&gt;
&lt;li&gt;SSH access to your instance&lt;/li&gt;
&lt;li&gt;An API key from your LLM provider (Anthropic, OpenAI, Gemini, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Install OpenClaw
&lt;/h2&gt;

&lt;p&gt;SSH into your EC2 instance and install OpenClaw:&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;# Make sure Node 22+ is installed&lt;/span&gt;
node &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Install OpenClaw&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://openclaw.ai/install.sh | bash

&lt;span class="c"&gt;# Run the onboarding wizard&lt;/span&gt;
openclaw onboard &lt;span class="nt"&gt;--install-daemon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During onboarding, when it asks about the gateway bind mode, pick &lt;strong&gt;loopback&lt;/strong&gt;. This is critical. It binds the gateway to &lt;code&gt;127.0.0.1&lt;/code&gt; only.&lt;/p&gt;

&lt;p&gt;If you've already set up OpenClaw with a different bind mode, fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;gateway.bind loopback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw gateway status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the gateway running on &lt;code&gt;127.0.0.1:18789&lt;/code&gt;. If you try to access &lt;code&gt;http://your-ec2-ip:18789&lt;/code&gt; from your browser right now, it should refuse the connection. That's exactly what we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Set Up Gateway Authentication
&lt;/h2&gt;

&lt;p&gt;Even though we're binding to loopback, set up token auth. Defense in depth:&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;# Generate a strong random token&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Your gateway token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Save this somewhere safe."&lt;/span&gt;

&lt;span class="c"&gt;# Set the token&lt;/span&gt;
openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;gateway.auth.mode token
openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;gateway.auth.token &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also configure trusted proxies so OpenClaw correctly reads the real client IP from Nginx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;gateway.trustedProxies &lt;span class="s1"&gt;'["127.0.0.1"]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, OpenClaw sees Nginx's IP for every request instead of the actual client IP. That breaks rate limiting and IP-based access controls.&lt;/p&gt;

&lt;p&gt;Restart the gateway to apply changes:&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="nb"&gt;sudo &lt;/span&gt;systemctl restart openclaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Install and Configure Nginx
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the Nginx config for OpenClaw:&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="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/openclaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;openclaw_gateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="nf"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;18789&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;openclaw.yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Redirect all HTTP to HTTPS (we'll set up SSL next)&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$server_name$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;openclaw.yourdomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# SSL certs will be added by Certbot in the next step&lt;/span&gt;
    &lt;span class="c1"&gt;# ssl_certificate /etc/letsencrypt/live/openclaw.yourdomain.com/fullchain.pem;&lt;/span&gt;
    &lt;span class="c1"&gt;# ssl_certificate_key /etc/letsencrypt/live/openclaw.yourdomain.com/privkey.pem;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://openclaw_gateway&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# WebSocket support — required for OpenClaw dashboard&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;"upgrade"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Pass real client IP to OpenClaw&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;# Longer timeouts for WebSocket connections&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_read_timeout&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_send_timeout&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;openclaw.yourdomain.com&lt;/code&gt; with your actual domain.&lt;/p&gt;

&lt;p&gt;Enable the site and test the config:&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="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; /etc/nginx/sites-enabled/default  &lt;span class="c"&gt;# Remove default site&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;nginx -t&lt;/code&gt; shows errors, fix them before moving on. Don't skip this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: SSL with Let's Encrypt
&lt;/h2&gt;

&lt;p&gt;Install Certbot and get your certificate:&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; certbot python3-certbot-nginx

&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; openclaw.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certbot will ask for your email, agree to terms, and then automatically modify your Nginx config to add the SSL certificate paths. It also sets up auto-renewal.&lt;/p&gt;

&lt;p&gt;Verify auto-renewal works:&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="nb"&gt;sudo &lt;/span&gt;certbot renew &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now uncomment the SSL lines in your Nginx config (Certbot usually does this automatically, but double-check):&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="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it: open &lt;code&gt;https://openclaw.yourdomain.com&lt;/code&gt; in your browser. You should see the OpenClaw Control UI with a valid SSL certificate and no browser warnings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Configure OpenClaw for the Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;Since we're accessing the Control UI through a domain now (not localhost), OpenClaw needs to know about the allowed origin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw config &lt;span class="nb"&gt;set &lt;/span&gt;gateway.controlUi.allowedOrigins &lt;span class="s1"&gt;'["https://openclaw.yourdomain.com"]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Watch out for a known gotcha&lt;/strong&gt;: if you see an error about &lt;code&gt;controlUI&lt;/code&gt; (capital UI), ignore that — the correct config key uses lowercase &lt;code&gt;controlUi&lt;/code&gt;. This is a known bug in the error message as of version 2026.2.24.&lt;/p&gt;

&lt;p&gt;Restart the gateway:&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="nb"&gt;sudo &lt;/span&gt;systemctl restart openclaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: systemd Service with Auto-Restart
&lt;/h2&gt;

&lt;p&gt;If you used &lt;code&gt;openclaw onboard --install-daemon&lt;/code&gt;, systemd should already be configured. But let's make sure it's set up properly for production:&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="nb"&gt;sudo &lt;/span&gt;nano /etc/systemd/system/openclaw.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;OpenClaw Gateway&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ubuntu&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;NODE_ENV=production&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/openclaw gateway --port 18789&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;
&lt;span class="py"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Restart=always&lt;/strong&gt; — if it crashes, systemd brings it back&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RestartSec=10&lt;/strong&gt; — waits 10 seconds before restart (avoids crash loops)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User=ubuntu&lt;/strong&gt; — don't run as root&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enable and start:&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="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;openclaw
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start openclaw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's running:&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="nb"&gt;sudo &lt;/span&gt;systemctl status openclaw
journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; openclaw &lt;span class="nt"&gt;-f&lt;/span&gt;  &lt;span class="c"&gt;# Live logs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a server reboot, OpenClaw will start automatically. Test this by running &lt;code&gt;sudo reboot&lt;/code&gt; and checking after the instance comes back up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Lock Down EC2 Security Groups
&lt;/h2&gt;

&lt;p&gt;Go to your AWS Console → EC2 → Security Groups → select the group attached to your instance.&lt;/p&gt;

&lt;p&gt;Set inbound rules to:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;Your IP only&lt;/td&gt;
&lt;td&gt;SSH access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;HTTP → redirects to HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;HTTPS (Nginx + SSL)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's it. Three ports. Everything else is blocked.&lt;/p&gt;

&lt;p&gt;Specifically, make sure port 18789 is &lt;strong&gt;NOT&lt;/strong&gt; in your security group rules. There's zero reason for it to be accessible from the internet since Nginx proxies everything through 443.&lt;/p&gt;

&lt;p&gt;If you want to be extra careful with SSH, restrict port 22 to only your IP address instead of &lt;code&gt;0.0.0.0/0&lt;/code&gt;. You can find your IP at &lt;code&gt;whatismyip.com&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Checklist
&lt;/h2&gt;

&lt;p&gt;Run through this before you call it done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;openclaw gateway status&lt;/code&gt; shows running on 127.0.0.1:18789&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;curl http://localhost:18789&lt;/code&gt; from the server returns a response&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;curl http://YOUR_EC2_PUBLIC_IP:18789&lt;/code&gt; from your laptop returns connection refused&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;https://openclaw.yourdomain.com&lt;/code&gt; loads the dashboard with valid SSL&lt;/li&gt;
&lt;li&gt;[ ] WebSocket connection works (dashboard is interactive, not just static)&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;sudo systemctl status openclaw&lt;/code&gt; shows active&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;sudo reboot&lt;/code&gt; → wait → OpenClaw comes back automatically&lt;/li&gt;
&lt;li&gt;[ ] EC2 security group only has ports 22, 80, 443&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;openclaw security audit&lt;/code&gt; runs clean (or with only expected warnings)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What This Setup Looks Like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
    │
    ▼
┌──────────────────────┐
│  EC2 Security Group  │
│  Only 22, 80, 443    │
└──────────┬───────────┘
           │
           ▼
┌──────────────────────┐
│     Nginx (:443)     │
│  SSL termination     │
│  WebSocket proxy     │
└──────────┬───────────┘
           │ proxy_pass
           ▼
┌──────────────────────┐
│  OpenClaw Gateway    │
│  127.0.0.1:18789     │
│  Token auth enabled  │
└──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No exposed ports. SSL everywhere. Auto-restart on crash or reboot. Gateway only talks to localhost.&lt;/p&gt;

&lt;p&gt;This is the same setup I run for myself and deploy for clients. Nothing fancy, just proper infrastructure that doesn't leave your AI agent exposed to the internet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Need Help Setting This Up?
&lt;/h2&gt;

&lt;p&gt;I deploy and maintain OpenClaw professionally — AWS, Docker, Nginx, Telegram, WhatsApp, multi-agent, the works.&lt;/p&gt;

&lt;p&gt;150+ projects delivered. 100% job success rate. Top Rated Plus on Upwork.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://www.upwork.com/freelancers/~01a4546b4bd4769843?mp_source=share" rel="noopener noreferrer"&gt;Hire me on Upwork&lt;/a&gt;&lt;br&gt;
→ Email: &lt;a href="mailto:chaudhryasim5@gmail.com"&gt;chaudhryasim5@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>aws</category>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
