Trying to securely install n8n / traefik on a rootless docker

I’ve tried to make the default docker n8n + traefik setup a bit more secure.
By doing so, I think I broke something.

On a rootless docker, that’s my yml file:

volumes:
  n8n_storage:
  traefik_storage:

services:
  traefik:
    image: "traefik"
    restart: always
    command:
      - "--api.dashboard=true"
      - --log.level=INFO
      - --accesslog=true
      - "--api=true"
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:8140"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:8143"
      - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
      - "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
      - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.mytlschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "--providers.file.directory=/etc/traefik/dynamic"
      - "--providers.file.watch=true"
    ports:
      - "8140:80"
      - "8143:443"
    volumes:
      - ./traefik_storage/letsencrypt:/letsencrypt
      - ./traefik/config:/etc/traefik/dynamic
      - /run/user/1001/docker.sock:/var/run/docker.sock:ro

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    labels:
      - traefik.enable=true
      - traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
      - traefik.http.routers.n8n.tls=true
      - traefik.http.routers.n8n.entrypoints=websecure
      - traefik.http.routers.n8n.tls.certresolver=mytlschallenge
      - traefik.http.middlewares.n8n.headers.SSLRedirect=true
      - traefik.http.middlewares.n8n.headers.STSSeconds=315360000
      - traefik.http.middlewares.n8n.headers.browserXSSFilter=true
      - traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
      - traefik.http.middlewares.n8n.headers.forceSTSHeader=true
      - traefik.http.middlewares.n8n.headers.SSLHost=${DOMAIN_NAME}
      - traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
      - traefik.http.middlewares.n8n.headers.STSPreload=true
      - traefik.http.routers.n8n.middlewares=n8n@docker
      - traefik.http.routers.mydashboard.rule=Host(`${TRAEFIK_SUBDOMAIN}.${DOMAIN_NAME}`)
      - traefik.http.routers.mydashboard.service=api@internal
      - "traefik.http.middlewares.myauth.basicauth.users=${TRAEFIK_BASIC_AUTH_USER}:${TRAEFIK_BASIC_AUTH_PASSWORD}"
      - "traefik.http.routers.mydashboard.middlewares=myauth@docker"

    environment:
      - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      # Enable authentication
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      # Secure credentials with encryption
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      # Disable the public API https://docs.n8n.io/hosting/securing/disable-public-api/
      - N8N_PUBLIC_API_DISABLED=true
      - N8N_PUBLIC_API_SWAGGERUI_DISABLED=true
      # Disable data collection https://docs.n8n.io/hosting/securing/telemetry-opt-out/#collected-data
      - N8N_DIAGNOSTICS_ENABLED=false
      - N8N_VERSION_NOTIFICATIONS_ENABLED=false
      - N8N_TEMPLATES_ENABLED=false
    volumes:
      - ./n8n_storage:/home/node/.n8n
      - ./n8n-local-files:/files

    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 3G

These are the log:

$ docker compose logs traefik
WARN[0000] The "apr1" variable is not set. Defaulting to a blank string. 
WARN[0000] The "n4729mxb" variable is not set. Defaulting to a blank string. 
WARN[0000] The "apr1" variable is not set. Defaulting to a blank string. 
WARN[0000] The "n4729mxb" variable is not set. Defaulting to a blank string. 
WARN[0000] The "apr1" variable is not set. Defaulting to a blank string. 
WARN[0000] The "n4729mxb" variable is not set. Defaulting to a blank string. 
WARN[0000] The "apr1" variable is not set. Defaulting to a blank string. 
WARN[0000] The "n4729mxb" variable is not set. Defaulting to a blank string. 
traefik-1  | 2025-03-12T07:11:59Z INF Traefik version 3.3.4 built on 2025-02-25T10:11:01Z version=3.3.4
traefik-1  | 2025-03-12T07:11:59Z INF 
traefik-1  | Stats collection is disabled.
traefik-1  | Help us improve Traefik by turning this feature on :)
traefik-1  | More details on: https://doc.traefik.io/traefik/contributing/data-collection/
traefik-1  | 
traefik-1  | 2025-03-12T07:11:59Z INF Starting provider aggregator *aggregator.ProviderAggregator
traefik-1  | 2025-03-12T07:11:59Z INF Starting provider *file.Provider
traefik-1  | 2025-03-12T07:11:59Z INF Starting provider *traefik.Provider
traefik-1  | 2025-03-12T07:11:59Z INF Starting provider *docker.Provider
traefik-1  | 2025-03-12T07:11:59Z INF Starting provider *acme.ChallengeTLSALPN
traefik-1  | 2025-03-12T07:11:59Z INF Starting provider *acme.Provider
traefik-1  | 2025-03-12T07:11:59Z INF Testing certificate renew... acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory providerName=mytlschallenge.acme
traefik-1  | 2025-03-12T07:12:09Z ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [sub.domain.de]: error: one or more domains had a problem:\n[sub.domain.de] invalid authorization: acme: error: 400 :: urn:ietf:params:acme:error:connection :: 5.189.164.68: Connection refused\n" ACME CA=https://acme-staging-v02.api.letsencrypt.org/directory acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory domains=["sub.domain.de"] providerName=mytlschallenge.acme routerName=n8n@docker rule=Host(`sub.domain.de`)
traefik-1  | 2025-03-12T07:12:10Z ERR error="service \"n8n-docker-n8n\" error: unable to find the IP address for the container \"/docker_n8n-n8n-1\": the server is ignored" container=n8n-docker-n8n-f74f85601a0b0981d4c8280925a38d75d321f454b501b9f414b92037dffd62b8 providerName=docker
traefik-1  | 2025-03-12T07:12:16Z ERR error="service \"n8n-docker-n8n\" error: unable to find the IP address for the container \"/docker_n8n-n8n-1\": the server is ignored" container=n8n-docker-n8n-f74f85601a0b0981d4c8280925a38d75d321f454b501b9f414b92037dffd62b8 providerName=docker
traefik-1  | 2025-03-12T07:12:17Z ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [sub.domain.de]: error: one or more domains had a problem:\n[sub.domain.de] invalid authorization: acme: error: 400 :: urn:ietf:params:acme:error:connection :: 5.189.164.68: Connection refused\n" ACME CA=https://acme-staging-v02.api.letsencrypt.org/directory acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory domains=["sub.domain.de"] providerName=mytlschallenge.acme routerName=n8n@docker rule=Host(`sub.domain.de`)
traefik-1  | 2025-03-12T07:12:21Z ERR error="service \"n8n-docker-n8n\" error: unable to find the IP address for the container \"/docker_n8n-n8n-1\": the server is ignored" container=n8n-docker-n8n-f74f85601a0b0981d4c8280925a38d75d321f454b501b9f414b92037dffd62b8 providerName=docker
traefik-1  | 2025-03-12T07:12:26Z ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [sub.domain.de]: error: one or more domains had a problem:\n[sub.domain.de] invalid authorization: acme: error: 400 :: urn:ietf:params:acme:error:connection :: 5.189.164.68: Connection refused\n" ACME CA=https://acme-staging-v02.api.letsencrypt.org/directory acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory domains=["sub.domain.de"] providerName=mytlschallenge.acme routerName=n8n@docker rule=Host(`sub.domain.de`)
traefik-1  | 2025-03-12T07:12:27Z ERR error="service \"n8n-docker-n8n\" error: unable to find the IP address for the container \"/docker_n8n-n8n-1\": the server is ignored" container=n8n-docker-n8n-f74f85601a0b0981d4c8280925a38d75d321f454b501b9f414b92037dffd62b8 providerName=docker
traefik-1  | 2025-03-12T07:12:33Z ERR error="service \"n8n-docker-n8n\" error: unable to find the IP address for the container \"/docker_n8n-n8n-1\": the server is ignored" container=n8n-docker-n8n-f74f85601a0b0981d4c8280925a38d75d321f454b501b9f414b92037dffd62b8 providerName=docker
traefik-1  | 2025-03-12T07:12:33Z ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [sub.domain.de]: error: one or more domains had a problem:\n[sub.domain.de] invalid authorization: acme: error: 400 :: urn:ietf:params:acme:error:connection :: 5.189.164.68: Connection refused\n" ACME CA=https://acme-staging-v02.api.letsencrypt.org/directory acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory domains=["sub.domain.de"] providerName=mytlschallenge.acme routerName=n8n@docker rule=Host(`sub.domain.de`)
traefik-1  | 2025-03-12T07:12:41Z ERR error="service \"n8n-docker-n8n\" error: unable to find the IP address for the container \"/docker_n8n-n8n-1\": the server is ignored" container=n8n-docker-n8n-f74f85601a0b0981d4c8280925a38d75d321f454b501b9f414b92037dffd62b8 providerName=docker
traefik-1  | 2025-03-12T07:12:42Z ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [sub.domain.de]: error: one or more domains had a problem:\n[sub.domain.de] invalid authorization: acme: error: 400 :: urn:ietf:params:acme:error:connection :: 5.189.164.68: Connection refused\n" ACME CA=https://acme-staging-v02.api.letsencrypt.org/directory acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory domains=["sub.domain.de"] providerName=mytlschallenge.acme routerName=n8n@docker rule=Host(`sub.domain.de`)
traefik-1  | 2025-03-12T07:12:52Z ERR error="service \"n8n-docker-n8n\" error: unable to find the IP address for the container \"/docker_n8n-n8n-1\": the server is ignored" container=n8n-docker-n8n-f74f85601a0b0981d4c8280925a38d75d321f454b501b9f414b92037dffd62b8 providerName=docker
traefik-1  | 2025-03-12T07:12:56Z ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [sub.domain.de]: error: one or more domains had a problem:\n[sub.domain.de] invalid authorization: acme: error: 400 :: urn:ietf:params:acme:error:connection :: 5.189.164.68: Connection refused\n" ACME CA=https://acme-staging-v02.api.letsencrypt.org/directory acmeCA=https://acme-staging-v02.api.letsencrypt.org/directory domains=["sub.domain.de"] providerName=mytlschallenge.acme routerName=n8n@docker rule=Host(`sub.domain.de`)

The warnings seem to come from the traefik dashboard auth.
Can’t get them to work - found several hints with $ and $$. But it’s only warning, so I hope it’s save to ignore it for now?

ping sub.domain.de

resolves correctly to my server IP - since a few days already.

$ docker network inspect docker_n8n_default
[
    {
        "Name": "docker_n8n_default",
        "Id": "16bca091a9abdca68a7d1bcec2309e9406be7a5b5f3d2acb552c0cb14dbc56b7",
        "Created": "2025-03-12T08:11:58.283728977+01:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "13d9afbab9014455bcff46c2c438a3b18b4787861a5685483c67d0723100ad50": {
                "Name": "docker_n8n-traefik-1",
                "EndpointID": "538a725569495e00d3ce398f9550fe4c2f31e513fcce5faf15c5ef30bd949d03",
                "MacAddress": "52:b2:b7:9c:2d:e7",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.config-hash": "82c611e68d66025c5ef0e83db86b44807a2e91129f99ed5f296f771d03c5fc30",
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "docker_n8n",
            "com.docker.compose.version": "2.33.1"
        }
    }
]

it seems as if n8n isn't connected to the network with traefik?
Shall I add networks manually in the composer yml file? (why are there none in the instruction? https://docs.n8n.io/hosting/installation/server-setups/docker-compose/#5-create-docker-compose-file )

I had to change to the acme staging server due to too many tries.
Should I still be able to access n8n via the browser?

At the moment I get from my browser:

Secure Connection Failed

An error occurred during a connection to subdomain.domain.de:8143. PR_END_OF_FILE_ERROR

Error code: PR_END_OF_FILE_ERROR


Information on your n8n setup

  • n8n version: latest
  • Database (default: SQLite): SQLite
  • n8n EXECUTIONS_PROCESS setting (default: own, main): own
  • Running n8n via (Docker, npm, n8n cloud, desktop app): docker
  • Operating system: Ubuntu 24.04 LTS

Don’t get into all this hassle, try using easupanel.io with Traefik embedded. Much better solutions to install lots of tools like n8n. I’m using this for years and it has never let me down.