Issue with https certificates with traefik and tailscale on nixos | ERR Unable to fetch certificate for domain | error="unexpected output: no delimiter"

Background

For about 8months (probably at least since 3monts ago) I’ve been using tailscale with traefik properly.
I essentially create nixos-containers, add them to tailscale network, magicdns creates an entry using their hostname and then with traefik I reverse proxy (a single) service inside the container to the ${MACHINE_NAME}.${TAILNET_NAME}.ts.net domain.
I add traefik to the tailscale tls certificate managers and traefik should then automatically renew the tailscale certificates.
In my config I set traefik to reverse proxy any requests to the service to the tailnet, though websecure (https).
Until about 5 months ago I think tailscale didn’t have the services tab that let you create subdomains and expose a local service to them, so I didn’t use that approach.
My approach was (and still is) essentially exposing only one service to the tailnet per container.
I don’t use tags in tailscale (using them didn’t change the result).

This is the module I’ve made to make the setup easier: https://codeberg.org/BlastboomStrice/dotfiles/src/commit/0d5531a63f2c7094c408cd9e1b2ca26bca70e566/.config/nixos-config/modules/nixos/traefik-tailscale-quicksetup.nix and this is the documentation I’ve written about it

# Example use in configuration.nix:
imports = [
	./../../modules/nixos/traefik-tailscale-quicksetup.nix
];
services.traefik-tailscale-quicksetup = {
	tailscale-hostname = "nixos-main";
	service-name = "stirling-pdf";
	service-port = "8080";
	is-container = true;
};
Info about my system
  • system: "x86_64-linux"
  • host os: Linux 6.18.22, NixOS, 26.05 (Yarara), 26.05.20260414.4bd9165
  • multi-user?: yes
  • sandbox: yes
  • version: nix-env (Lix, like Nix) 2.95.1 System type: x86_64-linux Additional system types: i686-linux, x86_64-v1-linux, x86_64-v2-linux, x86_64-v3-linux Features: gc, signed-caches System configuration file: /etc/nix/nix.conf User configuration files: /home/bs/.config/nix/nix.conf:/nix/store/9iz4xcqj0gi86igw1scirsvrzvhaqs30-plasma-workspace-6.6.4/etc/xdg/nix/nix.conf:/nix/store/0qj094w2czykrlii1h6w8chjfjyx830v-kglobalacceld-6.6.4/etc/xdg/nix/nix.conf:/nix/store/r41bwg0gj691ssgznw1pfwmnkk0dm2jn-baloo-6.25.0/etc/xdg/nix/nix.conf:/home/bs/.config/kdedefaults/nix/nix.conf:/home/bs/.config/kdedefaults/nix/nix.conf:/etc/xdg/nix/nix.conf:/home/bs/.local/share/flatpak/exports/etc/xdg/nix/nix.conf:/var/lib/flatpak/exports/etc/xdg/nix/nix.conf:/home/bs/.nix-profile/etc/xdg/nix/nix.conf:/nix/profile/etc/xdg/nix/nix.conf:/home/bs/.local/state/nix/profile/etc/xdg/nix/nix.conf:/etc/profiles/per-user/bs/etc/xdg/nix/nix.conf:/nix/var/nix/profiles/default/etc/xdg/nix/nix.conf:/run/current-system/sw/etc/xdg/nix/nix.conf Store directory: /nix/store State directory: /nix/var/nix Data directory: /nix/store/cn3vky5kqcy2v7dwd874y7bfhdxbflfb-lix-2.95.1/share
  • nixpkgs: /nix/store/0d1kf07ph2ply64i1y247f9q2an9cw00-source

Issue

Since some months ago, this config stopped working properly. I know that on 2026/02/14 it was working, judging from the logs (using # journalctl -u traefik.service):

-- Boot b54db9253e4c410c8db68eff47a296ce --
Feb 14 18:27:34 testcontainer systemd[1]: Starting Traefik reverse proxy...
Feb 14 18:27:35 testcontainer traefik[254]: 2026-02-14T18:27:35+02:00 WRN Traefik can reject some encoded characters in the request path.When your backend is not full>
Feb 14 18:27:35 testcontainer systemd[1]: Started Traefik reverse proxy.
Feb 14 18:29:00 testcontainer systemd[1]: Stopping Traefik reverse proxy...
Feb 14 18:29:00 testcontainer systemd[1]: traefik.service: Deactivated successfully.
Feb 14 18:29:00 testcontainer systemd[1]: Stopped Traefik reverse proxy.
Feb 14 18:29:00 testcontainer systemd[1]: traefik.service: Consumed 1.406s CPU time, 18.9M memory peak, 2M read from disk.

But I haven’t used the container much, so the issue went undetected, until 2026/04/30 I realised something is wrong:

-- Boot d133312be0084b25bc428cd182f2319c --
Apr 30 17:48:30 testcontainer systemd[1]: Started Traefik web server.
Apr 30 17:48:31 testcontainer traefik[246]: 2026-04-30T17:48:31+03:00 WRN Traefik can reject some encoded characters in the request path.When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986),it is recommended to set these options to `false` to avoid split-view situation.Refer to the documentation for more details: https://doc.traefik.io/traefik/v3.7/migrate/v3/#encoded-characters-configuration-default-values
Apr 30 17:48:31 testcontainer traefik[246]: 2026-04-30T17:48:31+03:00 ERR Unable to fetch certificate for domain "testcontainer.aegean-paridae.ts.net" error="unexpected output: no delimiter" providerName=mytailscaleresolver.tailscale
Apr 30 17:58:31 testcontainer traefik[246]: 2026-04-30T17:58:31+03:00 WRN A new release of Traefik has been found: 3.7.0-rc.3. Please consider updating.
Apr 30 18:03:45 testcontainer systemd[1]: Stopping Traefik web server...
Apr 30 18:03:46 testcontainer systemd[1]: traefik.service: Deactivated successfully.
Apr 30 18:03:46 testcontainer systemd[1]: Stopped Traefik web server.
Apr 30 18:03:46 testcontainer systemd[1]: traefik.service: Consumed 405ms CPU time over 15min 15.417s wall clock time, 107.3M memory peak, 86.5M read from disk.

So the issue here is ERR Unable to fetch certificate for domain "testcontainer.aegean-paridae.ts.net" error="unexpected output: no delimiter" providerName=mytailscaleresolver.tailscale

Apart from that, traefik doesn’t even seem to manage tailscale certificates, doesn’t renew them or ask for tailscale to create new

  • According to the docs https://doc.traefik.io/traefik/v3.0/https/tailscale/#automatic-renewals

    Traefik automatically tracks the expiry date of each Tailscale certificate it fetches, and starts to renew a certificate 14 days before its expiry to match Tailscale daemon renew policy.

    Yet the certificate in the container was about to expire in 13 days (I checked the tailscale admin panel). Also, fresh tailscale certificates expire in 3 months.

  • Deleting the certificates from /var/lib/tailscale/certs/ didn’t trigger a renewal from traefik.

What the docs say

I tried looking at documentations and guides (traefik, tailscale, nixos) and found some related info:

  • https://doc.traefik.io/traefik/v3.0/https/tailscale/#configuration-example
    Traefik docs:

    Enabling Tailscale certificate resolution

    entryPoints:
      web:
        address: ":80"
    
    
      websecure:
        address: ":443"
    
    
    certificatesResolvers:
      myresolver:
        tailscale: {}
    

    Domain from Router’s Rule Example

    ## Dynamic configuration
    http:
      routers:
        blog:
          rule: "Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)"
          tls:
            certResolver: myresolver
    
  • https://tailscale.com/docs/integrations/web-servers/traefik/traefik-certificates
    tailscale docs:

    For example, you can define a certificate resolver in the static configuration, and it automatically enables HTTPS:

    certificatesResolvers:
        myresolver:
            tailscale: {}
    

    Then, for each router or entrypoint where you want to use it, explicitly reference the resolver in the dynamic configuration:

    http:
      routers:
        routertailscale:
          service: "myservice"
          rule: "Host(`example.foo.ts.net`) && Path(`/tailscale`)"
          tls:
            certResolver: tailscale
    
    
      services:
        myservice:
          loadBalancer:
            servers:
            - url: "http://localhost:6060"
    
    • Here btw there seems to be a mistake in the tailscale docs, while they have the same config on the first step, there is a difference in the second step:
      certResolver: myresolver vs certResolver: tailscale
      • Following the 2nd I get Router uses a nonexistent certificate resolver certificateResolver=tailscale in the traefik logs.
  • https://haseebmajid.dev/posts/2024-08-19-setup-tls-certificate-with-traefik-tailscale-on-nixos/
    I took a look at their nixos config and looks like mine
  • https://www.youtube.com/watch?v=sTruD6LISxw
    This is my main inspiration about my config
  • https://github.com/tailscale/tscert/issues/17, https://github.com/tailscale/tailscale/issues/18257
    Someone here had a similar issue with mine and seems to work around it with tailscale services, but I think that didn’t work for me (and I don’t think I want to use the services featuer for now)
  • https://github.com/traefik/traefik/issues/9772
    Here the issue I have is explained; essentially the Unable to fetch certificate for domain - error="unexpected output: no delimiter" should come up if any of the 2 following conditions are true (which in my case neither are):
    • I use the domain of another machine in the traefik config than the domain corresponding to the container I run the setup
    • The domain does not exist in my tailnet

Things I’ve tried

I tried various things, but none succeeded:

  • Changing
    certificatesResolvers = {
      mytailscaleresolver.tailscale = {};
    };
    
    and
    tls = {
      certResolver = "mytailscaleresolver";
    };
    
    to
    certificatesResolvers = {
      myresolver.tailscale = {};
    };
    
    and
    tls = {
      certResolver = "myresolver";
    };
    
    Same issue.
  • Changing
    certificatesResolvers = {
      mytailscaleresolver.tailscale = {};
    };
    
    to
    certificatesResolvers = {
      myresolver.tailscale = { };
    };
    
    Same issue.
  • Changing
    certificatesResolvers = {
      mytailscaleresolver.tailscale = {};
    };
    
    to
    certificatesResolvers = {
      myresolver.tailscale = { };
    };
    
    Same issue.
  • Changing
    tls = {
      certResolver = "myresolver";
    };
    
    to
    tls = {
      certResolver = "tailscale";
    };
    
    Different issue:
    May 03 12:57:48 testcontainer traefik[248]: 2026-05-03T12:57:48+03:00 ERR Router uses a nonexistent certificate resolver certificateResolver=tailscale routerName=stirling-pdf@file
  • Changing
    tls = {
      certResolver = "myresolver";
    };
    
    to
    tls = {
      certResolver = "myresolver.tailscale";
    };
    
    Different issue:
    May 03 12:57:48 testcontainer traefik[248]: 2026-05-03T12:57:48+03:00 ERR Router uses a nonexistent certificate resolver certificateResolver=myresolver.tailscale routerName=stirling-pdf@file
  • Wiping /var/lib/traefik
    Same issue
  • Wiping /var/lib/tailscale
    Same issue, plus deleting the certificates (they are stored in /var/lib/tailscale/certs/) didn’t trigger a renewal from traefik
  • The same issue exists on the server I try to build.

Welp, I figured that traefik is probably more centered around mulitple physical machine clusters and docker, which I don’t seem to use for now.

So, I switched to caddy and it’s wayyy simpler to set up the reverse proxy:
https://codeberg.org/BlastboomStrice/dotfiles/src/.config/nixos-config/modules/nixos/caddy-tailscale-quicksetup.nix

Check out how simpler it is (removed some not-that-related parts for a better comparison):
Traefik:

services.tailscale.permitCertUid = "traefik"; # let traefik use tailscales tls
services.traefik = {
  enable = true; # Enable traefik
  # STATIC config
  staticConfigOptions = {
    log = {
      level = "WARN";
    };
    # api = {}; # enable API handler
    entryPoints = {
      web = {
        address = ":80";
        http.redirections.entryPoint = {
          to = "websecure";
          scheme = "https";
        };
      };
      websecure = {
        address = ":443";
      };
    };
    certificatesResolvers = {
      mytailscaleresolver.tailscale = { };
    };
  };
  # STATIC config END
  # DYNAMIC config BEGIN
  dynamicConfigOptions = {
    http = {
      services.${cfg.service-name}.loadBalancer.servers = [
        {
          url = "http://localhost:${cfg.service-port}/"; # either "localhost" or "127.0.0.1" work
        }
      ];
      routers.${cfg.service-name} = {
        rule = "Host(`${cfg.tailscale-hostname}.aegean-paridae.ts.net`)";
        service = "${cfg.service-name}";
        entrypoints = [ "websecure" ];
        tls = {
          certResolver = "mytailscaleresolver";
        };
      };
    };
  };
  # DYNAMIC config END
};

vs

Caddy:

services.tailscale.permitCertUid = "caddy"; # let caddy use tailscales tls
services.caddy = {
  enable = true; # Enable Caddy
  virtualHosts = {
    # set the virtual host name
    ${cfg.service-name} = {
      # set the hostname
      # eg. nixos-main.aegean-paridae.ts.net
      hostName = "${cfg.tailscale-hostname}.${cfg.tailnet-name}.ts.net";
      # redirect incoming traffic from hostname to the reverse_proxy address:port
      # either "localhost" or "127.0.0.1" (or "::1") work
      extraConfig = ''
        reverse_proxy http://localhost:${cfg.service-port}
        reverse_proxy http://127.0.0.1:${cfg.service-port}
        reverse_proxy http://::1:${cfg.service-port}
      '';
    };
  };
};

or, even simpler:

services.tailscale.permitCertUid = "caddy"; # let caddy use tailscales tls
services.caddy = {
  enable = true; # Enable Caddy
  virtualHosts."${cfg.tailscale-hostname}.aegean-paridae.ts.net".extraConfig = ''
    reverse_proxy http://localhost:${cfg.service-port}
  '';
};

I think you’re on the wrong forum here, traefik’s discourse would probably be able to help you better.

But yeah, I mean caddy is practically just a pre-configured traefik. It’s much less hands-on, if that works better for you just use that.

1 Like

I was mainly looking for a nix config for traefik that pairs with tailscale cuz mine broke somehow, but hmm, maybe not many do a traefik+tailscale combo and oh well, caddy is better for my case anyways haha

(Maybe I should have stated clearer what I wanted, cuz this post looks more like a bug report the way I made it.)

I do use traefik + Tailscale. Though I don’t see how each are related.


Edit

I just checked the OP, and saw that you want to use TS for the certs. I indeed don’t do that. I do DNS based certs with cloud flare.

1 Like