Ejabberd configuration on Docker Swarm with Traefik as proxy

I have a Docker Swarm cluster which has been running just fine to
deploy a few dozen web applications. I'm now trying to get ejabberd
(XMPP server) to run on the same cluster, which seems to be deployed
correctly (as seen on the dashboard), but I can only get to connect to
the http router.

Because of ejabberd using STARTTLS, I need to have the certificate
generation done on ejabberd and traefik passing the connection through
(that means, not terminating TLS on itself).

This is my stack to deploy traefik:

services:
  traefik:
    image: traefik:v3.3
    command:
      - --providers.swarm.endpoint=unix:///var/run/docker.sock
      - --providers.swarm.exposedbydefault=false
      - --providers.swarm.network=traefik-net

      - --core.defaultRuleSyntax=v2

      - --certificatesresolvers.le-http.acme.httpchallenge=true
      - --certificatesresolvers.le-http.acme.httpchallenge.entrypoint=web
      - --certificatesresolvers.le-http.acme.email=emailme@example.com
      - --certificatesresolvers.le-http.acme.storage=/etc/traefik/acme-https.json

      - --entrypoints.web.address=:80
      - --entryPoints.web.allowACMEByPass=true
      - --entrypoints.websecure.address=:443
      - --entrypoints.xmpp-s2s.address=:5269
      - --entrypoints.xmpp-c2s.address=:5222

      - --api
      - --log
      - --log.level=DEBUG
      - --log.format=json
      - --accesslog
      - --accesslog.addinternals
      - --accesslog.format=json

      - --tracing
      - --tracing.addinternals

      - --metrics.prometheus=true
      - --metrics.prometheus.addEntryPointsLabels=true
      - --metrics.prometheus.addrouterslabels=true
      - --metrics.prometheus.addServicesLabels=true
      
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
      - target: 5222
        published: 5222
        mode: host
      - target: 5269
        published: 5269
        mode: host
        
    networks:
      - traefik-net

    volumes:
      - traefik-data:/etc/traefik
      - /var/run/docker.sock:/var/run/docker.sock:ro

These are the relevant parts in case I want to deploy ejabberd on xmpp.example.com:

services:
  ejabberd:
    image: ghcr.io/processone/ejabberd

    networks:
      - traefik-net

    deploy:
      labels:
        traefik.enable: "true"
        traefik.swarm.network: traefik-net

        traefik.tcp.routers.xmpp-s2s.rule: HostSNI(`xmpp.example.com`)
        traefik.tcp.routers.xmpp-s2s.entrypoints: xmpp-s2s
        traefik.tcp.routers.xmpp-s2s.tls: "true"
        traefik.tcp.routers.xmpp-s2s.tls.passthrough: "true"
        traefik.tcp.routers.xmpp-s2s.service: xmpp-s2s
        traefik.tcp.services.xmpp-s2s.loadbalancer.server.port: 5269

        traefik.tcp.routers.xmpp-c2s.rule: HostSNI(`xmpp.example.com`)
        traefik.tcp.routers.xmpp-c2s.entrypoints: xmpp-c2s
        traefik.tcp.routers.xmpp-c2s.tls: "true"
        traefik.tcp.routers.xmpp-c2s.tls.passthrough: "true"        
        traefik.tcp.routers.xmpp-c2s.service: xmpp-c2s
        traefik.tcp.services.xmpp-c2s.loadbalancer.server.port: 5222

        traefik.http.routers.xmpp-http.rule: Host(`xmpp.example.com`) && (PathPrefix("/admin") || PathPrefix("/bosh"))
        traefik.http.routers.xmpp-http.entrypoints: websecure
        traefik.http.routers.xmpp-http.tls: "true"
        traefik.http.routers.xmpp-http.tls.certresolver: le-http
        traefik.http.routers.xmpp-http.service: xmpp-http
        traefik.http.services.http.loadbalancer.server.port: 5433

        traefik.http.routers.xmpp-acme.rule: Host(`xmpp.example.com`) && PathPrefix("/.well-known/acme-challenge")
        traefik.http.routers.xmpp-acme.entrypoints: web
        traefik.http.routers.xmpp-acme.priority: 10000
        traefik.http.routers.xmpp-acme.service: xmpp-acme
        traefik.http.services.xmpp-acme.loadbalancer.server.port: 5280   

I am fairly certain that the issue is that the tcp routers are not
receiving the connection, because the XMPP client can connect to the
server and port, but ejabberd logs are not showing anything.

Any help would be deeply appreciated.

AFAIK StartTLS is not a regular TLS TCP connection.

You can probably only use Traefik with HostSNI(`*`) without enabling any TLS at all, just default plain TCP passthrough.

I can not use HostSNI(`*`) because I am planning to host many different deployments of ejabberd, so I need a rule on the domain to know which service to route to, no?

For multiple services with StartTLS, you would need to use different ports. Or find a proxy (placed in front of Traefik) that supports it.

Or ensure Jabber clients and server use regular TLS with HostSNI.

Yeah, I looked at sslh but it didn't seem to provide an easy way to configure the routing of independent services, like I can do with Traefik configuration via labels.

I am a bit lost on how to go next. My original design was to have one ejabberd server per deployment, but maybe I should deploy one single ejabberd in the whole cluster and then set up the different deployments as virtual hosts.