Use same domain, but differ with port

Hi guys,

I created both stacks below, which I am able to access using domain n8n.example.com. My manager idea is exposing different ports and host different instances with same domain to allow n8n.example.com:5679, n8n.example.com:5680 and so on, instead of registering the subdomain on respective provider. On other file "n8n.yaml", by changing every referencen8n to "n8n_n", as well as free port "5679 + n", we can host the instance 'n'. But I am not being able to access using protocol https, only http (http://n8n.example.com:$((5679 + n))). Can you understand my use case? Is it reasonable?

docker stack deploy --prune --resolve-image always -c traefik.yaml traefik
docker stack deploy --prune --resolve-image always -c n8n.yaml n8n

traefik.yaml:

services:

  traefik:
    image: traefik:v3.2.3
    command:
      - "--api.dashboard=true"
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik_network"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.transport.respondingTimeouts.idleTimeout=3600"
      - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencryptresolver.acme.storage=/etc/traefik/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencryptresolver.acme.email=josezago@suasvendas.com.br"
      - "--log.level=DEBUG"
      - "--log.format=common"
      - "--log.filePath=/var/log/traefik/traefik.log"
      - "--accesslog=true"
      - "--accesslog.filepath=/var/log/traefik/access-log"

    volumes:
      - "vol_certificates:/etc/traefik/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

    networks:
      - traefik_network

    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host

volumes:
  vol_certificates:
    external: true
    name: volume_certificates

networks:
  traefik_network:
    external: true
    attachable: true
    name: traefik_network

n8n.yaml:

version: '3.8'

services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=postgres
      - DB_POSTGRESDB_PASSWORD=mysecrepwd_shh
      - N8N_SECURE_COOKIE=false
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=me@example.com
      - N8N_BASIC_AUTH_PASSWORD=me@example.com
      - N8N_PROTOCOL=https
      - N8N_PORT=5678
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.n8n.rule=Host(n8n.example.com)"
      - "traefik.http.services.n8n.loadbalancer.server.port=5678"
      - "traefik.http.routers.n8n.entrypoints=https"
      - "traefik.http.routers.n8n.tls=true"
      - "traefik.http.routers.n8n.tls.certresolver=default"
      - "traefik.http.routers.n8n.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    ports:
      - "5679:5678"
    networks:
      - "traefik_network"
    volumes:
      - n8n-data-zaff:/home/node/.n8n

volumes:
  n8n-data:

networks:
  traefik_network:
    external: true
    name: "traefik_network"

You want to proxy via Traefik, so you need to publish the additional ports on Traefik container, create additional Traefik entrypoints in static config and assign them to the according router in dynamic config (labels).

Could you please provide the new content of file traefik.yaml with an additional port, for example, 5679?

Example with 3 Traefik entrypoints.

1 Like

Does it apply when the dockerized application port differs from exposed port? My questions are:

  1. On the example, let us say the application uses 9000, but exposes 9001. How should it be set up? Should we expose port 9001 at all like below?
    ports:
      - "9001:9000"
  1. Even not defining a target like below, should we still be able to direct the incoming traffic from tcp.example.com:9001 to the application, and other targets defined tcp.example.com:9002 as well?
  2. I see, the example defines different subdomains to each service. How could we define uniquelu the domain, but differ by accessing the port?

Usually Traefik is using a Docker network to connect to target services. Within, publishing ports has no impact, you connect to the original port of the app.

If you want to use different ports, you need to publish them on the Traefik container and create additional entrypoints in static config.

Ok, let me get that right: on example [1] you provided, we define a line - --entryPoints.tcp-plain.address=:$PORT on property command AND the port on property ports. Right?

- target: $PORT
  published: $PORT
  protocol: tcp
  mode: host

On my small use case, I want to access as url https://subdomain.domain.com:$PORT multiple instances, since multiple subdomains would require multiple registrations on DNS provider (laziness detected). The internal port is the same, but the access port is different though. How will Swarm differ between the applications if the internal port is the same? It is similar to, example, having multiple Postgres with internal port as 5432 and exposing some other 54XX.

Am I clear?

[1] traefik-best-practice/docker-traefik-tcp/docker-compose.yml at main · bluepuma77/traefik-best-practice · GitHub

Yes, publish port and add entrypoint.

If you have different entrypoints, then you can setup different routers listening to them (exclusively).

If you then simply proxy/forward them to a target service, they could look very alike, except for X-Forwarded-Port header.

Great! I promess this is the last question, yes!? Since I prefer the all-in-one configuration file, how would a target be defined with header X-Forwarded-Port? Despite well-documented, development requires too much experimentation for me to .

I understand we define a new entrypoint, expose the port on same file, define a router on respective service compose file and forward to the respective port service. Ok, so using a single entrypoint pointing to same port allows us to say to Traefik: "Look, we have an entrypoint for services with this port, but we will forward to correct port". The next question is: How?

Can you re-phrase your question?

A port is used by an entrypoint. A router can be assigned to one or more entrypoints. A service can be assigned to one or more routers.

Maybe have a look at this post.

If you use different ports and entrypoints with the same router and same service:

https://sub.example.com/
https://sub.example.com:8443/

then the target service can only figure out by request header which one was used.

Of course you can setup different routers, middlewares and services for each port/entrypoint.

The images on documentation clarify much, but the syntax is somewhat confusing. Traefik works as a front entity, an API gateway. I am able to deploy the application with the desired ports like 3001, 3002, 3003, assign the desired port to respective new entrypoints and also each entrypoint to the service, for example. Can I assign entrypoint custom-port AND websecure? Is this the way how I just use traefik setup to access the 2 different services 3000 and 3001 only by url without modifying the header? I will keep studying the documentation.

You don’t need to modify headers, they will automatically be supplied.

You can assign multiple entrypoints to the same router.

Great !! In summary, to add new service with port accessed websecure url, we:

  1. Create new entrypoint on property command, enable respective tls and expose application port on property ports on traefik compose;
  2. Define router host, assign tls-secured custom entrypoint to router, add port to service load balancer, change the internal on app port to the exposed port on traefik;
  3. Re-deploy traefik and deploy service composes.

Anything else, like enabling tls or certificate resolvers, right?

Thanks for your patience.

@bluepuma77 Update: I performed the suggestions above, but still am unable to access the service through domain https://n8n.example.com:5681. A (unwanted) workaround is exposing on service compose the port, making me able to access http://n8n.example.com:5681, but neglecting traefik safety (tears). Would you reproduce an example with service whoami allowing https instead? :slight_smile:

Share your full Traefik static and dynamic config, and docker-compose.yml if used.

Here it is. BUT I GOT IT CORRECTLY!!!! THANKS!!

traefik.yaml:

services:
  traefik:
    image: traefik:v3.2.3
    hostname: '{{.Node.Hostname}}'
    command:
      - "--api.dashboard=true"
      - "--log.level=INFO"
      - "--providers.swarm.exposedByDefault=false"
      - "--providers.swarm.network=traefik_network"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.websecure.asDefault=true"
      - "--entrypoints.n8n_5679.address=:5679"
      - "--entrypoints.n8n_5679.http.tls=true"
      - "--entrypoints.n8n_5680.address=:5680"
      - "--entrypoints.n8n_5680.http.tls=true"
      - "--entrypoints.n8n_5681.address=:5681"
      - "--entrypoints.n8n_5681.http.tls=true"
      - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencryptresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencryptresolver.acme.storage=/etc/traefik/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencryptresolver.acme.email=`mailme@mail.com"
      - "--log.format=common"
      - "--log.filePath=/var/log/traefik/traefik.log"
      - "--accesslog=true"
      - "--accesslog.filepath=/var/log/traefik/access-log"

    restart: unless-stopped

    security_opt:
      - no-new-privileges:true

    deploy:
      mode: global
      placement:
        constraints:
          - node.role==manager

      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.dashboard.rule=Host(`traefik.app.com`)"
        - "traefik.http.routers.dashboard.entrypoints=websecure"
        - "traefik.http.routers.dashboard.service=api@internal"
        - "traefik.http.routers.dashboard.tls.certresolver=letsencryptresolver"
        - "traefik.http.services.dummy-svc.loadbalancer.server.port=9999"
        - "traefik.http.routers.dashboard.middlewares=myauth"
        - "traefik.http.middlewares.myauth.basicauth.users=test:hashed_password"

    volumes:
      - "/etc/localtime:/etc/localtime:ro"
      - "vol_certificates:/etc/traefik/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

    networks:
      - traefik_network

    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
      - target: 5679
        published: 5679
        mode: host
      - target: 5680
        published: 5680
        mode: host
      - target: 5681
        published: 5681
        mode: host

volumes:
  vol_certificates:
    external: true
    name: volume_certificates

networks:
  traefik_network:
    attachable: true
    name: traefik_network

Custom n8n:

services:
  n8n_5680:
    image: n8nio/n8n:latest
    environment:
      ## Database configuration
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n_5680
      - DB_POSTGRESDB_USER=postgres
      - DB_POSTGRESDB_PASSWORD=postgres

      # Authentication for the editor UI
      - N8N_SECURE_COOKIE=false
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=n8n@mail.com
      - N8N_BASIC_AUTH_PASSWORD=secret_password_shh
      - N8N_PROTOCOL=https
      - N8N_HOST=localhost
      - N8N_PORT=5680
      - WEBHOOK_URL=https://customer.app.com:5680

      # Execution settings
      - EXECUTIONS_MODE=queue

      ## Redis settings
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_BULL_REDIS_PORT=6379
      - QUEUE_BULL_REDIS_DB=2
      - NODE_FUNCTION_ALLOW_EXTERNAL=moment,lodash,moment-with-locales
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=336

      ## Community Nodes
      - N8N_REINSTALL_MISSING_PACKAGES=true
      - N8N_COMMUNITY_PACKAGES_ENABLED=true
      - N8N_NODE_PATH=/home/node/.n8n/nodes

      ## Timezone
      - GENERIC_TIMEZONE=America/Sao_Paulo
      - TZ=America/Sao_Paulo
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
          - node.role == manager
      resources:
        limits:
          cpus: "1"
          memory: 1024M

      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.n8n_5680.rule=Host(`customer.app.com`)"
        - "traefik.http.services.n8n_5680.loadbalancer.server.port=5680"
        - "traefik.http.routers.n8n_5680.entrypoints=websecure,n8n_5680"
        - "traefik.http.routers.n8n_5680.tls=true"
        - "traefik.http.routers.n8n_5680.tls.certresolver=default"

    networks:
      - "traefik_network"
    volumes:
      - n8n-data-zaff:/home/node/.n8n

volumes:
  n8n-data-zaff:

networks:
  traefik_network:
    external: true
    name: "traefik_network"

Great when it works :slight_smile:

I would assign the certResolver directly in entrypoints. No need for tls=true when you assign a certResolver.

And I would assume that only one method (httpChallenge, tlsChallenge) is used.

1 Like

I don't know how to assign the certResolver directly on entrypoint. I removed the tls flag, and also httpChallenge.

It's probably in most examples I provide :wink:

      - --entryPoints.websecure.address=:443
      - --entrypoints.websecure.http.tls.certresolver=myresolver
1 Like

I will be back with more questions in the future. Beware!