8 cfg = config.services.cloudflared;
11 connectTimeout = lib.mkOption {
12 type = with lib.types; nullOr str;
16 Timeout for establishing a new TCP connection to your origin server. This excludes the time taken to establish TLS, which is controlled by [tlsTimeout](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#tlstimeout).
20 tlsTimeout = lib.mkOption {
21 type = with lib.types; nullOr str;
25 Timeout for completing a TLS handshake to your origin server, if you have chosen to connect Tunnel to an HTTPS server.
29 tcpKeepAlive = lib.mkOption {
30 type = with lib.types; nullOr str;
34 The timeout after which a TCP keepalive packet is sent on a connection between Tunnel and the origin server.
38 noHappyEyeballs = lib.mkOption {
39 type = with lib.types; nullOr bool;
43 Disable the “happy eyeballs” algorithm for IPv4/IPv6 fallback if your local network has misconfigured one of the protocols.
47 keepAliveConnections = lib.mkOption {
48 type = with lib.types; nullOr int;
52 Maximum number of idle keepalive connections between Tunnel and your origin. This does not restrict the total number of concurrent connections.
56 keepAliveTimeout = lib.mkOption {
57 type = with lib.types; nullOr str;
61 Timeout after which an idle keepalive connection can be discarded.
65 httpHostHeader = lib.mkOption {
66 type = with lib.types; nullOr str;
70 Sets the HTTP `Host` header on requests sent to the local service.
74 originServerName = lib.mkOption {
75 type = with lib.types; nullOr str;
79 Hostname that `cloudflared` should expect from your origin server certificate.
83 caPool = lib.mkOption {
84 type = with lib.types; nullOr (either str path);
88 Path to the certificate authority (CA) for the certificate of your origin. This option should be used only if your certificate is not signed by Cloudflare.
92 noTLSVerify = lib.mkOption {
93 type = with lib.types; nullOr bool;
97 Disables TLS verification of the certificate presented by your origin. Will allow any certificate from the origin to be accepted.
101 disableChunkedEncoding = lib.mkOption {
102 type = with lib.types; nullOr bool;
106 Disables chunked transfer encoding. Useful if you are running a WSGI server.
110 proxyAddress = lib.mkOption {
111 type = with lib.types; nullOr str;
113 example = "127.0.0.1";
115 `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen address for that proxy.
119 proxyPort = lib.mkOption {
120 type = with lib.types; nullOr int;
124 `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen port for that proxy. If set to zero, an unused port will randomly be chosen.
128 proxyType = lib.mkOption {
138 `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures what type of proxy will be started. Valid options are:
140 - `""` for the regular proxy
141 - `"socks"` for a SOCKS5 proxy. Refer to the [tutorial on connecting through Cloudflare Access using kubectl](https://developers.cloudflare.com/cloudflare-one/tutorials/kubectl/) for more information.
147 options.services.cloudflared = {
148 enable = lib.mkEnableOption "Cloudflare Tunnel client daemon (formerly Argo Tunnel)";
150 user = lib.mkOption {
151 type = lib.types.str;
152 default = "cloudflared";
153 description = "User account under which Cloudflared runs.";
156 group = lib.mkOption {
157 type = lib.types.str;
158 default = "cloudflared";
159 description = "Group under which cloudflared runs.";
162 package = lib.mkPackageOption pkgs "cloudflared" { };
164 tunnels = lib.mkOption {
168 type = lib.types.attrsOf (
169 lib.types.submodule (
173 inherit originRequest;
175 credentialsFile = lib.mkOption {
176 type = lib.types.str;
180 See [Credentials file](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms/#credentials-file).
185 enabled = lib.mkOption {
186 type = with lib.types; nullOr bool;
191 See [Connect from WARP to a private network on Cloudflare using Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/tutorials/warp-to-tunnel/).
196 default = lib.mkOption {
197 type = lib.types.str;
199 Catch-all service if no ingress matches.
203 example = "http_status:404";
206 ingress = lib.mkOption {
215 inherit originRequest;
217 service = lib.mkOption {
218 type = with lib.types; nullOr str;
221 Service to pass the traffic.
223 See [Supported protocols](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#supported-protocols).
225 example = "http://localhost:80, tcp://localhost:8000, unix:/home/production/echo.sock, hello_world or http_status:404";
228 path = lib.mkOption {
229 type = with lib.types; nullOr str;
234 If not specified, all paths will be matched.
236 example = "/*.(jpg|png|css|js)";
248 See [Ingress rules](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/).
251 "*.domain.com" = "http://localhost:80";
252 "*.anotherone.com" = "http://localhost:80";
262 "00000000-0000-0000-0000-000000000000" = {
263 credentialsFile = "/tmp/test";
266 service = "http://localhost:80";
269 default = "http_status:404";
275 config = lib.mkIf cfg.enable {
276 systemd.targets = lib.mapAttrs' (
278 lib.nameValuePair "cloudflared-tunnel-${name}" {
279 description = "Cloudflare tunnel '${name}' target";
280 requires = [ "cloudflared-tunnel-${name}.service" ];
281 after = [ "cloudflared-tunnel-${name}.service" ];
282 unitConfig.StopWhenUnneeded = true;
284 ) config.services.cloudflared.tunnels;
286 systemd.services = lib.mapAttrs' (
289 filterConfig = lib.attrsets.filterAttrsRecursive (
298 filterIngressSet = lib.filterAttrs (_: v: builtins.typeOf v == "set");
299 filterIngressStr = lib.filterAttrs (_: v: builtins.typeOf v == "string");
301 ingressesSet = filterIngressSet tunnel.ingress;
302 ingressesStr = filterIngressStr tunnel.ingress;
304 fullConfig = filterConfig {
306 "credentials-file" = tunnel.credentialsFile;
307 warp-routing = filterConfig tunnel.warp-routing;
308 originRequest = filterConfig tunnel.originRequest;
315 // lib.getAttr key (filterConfig (filterConfig ingressesSet))
316 ) (lib.attrNames ingressesSet))
319 service = lib.getAttr key ingressesStr;
320 }) (lib.attrNames ingressesStr))
321 ++ [ { service = tunnel.default; } ];
324 mkConfigFile = pkgs.writeText "cloudflared.yml" (builtins.toJSON fullConfig);
326 lib.nameValuePair "cloudflared-tunnel-${name}" ({
329 "network-online.target"
333 "network-online.target"
335 wantedBy = [ "multi-user.target" ];
339 ExecStart = "${cfg.package}/bin/cloudflared tunnel --config=${mkConfigFile} --no-autoupdate run";
340 Restart = "on-failure";
343 ) config.services.cloudflared.tunnels;
345 users.users = lib.mkIf (cfg.user == "cloudflared") {
352 users.groups = lib.mkIf (cfg.group == "cloudflared") {
357 meta.maintainers = with lib.maintainers; [