<?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: Exiro Studio</title>
    <description>The latest articles on DEV Community by Exiro Studio (@exiro_studio).</description>
    <link>https://dev.to/exiro_studio</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%2F3926745%2F32269c06-f1a7-46a2-832b-bac319743480.png</url>
      <title>DEV Community: Exiro Studio</title>
      <link>https://dev.to/exiro_studio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/exiro_studio"/>
    <language>en</language>
    <item>
      <title>Managing Multiple Cloudflare Tunnel Accounts with Cfmux</title>
      <dc:creator>Exiro Studio</dc:creator>
      <pubDate>Thu, 14 May 2026 18:41:04 +0000</pubDate>
      <link>https://dev.to/exiro_studio/building-cfmux-a-wrapper-around-cloudflared-for-multi-account-tunnel-management-48bf</link>
      <guid>https://dev.to/exiro_studio/building-cfmux-a-wrapper-around-cloudflared-for-multi-account-tunnel-management-48bf</guid>
      <description>&lt;p&gt;Cfmux is a Golang CLI wrapper for cloudflared that helps manage multiple Cloudflare Tunnel accounts using isolated profiles, runtime separation, and systemd integration.&lt;/p&gt;

&lt;p&gt;Most infrastructure tools are born from one thing:&lt;br&gt;
Frustration.&lt;/p&gt;

&lt;p&gt;Cfmux started exactly the same way.&lt;/p&gt;

&lt;p&gt;For a long time, I used Cloudflare Tunnel through &lt;code&gt;cloudflared&lt;/code&gt; for almost everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;self-hosted apps&lt;/li&gt;
&lt;li&gt;development environments&lt;/li&gt;
&lt;li&gt;temporary testing&lt;/li&gt;
&lt;li&gt;internal dashboards&lt;/li&gt;
&lt;li&gt;webhook receivers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And honestly?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cloudflared&lt;/code&gt; is great.&lt;/p&gt;

&lt;p&gt;It is lightweight, stable, simple to deploy, and probably one of the easiest ways to expose local services securely without dealing with public IPs or complicated reverse proxy setups.&lt;/p&gt;

&lt;p&gt;But eventually I hit a workflow problem.&lt;/p&gt;

&lt;p&gt;Not a technical limitation.&lt;/p&gt;

&lt;p&gt;A workflow limitation.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Problem Wasn't Tunneling
&lt;/h1&gt;

&lt;p&gt;The problem appeared when I started managing multiple Cloudflare accounts.&lt;/p&gt;

&lt;p&gt;Something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personal projects&lt;/li&gt;
&lt;li&gt;client infrastructure&lt;/li&gt;
&lt;li&gt;staging environments&lt;/li&gt;
&lt;li&gt;isolated test environments&lt;/li&gt;
&lt;li&gt;temporary deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first it was manageable.&lt;/p&gt;

&lt;p&gt;Then slowly things became messy.&lt;/p&gt;

&lt;p&gt;Credential confusion.&lt;br&gt;
Wrong account usage.&lt;br&gt;
Mixed runtime state.&lt;br&gt;
Configs everywhere.&lt;/p&gt;

&lt;p&gt;And I realized something important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was manually building my own management layer around cloudflared anyway.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So eventually I decided to formalize it.&lt;/p&gt;


&lt;h1&gt;
  
  
  Enter Cfmux
&lt;/h1&gt;

&lt;p&gt;Cfmux is a lightweight wrapper around &lt;code&gt;cloudflared&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Not a replacement.&lt;/p&gt;

&lt;p&gt;Not a competitor.&lt;/p&gt;

&lt;p&gt;It still relies entirely on the official Cloudflare tooling underneath.&lt;/p&gt;

&lt;p&gt;The goal is actually very small:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;make multi-account tunnel workflows cleaner and isolated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;No magic networking layer.&lt;br&gt;
No custom tunnel protocol.&lt;br&gt;
No attempt to reinvent Cloudflare Tunnel.&lt;/p&gt;

&lt;p&gt;Just workflow management.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Core Idea
&lt;/h1&gt;

&lt;p&gt;Instead of relying on one global runtime state, Cfmux isolates everything into profiles.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.cfmux/
├── profiles/
│   ├── personal/
│   ├── client-a/
│   └── staging/
│
└── current-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each profile has isolated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;credentials&lt;/li&gt;
&lt;li&gt;configs&lt;/li&gt;
&lt;li&gt;runtime state&lt;/li&gt;
&lt;li&gt;services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So switching contexts becomes much safer.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Difference Sounds Small...
&lt;/h1&gt;

&lt;p&gt;But in practice it changes the workflow completely.&lt;/p&gt;

&lt;p&gt;Instead of constantly doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared &lt;span class="nt"&gt;--origincert&lt;/span&gt; path/to/cert.pem tunnel list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You simply do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cfmux tunnel list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the active profile already knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which credentials to use&lt;/li&gt;
&lt;li&gt;which runtime state belongs to which account&lt;/li&gt;
&lt;li&gt;which service belongs to which tunnel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It removes a surprising amount of friction.&lt;/p&gt;




&lt;h1&gt;
  
  
  One Important Design Decision
&lt;/h1&gt;

&lt;p&gt;From the beginning, I intentionally avoided turning this into:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“another overengineered dashboard project.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single binary&lt;/li&gt;
&lt;li&gt;CLI-first&lt;/li&gt;
&lt;li&gt;minimal dependencies&lt;/li&gt;
&lt;li&gt;Linux-friendly&lt;/li&gt;
&lt;li&gt;boring architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the stack became:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Golang&lt;/li&gt;
&lt;li&gt;Cobra CLI&lt;/li&gt;
&lt;li&gt;YAML&lt;/li&gt;
&lt;li&gt;systemd integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Very traditional.&lt;/p&gt;

&lt;p&gt;Very infrastructure-oriented.&lt;/p&gt;

&lt;p&gt;And honestly, I think boring stacks are underrated for infra tooling.&lt;/p&gt;




&lt;h1&gt;
  
  
  The MVP Was Extremely Small
&lt;/h1&gt;

&lt;p&gt;The first version only focused on four things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cfmux profile add
cfmux profile use
cfmux tunnel list
cfmux service &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was enough.&lt;/p&gt;

&lt;p&gt;No web dashboard.&lt;br&gt;
No database.&lt;br&gt;
No Kubernetes operator.&lt;/p&gt;

&lt;p&gt;Just practical tooling.&lt;/p&gt;


&lt;h1&gt;
  
  
  One Thing That Surprised Me
&lt;/h1&gt;

&lt;p&gt;The deeper I went into this project, the more I realized:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;infrastructure tools live or die from reliability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not features.&lt;/p&gt;

&lt;p&gt;So instead of aggressively adding functionality, I started focusing heavily on testing.&lt;/p&gt;

&lt;p&gt;Especially integration testing.&lt;/p&gt;

&lt;p&gt;Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;profile isolation&lt;/li&gt;
&lt;li&gt;runtime separation&lt;/li&gt;
&lt;li&gt;corrupt registry handling&lt;/li&gt;
&lt;li&gt;passthrough execution&lt;/li&gt;
&lt;li&gt;filesystem safety&lt;/li&gt;
&lt;li&gt;drift detection&lt;/li&gt;
&lt;li&gt;config protection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because infrastructure software stops being “just code” once people trust it with production environments.&lt;/p&gt;


&lt;h1&gt;
  
  
  Why I Still Respect cloudflared
&lt;/h1&gt;

&lt;p&gt;One thing I want to make very clear:&lt;/p&gt;

&lt;p&gt;Cfmux exists because of a workflow gap.&lt;/p&gt;

&lt;p&gt;Not because &lt;code&gt;cloudflared&lt;/code&gt; is bad.&lt;/p&gt;

&lt;p&gt;Actually the opposite.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cloudflared&lt;/code&gt; is good enough that I wanted a better management experience around it instead of replacing it entirely.&lt;/p&gt;

&lt;p&gt;And honestly?&lt;/p&gt;

&lt;p&gt;If Cloudflare eventually adds proper native multi-account workflow management someday, that would be great.&lt;/p&gt;

&lt;p&gt;Cfmux was never intended to fight the ecosystem.&lt;/p&gt;

&lt;p&gt;It was built to make one specific workflow cleaner.&lt;/p&gt;


&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;Currently installation/update is just:&lt;br&gt;
&lt;/p&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://raw.githubusercontent.com/ExiroStudio/cfmux/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  GitHub
&lt;/h1&gt;

&lt;p&gt;Project repository:&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/ExiroStudio/cfmux" rel="noopener noreferrer"&gt;https://github.com/ExiroStudio/cfmux&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My GitHub: &lt;a href="https://github.com/ExiroStudio" rel="noopener noreferrer"&gt;https://github.com/ExiroStudio&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;One thing I’ve learned from building this project:&lt;/p&gt;

&lt;p&gt;Some of the best software ideas are not revolutionary.&lt;/p&gt;

&lt;p&gt;Sometimes they are just:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“this workflow annoys me enough that I should fix it properly.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And weirdly enough, those projects often become the most useful ones.&lt;/p&gt;

&lt;p&gt;Especially when you end up using them yourself every single day.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>opensource</category>
      <category>go</category>
    </item>
  </channel>
</rss>
