Running multiple instances of cloudflared on Windows
Update: December 2021
Since writing this article, Cloudflare has released a new feature to allow hosting multiple origin endpoints with one installation of cloudflared
.
While this approach is useful, I mainly still use a variation of my technique below because this allows the tunnels to be kept logically separate - for example they can be restarted independently. It's also useful if you want to use the service dependency features of Windows Services to restart a specific tunnel when another service restarts, without affecting other tunnels hosted on the same server. The official Cloudflare approach doesn't provide this flexibility.
Cloudflare Tunnel only supports installing one instance of a Windows Service by default, but you can use Windows' sc.exe
command to manually install as many instances as you need.
Though I only focus on Windows here, it's possible to do the exact same thing with multiple systemd
services on Linux.
Background
Cloudflare Tunnel provides a quick and easy way to setup reverse proxy tunnels for both dev and production. It's enabled by running the cloudflared
daemon locally on Windows, Mac or Linux, and the official docs provide a simple and easy to follow getting started guide.
For production use, having the cloudflared
daemon survive server restarts is a must, and the recommended approach on Windows is to install it as a Windows Service. However there is currently no officially supported way to install multiple instances of the cloudflared
service on Windows.
This is how the Cloudflare docs suggest installing cloudflared
as a service, using their built in helper command:
It seems that the service name Cloudflared is hardcoded and there is no override to allow multiple instances to be installed. Nor do they intelligently check for an existing instance and create a unique service name on the fly. Try this command more than once, even from a copy of cloudflared
in a different directory, and you'll see this error:
Cloudflare's current workaround for this is via some CNAME DNS entries and configuring "multiple hostnames" in your configuration file, but this feels like a bit of a hack and doesn't provide good separation for running multiple independent tunnels from one box. (For example, all your logging for multiple tunnels goes into one file, plus restarts and configuration changes affect all tunnels - not good).
What makes this even worse, is cloudflared
looks in a hard-coded system directory for its YAML config file when running as a service. To override this default directory requires a manual registry edit after installing the service. Yuck!
I found myself wanting either a way to define multiple tunnels in the cloudflared
YAML config file, or have the ability to install multiple instances of the service. And I wanted my config stored somewhere sensible, with the aim to having it deployed/configured dynamically by something like Ansible or Octopus Deploy.
Thankfully, there is a way to do this...
Setting up Multiple Cloudflared Windows Services
We'll use the built-in sc.exe
Windows service control tool to install our cloudflared
services instead of Cloudflare's own helper command.
I'm going to assume you're already familiar with the basics of setting up an Cloudflare Tunnel and authenticating with your Cloudflare account. If you haven't yet got that far, I'd suggest following their quick start guide first.
For this exercise, I am setting up an Cloudflare Tunnel for a standalone microservice API and a separate admin dashboard web app. Both are hosted in IIS and bound to separate ports on localhost. (One of the main advantages of Cloudflare Tunnel is you can just bind all your web apps / APIs to something like http://localhost:8080
, with no firewall ports opened up. Once configured, cloudflared
will reverse proxy all traffic from an outside Cloudflare-hosted domain name at https://mysecureapp.example.com
to that localhost binding.You get TLS for free and no other network config is required).
To setup my two tunnels, first I need to define a configuration file for each tunnel. I prefer to have the cloudflared.exe
binary, my Cloudflare authentication certificate and my config files all in one directory, however it would also be perfectly valid to put cloudflared.exe
in somewhere like c:\Program Files\Cloudflare
and have your config/log files elsewhere.
My cloudflared
configuration files for the two tunnels look like this:
Once saved, my Cloudflared folder contents look like this:
Time to install the Windows services. First I'll install the tunnel service for my microservice API with the following command (adjust the file paths accordingly):
We pass in the unique path to our config file as part of the binary path in the service definition. This allows us to override the default behaviour of looking for config files in the hardcoded system path. Not only do I dislike having app-specific configuration buried deep in obscure windows system folders, it would also prevent us from having multiple service instances, because they'd all look for the same config file. Overriding this config file path when we create the Windows Service is fundamental to unlocking the ability to have as many instances of Cloudflare Tunnels as we like.
All Windows services across the OS must have a unique name (Cloudflared-Api in this case) and a unique display name (Cloudflare Tunnel - Api). These are my choices but you can pick any name and display name to suit. (Wherever you see Cloudflare-Api in the subsequent commands below, you should substitute in whatever name you picked for your service instance.)
You'll note that this service is also set to startup automatically, which is what will let it recover gracefully from server reboots.
When you use the cloudflared
built-in helper command to install the service, it also configures automatic service recovery options, which allow the service to restart itself automatically if it exits unexpectedly. This is optional, but if you wish to replicate these recovery options, you can do so with this command:
Next, I advise setting a description on the service to help you identify what each tunnel is (especially important if you have several of them):
And lastly, if you have any other Windows services that your tunnels should depend on, you can add these dependencies with the following command, to ensure that your tunnels do not start until their dependant services have also started. In my case, because both my API and web dashboard applications are hosted in IIS, I chose to configure my tunnels to depend on the W3SVC IIS service:
That's it. Repeat the above series of commands for your second (and more) services, substituting in unique names, display names and config file paths, and you can setup as many distinct cloudflared
instances as you need.
All that's left is to start your tunnels (repeat for each instance you installed):
You should now be able to see all your instances happily co-existing in the Windows Service list:
And the separate log files are created automatically for each instance:
Now I can safely restart either service without affecting the other, making config changes less disruptive. I could even choose to use a distinct copy of the cloudflared
binary per service, to allow each service to run on its own version of the binary if I wanted that level of granularity.
Summary
Hopefully Cloudflare will bake in some of this functionality into a future version of cloudflared
(edit: they did, sort of) but until then, I hope you find this approach useful.
Note: There currently seems to be a bug causing the cloudflared
service to sometimes display some odd behaviour when stopping it. This seems to happen regardless of whether you use the official cloudflared
helper method for installing the service, or the sc.exe
approach as outline above. For me, it doesn't cause major issues, I can still stop the services and server restarts are unaffected.