Set up paths in Traefik

I am trying to set up my Bookstack instance behind Traefik using paths, and I am running into problems.

My Docker host is in a Windows domain, so let's say it's accessible at dockerserver.company.local. My Bookstack container is directly accessible at dockerserver.company.local:6875, and I would like to make it available at dockerserver.company.local/bookstack/. But after reading through the documentation and trying multiple configurations, I still can't get it to work.

I also can't find any example setups either, which would be quite helpful as a reference.

Here is my current Bookstack docker-compose.yml file:

---
version: "2"
services:
  bookstack:
    image: linuxserver/bookstack
    container_name: bookstack
    environment:
      - PUID=1000
      - PGID=1000
      - DB_HOST=bookstack_db
      - DB_USER=bookstack
      - DB_PASS=bookstack
      - DB_DATABASE=bookstackapp
    volumes:
      - /home/user/bookstack/config
    ports:
      - 6875:80
    restart: unless-stopped
    depends_on:
      - bookstack_db
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.bookstack.rule=Host(`dockerserver.company.local`) && Path(`/bookstack`)"
      - "traefik.http.middlewares.bookstack.stripprefix.prefixes=/bookstack"
      - "traefik.http.routers.bookstack.entrypoints=web"
      - "traefik.http.services.bookstack.loadbalancer.server.port=6875"
  bookstack_db:
    image: linuxserver/mariadb
    container_name: bookstack_db
    environment:
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=bookstack
      - TZ=America/New_York
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=bookstack
    volumes:
      - /home/user/bookstack/config
    restart: unless-stopped

And here is my Traefik docker-compose.yml file:

version: "3.3"

services:
  traefik:
    image: "traefik:v2.0"
    command:
      - --entrypoints.web.address=:80
      - --providers.docker=true
      - --api.insecure # Don't do that in production
      - "--log.level=DEBUG"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

Now with this configuration, if I attempt to visit http://dockerserver.company.local/bookstack/, I get a 404 error (and no Traefik logs that I can tell). And if I attempt to visit http://dockerserver.company.local/bookstack (note no trailing forward slash), I get a Gateway Timeout error, and the Traefik log is as follows:

\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/bookstack\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Accept-Language\":[\"en-US,en;q=0.5\"],\"Connection\":[\"keep-alive\"],\"Upgrade-Insecure-Requests\":[\"1\"],\"User-Agent\":[\"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\"],\"X-Forwarded-Host\":[\"dockerserver.company.local\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"902385745d4a\"],\"X-Real-Ip\":[\"192.168.0.57\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"dockerserver.company.local\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"192.168.0.57:14634\",\"RequestURI\":\"/bookstack\",\"TLS\":null}" ForwardURL="http://172.18.0.3:6875"
time="2019-10-11T12:04:41Z" level=debug msg="'504 Gateway Timeout' caused by: dial tcp 172.18.0.3:6875: i/o timeout"
time="2019-10-11T12:04:41Z" level=debug msg="vulcand/oxy/roundrobin/rr: completed ServeHttp on request" Request="{\"Method\":\"GET\",\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/bookstack\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Accept-Language\":[\"en-US,en;q=0.5\"],\"Connection\":[\"keep-alive\"],\"Upgrade-Insecure-Requests\":[\"1\"],\"User-Agent\":[\"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\"],\"X-Forwarded-Host\":[\"dockerserver.company.local\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"902385745d4a\"],\"X-Real-Ip\":[\"192.168.0.57\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"dockerserver.company.local\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"192.168.0.57:14634\",\"RequestURI\":\"/bookstack\",\"TLS\":null}"

Can someone give me advice on how to accomplish what I am trying to do? Or tips on how to troubleshoot? Or even example setups?

At this point, I am suspicious that Traefik can't talk to my Bookstack container at all (hence the 404 errors). Do I need to add some networking magic in my Bookstack container?

TIA!

Hi @chrisn, you have to set the "load balancer" port to 80.
This is related to the following configuration element of the bookstack service:

ports:
      - 6875:80

which means that the public port 6875 is NAT-ed to the private port 80 of the container bookstack.

This private port is the port used by Traefik to contact the bookstack service, and is different than the 80 of Traefik of course.

OK, that makes sense. I've changed it now (actually, I removed that line totally), but unfortunately the behavior is still the same. This is what my Bookstack docker-compose.yml file looks like now:

---
version: "2"
services:
  bookstack:
    image: linuxserver/bookstack
    container_name: bookstack
    environment:
      - PUID=1000
      - PGID=1000
      - DB_HOST=bookstack_db
      - DB_USER=bookstack
      - DB_PASS=bookstack
      - DB_DATABASE=bookstackapp
      - APP_URL=http://dockerserver.company.local/bookstack/
    volumes:
      - /home/user/bookstack/config
    ports:
      - 6875:80
    restart: unless-stopped
    depends_on:
      - bookstack_db
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.bookstack.rule=Host(`dockerserver.company.local`) && Path(`/bookstack`)"
      - "traefik.http.middlewares.bookstack.stripprefix.prefixes=/bookstack"
  bookstack_db:
    image: linuxserver/mariadb
    container_name: bookstack_db
    environment:
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=bookstack
      - TZ=America/New_York
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=bookstack
    volumes:
      - /home/user/bookstack/config
    restart: unless-stopped

I still get a 404 error if I attempt to visit http://dockerserver.company.local/bookstack/, with no Traefik logs.

And I still get a Gateway Timeout if I attempt to visit http://dockerserver.company.local/bookstack (with no trailing forward slash). The Traefik logs for that request are as follows:

time="2019-10-11T17:59:02Z" level=debug msg="vulcand/oxy/roundrobin/rr: Forwarding this request to URL" Request="{\"Method\":\"GET\",\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/bookstack\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Accept-Language\":[\"en-US,en;q=0.5\"],\"Connection\":[\"keep-alive\"],\"Upgrade-Insecure-Requests\":[\"1\"],\"User-Agent\":[\"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\"],\"X-Forwarded-Host\":[\"dockerserver.company.local\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"902385745d4a\"],\"X-Real-Ip\":[\"192.168.0.57\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"dockerserver.company.local\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"192.168.0.57:36465\",\"RequestURI\":\"/bookstack\",\"TLS\":null}" ForwardURL="http://172.18.0.3:80"
time="2019-10-11T17:59:32Z" level=debug msg="'504 Gateway Timeout' caused by: dial tcp 172.18.0.3:80: i/o timeout"
time="2019-10-11T17:59:32Z" level=debug msg="vulcand/oxy/roundrobin/rr: completed ServeHttp on request" Request="{\"Method\":\"GET\",\"URL\":{\"Scheme\":\"\",\"Opaque\":\"\",\"User\":null,\"Host\":\"\",\"Path\":\"/bookstack\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\"},\"Proto\":\"HTTP/1.1\",\"ProtoMajor\":1,\"ProtoMinor\":1,\"Header\":{\"Accept\":[\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Accept-Language\":[\"en-US,en;q=0.5\"],\"Connection\":[\"keep-alive\"],\"Upgrade-Insecure-Requests\":[\"1\"],\"User-Agent\":[\"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0\"],\"X-Forwarded-Host\":[\"dockerserver.company.local\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Forwarded-Server\":[\"902385745d4a\"],\"X-Real-Ip\":[\"192.168.0.57\"]},\"ContentLength\":0,\"TransferEncoding\":null,\"Host\":\"dockerserver.company.local\",\"Form\":null,\"PostForm\":null,\"MultipartForm\":null,\"Trailer\":null,\"RemoteAddr\":\"192.168.0.57:36465\",\"RequestURI\":\"/bookstack\",\"TLS\":null}"

Hi @chrisn, I was able to draft a working setup, put at the end of my message.

The following elements needs to be corrected on your setup to go to this "working" setup:

  • The routing rule should be adapted to use PathPrefix() instead of Path() to capture all subpaths under /boostack (reference: last note of the section https://docs.traefik.io/v2.0/routing/routers/#rule explaining that the rule Path only matches the exact URL path, not children).
  • Add the flag - --providers.docker.exposedByDefault=false to Traefik's command, to avoid exposing the bookstack database or any other container in Traefik.
  • The error "502 Bad Gateway" come from the fact that Traefik container is not on the same network as the bookstack application. This is because you are using 2 different docker-compose files, each one creating implicitly its own private network. You can read more on this on the official docker-compose documentation: https://docs.docker.com/compose/networking/ . The solution in my proposal below is to declare an external network for communication between Traefik and Bookstack. Also, the communication from bookstack to the database is also done on another private network so Traefik can not access it.
    Once you are sure that Traefik and Bookstack share 1 private network, you need to add a special label to specify which network to use for Traefik to connect to bookstack: https://docs.traefik.io/v2.0/routing/providers/docker/#traefikdockernetwork.
  • In my tests, the bookstack container took some time before starting to answer requests. Initially I thought I did an error in my configuration, so I connected to the Traefik container and "curl-ed" the service until it answered: at this moment, I was able to access it from my web-browser through Traefik.
  • You need to "wire" the middleware which strip prefix to the router (check the Traefik dashboard: the middleware is not present on the router's page), using this label: https://docs.traefik.io/v2.0/routing/providers/docker/#middleware
  • As the bookstack Docker image exposes 2 ports by default (80 and 443 from the result of docker inspect linuxserver/bookstack), you need to tell Traefik which port to use for communication because it cannot be autoamtically guessed. Specify the loadbalancer.server.port to 80 for this, as described in https://docs.traefik.io/v2.0/routing/providers/docker/#services.

docker-compose.yml :

version: "3.3"

networks:
  bookstack-front:
    external: true

services:
  traefik:
    image: "traefik:v2.0"
    command:
      - --entrypoints.web.address=:80
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
      - --api.insecure # Don't do that in production
      - "--log.level=DEBUG"
    ports:
      - "80:80"
      - "8080:8080"
    networks:
      - bookstack-front
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

bookstack.yml:

---
version: "2"

networks:
  bookstack-front:
    external: true
  bookstack-back:

services:
  bookstack:
    image: linuxserver/bookstack
    container_name: bookstack
    environment:
      - PUID=1000
      - PGID=1000
      - DB_HOST=bookstack_db
      - DB_USER=bookstack
      - DB_PASS=bookstack
      - DB_DATABASE=bookstackapp
      - APP_URL=http://localhost/bookstack/
    volumes:
      - /home/user/bookstack/config
    networks:
      - bookstack-front
      - bookstack-back
    restart: unless-stopped
    depends_on:
      - bookstack_db
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.bookstack.rule=Host(`localhost`) && PathPrefix(`/bookstack`)"
      - "traefik.http.routers.bookstack.middlewares=bookstack-removeprefix" # Wire middleware to this router
      - "traefik.http.middlewares.bookstack-removeprefix.stripprefix.prefixes=/bookstack"
      - "traefik.http.services.bookstack-svc.loadbalancer.server.port=80" # Because Docker image "linuxserver/bookstack" exposes 2 ports: 80 and 443.
      - "traefik.docker.network=bookstack-front"
  bookstack_db:
    image: linuxserver/mariadb
    container_name: bookstack_db
    environment:
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=bookstack
      - TZ=America/New_York
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=bookstack
    volumes:
      - /home/user/bookstack/config
    networks:
      - bookstack-back
    restart: unless-stopped

Steps to start the stack:

$ docker network create bookstack-front
$ docker-compose up -d
$ docker-compose -f bootstack.yml up -d
1 Like

I've adapted your configuration to my setup, and it indeed works as advertised. I am new to both Docker and Traefik, so I'm trying to learn the basics. Thanks a lot for the explanation, references, and working configuration!

1 Like