{ config, lib, pkgs, ... }: let # Cloudflare tunnel definitions tunnels = { warp = { remoteHost = "warp.ftl.host"; # Cloudflare hostname remoteTarget = "localhost:22"; # Where traffic routes to localBindPort = 4401; # Local port exposed by cloudflared logDir = "/var/lib/cloudflared"; # Directory for logs }; }; in { # Install cloudflared environment.systemPackages = with pkgs; [ cloudflared ]; # Define cloudflared user and group at the top level users.users.cloudflared = { isSystemUser = true; group = "cloudflared"; description = "Cloudflared service user"; home = "/var/lib/cloudflared"; createHome = true; }; users.groups.cloudflared = {}; # Ensure /var/lib/cloudflared exists with proper permissions systemd.tmpfiles.rules = [ "d /var/lib/cloudflared 0755 cloudflared cloudflared -" ]; # Systemd services per tunnel systemd.services = lib.mapAttrs (name: tunnel: { description = "Cloudflared Access TCP Tunnel for ${tunnel.remoteHost}"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { # The key setting to ensure the service stays running Type = "simple"; ExecStart = "${pkgs.cloudflared}/bin/cloudflared access tcp --hostname ${tunnel.remoteHost} --url ${tunnel.remoteTarget} --port ${toString tunnel.localBindPort} --logfile ${tunnel.logDir}/${name}.log"; # Automatic restart Restart = "always"; RestartSec = "5s"; # Run as cloudflared user User = "cloudflared"; Group = "cloudflared"; # Standard outputs StandardOutput = "journal"; StandardError = "journal"; }; }) tunnels; # Documentation metadata meta.doc = '' This module defines persistent cloudflared TCP tunnels using access tcp. Add tunnels by appending to the `tunnels` attribute set. Required keys: - remoteHost: The public hostname exposed via Cloudflare Tunnel. - remoteTarget: The internal service to forward traffic to (e.g. localhost:22). - localBindPort: The port to expose locally (e.g. 4401). - logDir: Directory path for log files. ''; }