Cloudflared
Cloudflared is a command line client that can be used to establish a network tunnel from the Cloudflare network to a server.
Cloudflare Tunnel
Prerequisites
- A Cloudflare account
- A domain registered on Cloudflare (see https://developers.cloudflare.com/fundamentals/manage-domains/add-site/)
If you do not wish to install cloudflared you may use a nix-shell to use it for the following steps without adding it to your configuration.
$ nix-shell -p cloudflared
You will need to log in to your Cloudflare account through the command line, the following command will open a web browser to allow you to log in:
$ cloudflared -- tunnel login
You can use the link it provides on any machine that has a browser in order to get the needed cert.pem file. Afterwards you will need to create the tunnel from the machine.
$ cloudflared tunnel create <tunnel-name-of-choice>
The command will output the tunnel ID in the format 00000000-0000-0000-0000-000000000000, which will be needed for setting up the tunnel service. The following example uses flakes and sops-nix to hide the credentials file secret.
{
inputs,
config,
...
}: {
imports = [
inputs.sops-nix.nixosModules.sops
];
sops = {
age.keyFile = "/home/username/.config/sops/age/keys.txt";
defaultSopsFile = secrets/secrets.yaml;
secrets."cloudflared-creds" = {
owner = "username";
group = "users";
mode = "0400";
};
};
services.cloudflared = {
enable = true;
tunnels = {
"00000000-0000-0000-0000-000000000000" = {
credentialsFile = "${config.sops.secrets.cloudflared-creds.path}";
default = "http_status:404";
};
};
};
}
Then you can use the dashboard to add your public hosts (will need to convert the new tunnel to dashboard-managed).
Alternatively, save the cert.pem to cloudflared user's %home%/.cloudflared/cert.pem, and instead of using dashboard specify ingress rules in your configuration.nix like this:
{
services.cloudflared = {
enable = true;
tunnels = {
"00000000-0000-0000-0000-000000000000" = {
credentialsFile = "${config.sops.secrets.cloudflared-creds.path}";
ingress = {
"*.domain1.com" = {
service = "http://localhost:80";
path = "/*.(jpg|png|css|js)";
};
"*.domain2.com" = "http://localhost:80";
};
default = "http_status:404";
};
};
};
}
Troubleshooting
At the moment (2025), for support of browser rendering of the tunnels, this line is required:
services.openssh.settings.Macs = [
# Current defaults:
"hmac-sha2-512-etm@openssh.com"
"hmac-sha2-256-etm@openssh.com"
"umac-128-etm@openssh.com"
# Added:
"hmac-sha2-256"
];
The issue has been reported on Github