
Cloudflare Tunnels on FreeBSD 14.3
Learn how to securely expose FreeBSD services to the internet using Cloudflare Tunnels. This step-by-step guide shows you how to set up and configure tunnels to protect your FreeBSD applications with Cloudflare's security features.
At Conrad Research, we deploy most services on FreeBSD because we leverage its jails subsystem to easily create immutable infrastructure. This is a great way to deploy services because it allows us to have a very predictable and stable environment. We also use Cloudflare Tunnels and Zero Trust to secure our services. As such, we've had to figure out how to get Cloudflare and FreeBSD to play well together. Step by step instructions below:
Steps
1. Make sure that dependencies are installed.
pkg install git go gmake
2. Make sure the FreeBSD ports tree is up to date.
Check if the ports tree is installed. If the directory is empty then you need to install the ports tree.
ls /usr/ports
To install the ports tree, run the following as root:
git clone --depth 1 https://git.freebsd.org/ports.git /usr/ports
Update the ports tree.
cd /usr/ports
git pull --ff-only
3. Create a local port of the cloudflared port.
This way we can maintain our own version of the cloudflared port and keep it up to date. The official port usually lags behind the latest releases a fair bit.
mkdir -p /usr/local/ports/net/cloudflared
Copy the official cloudflared port to our local ports directory.
cp -r /usr/ports/net/cloudflared/* /usr/local/ports/net/cloudflared
Modify the cloudflared service template to not have a default config file path. This is because it defaults to a config.yml file but in step 8 we'll use a token file instead of a config file.
sed -i '' 's|--config ${cloudflared_conf}||' /usr/local/ports/net/cloudflared/files/cloudflared.in
4. Update the local port to the latest version of cloudflared.
Find the latest version of cloudflared on the Cloudflare Tunnels GitHub repository then change to the local ports directory.
cd /usr/local/ports/net/cloudflared
Use your preferred text editor to open the Makefile and update the DISTVERSION variable to the latest version AND change the golang version within the USES variable to the current version of golang installed on the system. You can find the current version of golang installed on the system by running the following command:
go version
Note: When updating the golang version only use the major version number. For example, if the current version of golang is 1.24.4, then the USES variable should be
cpe go:1.24,modules
.
The port uses a checksum to verify the downloaded source tarball. Since you’re changing the version, you need to regenerate the distinfo file. This fetches the latest source tarball and updates the checksum.
make makesum
WARNING: As of the time of writing, there is a bug in cloudflared that prevents it from building on FreeBSD. If you encounter this error, see Patching section below and then skip to step 5 below.
Build and install the local port.
make install clean
5. Verify that cloudflare is installed.
cloudflared --version
6. Add a 'service', or similar, group to the system if it doesn't already exist.
This group should have permissions to write to the pid and log files. Make sure to use the same group in the next step when we create a user.
pw groupadd service
chown root:service /var/run
chown root:service /var/log
chmod 770 /var/run
chmod 770 /var/log
7. Add a user and assign permissions.
Make sure to add the user without login capabilities and assign to the 'service' group.
pw useradd cloudflared -d /nonexistent -s /sbin/nologin -c "Cloudflare Tunnel Service Account" -g service
Change the ownership of the cloudflared binary to the cloudflared user.
chown cloudflared:service /usr/local/bin/cloudflared
chmod 740 /usr/local/bin/cloudflared
Add the cloudflared service to the system startup and make sure it runs as the cloudflared user.
sysrc -f /etc/rc.conf cloudflared_enable="YES"
sysrc -f /etc/rc.conf cloudflared_user="cloudflared"
sysrc -f /etc/rc.conf cloudflared_group="service"
sysrc -f /etc/rc.conf cloudflared_mode="tunnel run"
8. Let's create a tunnel.
Go to the Cloudflare Zero Trust dashboard
Navigate to Networks > Tunnels and create a new tunnel.
Copy the token into a file. It'll be a JSON web token string starting with 'eyJ...'
mkdir -p /usr/local/etc/cloudflared
echo 'eyJ...' > /usr/local/etc/cloudflared/token.txt
chown cloudflared:service /usr/local/etc/cloudflared/token.txt
chmod 640 /usr/local/etc/cloudflared/token.txt
IMPORTANT: Leave the webpage for the tunnel open in your browser. You'll need it for a future step.
Add a environment variable to the system to store the token. See documentation for the 'TUNNEL_TOKEN_FILE' parameter and the 'NO_AUTOUPDATE' parameter.
sysrc -f /etc/rc.conf cloudflared_env="NO_AUTOUPDATE=true TUNNEL_TOKEN_FILE=/usr/local/etc/cloudflared/token.txt"
Start the cloudflared service.
service cloudflared start
service cloudflared status
NOTE: If the service failed to start, check the logs for errors.
tail -f /var/log/cloudflared.log
Return to the dashboard webpage for the tunnel. You should see your server now connected under 'Connectors' with a status of 'Connected'.
You're now ready to click 'next' on the dashboard webpage to complete the tunnel's configuration.
NOTE: The tunnel is now running and can be accessed from the internet.
Conclusion
Restart your server and verify that the tunnel is still running after restart.
service cloudflared status
Enjoy your FreeBSD server with Cloudflare Tunnels!
Cheers 🥂
Patching
As of the time of writing, there is a bug in cloudflare that prevents it from building on FreeBSD. If you encounter this error, see the following commands to patch the cloudflared port. The patch is from this pull request.
Download the patch and change to the local ports directory.
cd /usr/local/ports/net/cloudflared/files
curl -L https://github.com/cloudflare/cloudflared/pull/1526.patch -o 1526.patch
cd /usr/local/ports/net/cloudflared
Update the Makefile to apply the patch. Using your preferred text editor, open the Makefile and add the following line to file prior to the '.include <bsd.port.mk>' line.
do-patch:
@cd ${WRKSRC} && ${PATCH} -p1 < ${FILESDIR}/1526.patch
NOTE: If using the above patch number, make sure to set the DISTVERSION variable to 2025.8.1
Regenerate the distinfo file. This will display multiple selection screens. Selecting the defaults is fine.
make makesum
Build and install the local port.
make install clean
Return to step 5 above and continue with the rest of the steps.