<?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>Forem: Paul Ng</title>
    <description>The latest articles on Forem by Paul Ng (@gaupoit).</description>
    <link>https://forem.com/gaupoit</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%2F100529%2F6f5b913f-c2be-4f22-9fb7-5f5cbbb95863.png</url>
      <title>Forem: Paul Ng</title>
      <link>https://forem.com/gaupoit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gaupoit"/>
    <language>en</language>
    <item>
      <title>How to Run Multiple Cloudflare Tunnels on a Single Server</title>
      <dc:creator>Paul Ng</dc:creator>
      <pubDate>Sun, 04 Jan 2026 13:40:32 +0000</pubDate>
      <link>https://forem.com/gaupoit/how-to-run-multiple-cloudflare-tunnels-on-a-single-server-4kf9</link>
      <guid>https://forem.com/gaupoit/how-to-run-multiple-cloudflare-tunnels-on-a-single-server-4kf9</guid>
      <description>&lt;p&gt;Cloudflare (CF) Tunnel is a powerful way to expose your local services to the internet without opening ports or configuring firewalls. But what if you need to run multiple tunnels on the same server?&lt;/p&gt;

&lt;p&gt;This guide covers two approaches and shows you how to manage them with systemd.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach #1: Single tunnel with Multiple Services
&lt;/h3&gt;

&lt;p&gt;If all services belong to the same CF Account, use one tunnel with multiple ingress rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/cloudflared/config.yml
  tunnel: my-tunnel-id
  credentials-file: /etc/cloudflared/my-tunnel-id.json

  ingress:
    - hostname: app1.example.com
      service: http://localhost:3000
    - hostname: app2.example.com
      service: http://localhost:8080
    - hostname: api.example.com
      service: http://localhost:5000
    - service: http_status:404
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The catch-all rule at the end is required.&lt;/p&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cloudflared service install
sudo systemctl start cloudflared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach will take less resource and easier to for monitoring and management. However, we need all services share the same account.&lt;/p&gt;

&lt;p&gt;So when you need separate tunnels, approach #2 will help with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach #2: Multiple Tunnel Instances
&lt;/h3&gt;

&lt;p&gt;First, check your existing tunnel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ps aux | grep cloudflared | grep -v grep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or checking systemd service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl status cloudflared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create additional service files&lt;/p&gt;

&lt;p&gt;By default, it will creates the file /etc/systemd/system/cloudflared.service. For additional tunnels, create new service files manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vi /etc/systemd/system/cloudflared-tunnel2.service
[Unit]
Description=Cloudflare Tunnel 2
After=network-online.target
Wants=network-online.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Service]
Type=notify
ExecStart=/usr/local/bin/cloudflared --no-autoupdate tunnel run --token YOUR_TUNNEL_TOKEN_HERE
Restart=on-failure
RestartSec=5
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
Replace YOUR_TUNNEL_TOKEN_HERE with your actual tunnel token from Cloudflare Zero Trust dashboard.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, enable and start the new service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl enable cloudflared-tunnel2
sudo systemctl start cloudflared-tunnel2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally verify both tunnels are running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl status cloudflared
sudo systemctl status cloudflared-tunnel2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tips: instead of inline tokens, we can use config files for each tunnel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir -p /etc/cloudflared/tunnel2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create /etc/cloudflared/tunnel2/config.yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tunnel: your-tunnel-id
credentials-file: /etc/cloudflared/tunnel2/credentials.json

ingress:
- hostname: service.example.com
  service: http://localhost:4000
- service: http_status:404
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the service file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ExecStart=/usr/local/bin/cloudflared --no-autoupdate tunnel --config /etc/cloudflared/tunnel2/config.yml run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Vibe coding is great! But we don't need to build anything from scratch.</title>
      <dc:creator>Paul Ng</dc:creator>
      <pubDate>Sat, 03 Jan 2026 13:05:13 +0000</pubDate>
      <link>https://forem.com/gaupoit/vibe-coding-is-great-but-we-dont-need-to-build-anything-from-scratch-2adb</link>
      <guid>https://forem.com/gaupoit/vibe-coding-is-great-but-we-dont-need-to-build-anything-from-scratch-2adb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy8rbs2n1fklrthkfuez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzy8rbs2n1fklrthkfuez.png" alt="Don't Reinvent the Wheel" width="800" height="462"&gt;&lt;/a&gt;&lt;br&gt;
Vibe coding with Claude is addictive. You describe what you want, Claude writes it, you iterate. It feels productive. But here's what I learned the hard way: You're often reinventing wheels that have thousands of GitHub stars.&lt;/p&gt;

&lt;p&gt;That's why I have build a "Don't Reinvent the Wheel" skill, namely programming-advisor. It triggers whenever I say things like: "I want to build a...", "Help me create...", "Can you code...".&lt;/p&gt;

&lt;p&gt;Before Claude writes a single line of code, it searches for existing solutions and show me a comparison table.&lt;/p&gt;

&lt;p&gt;For example, when I ask "I want to build a Hacker News CLI to browse top stories from terminal". It will reply by following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ## 🔍 Existing Solutions Found

  ### NPM Packages
  - hackernews-cli: Browse HN from terminal | 200+ stars
  - haxor-news: Full featured HN CLI in Python | 3.8K stars
  - hn-cli: Minimal HN reader | 150+ stars

  ### Open Source Tools
  - circumflex: Beautiful HN terminal client | 1K+ stars
  - clx: TUI for Hacker News | 400+ stars

  ## 📊 Build vs Buy Comparison

  | Option | Type | Cost | Setup | Maintenance | Est. Tokens |
  |--------|------|------|-------|-------------|-------------|
  | circumflex | CLI tool | Free | 1 min | None | 0 |
  | haxor-news | CLI tool | Free | 2 min | None | 0 |
  | Custom CLI | Build | Free | 2-4 hrs | You own it | ~40-80K |

  ## 💡 Recommendation

  Install circumflex or haxor-news. Both are mature, well-maintained,
  and have features you'll forget to implement:

  - Collapsible comments
  - Vim keybindings
  - Color themes
  - Story filtering
  - Offline caching
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I installed circumflex in 10 seconds. It does everything I wanted plus features I didn't know I needed.&lt;br&gt;
Tokens saved: ~50K. Time saved: 3 hours.&lt;/p&gt;

&lt;p&gt;Wanna try it?&lt;/p&gt;

&lt;p&gt;Install the skill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Add the marketplace
/plugin marketplace add gaupoit/programming-advisor

# Install the plugin
/plugin install programming-advisor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or try the demo project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/gaupoit/dont-reinvent-the-wheel-demo.git
cd dont-reinvent-the-wheel-demo
claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy Coding/Vibing&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>skill</category>
      <category>claude</category>
    </item>
  </channel>
</rss>
