Bookstack auf Docker mit traefik

hubort

Member
Moin ihr Lieben,

vielleicht habt ihr einen guten Rat für mich :)

Aktuell versuche ich Bookstack als Dokumentations-Tool in einer Docker-Umgebung aufzusetzen. Zu allem Übel soll das Ganze hinter einer Reverse Proxy laufen.
Zu diesem Zwecke ist traefik aufgesetzt, welches auch bereits mit Passbolt und Nextcloud erfolgreich läuft.
Bookstack macht da aber ganz schön faxen und im Internet findet sich leider wenig Hilfe im Umgang mit traefik und Bookstack.

Bookstack ohne Reverse Proxy ist recht einfach aufgesetzt und funktioniert auch sofort. Ich kann unter der beispielhaften IP-Adresse 192.19.21.92:6875 Bookstack aufrufen und regulär benutzen.
Das geht dann allerdings nur solange, wie ich mich im gleichen Netzwerk wie der Server auf welchem Docker läuft befinde.

Bookstack soll unter der beispielhaften Domain https://bookstack.my-sub.domain.de erreichbar sein. Im docker-compose.yml von Linuxserver, deren Docker Image ich benutze, findet sich dazu auch die Variable APP_URL, in welche ich meine URL speichere.
Weiterhin richte ich die notwendigen traefik-Labels und Netzwerke ein: Die Bookstack-Datenbank soll nicht im gleichen Netzwerk wie unser traefik-Proxy liegen, um nach außen hin geschützt zu sein.
Somit teilen sich die Bookstack-Datenbank und der Bookstack-Service ein Netzwerk bookstack-default und der Service und traefik teilen sich das Netzwerk proxy.

Ich bekomme leider ständig einen Bad Gateway-Fehler.

Ich füge mal mein docker-compose-File anbei und die traefik-Konfiguration und bin äußerst dankbar für jeden Input! :)

docker-compose.yml von Bookstack


Code:
version: "3.7"
services:
  bookstack:
    image: linuxserver/bookstack:latest
    container_name: bookstack
    environment:
      - APP_URL=my-sub.domain.com
      - TZ=Europe/Berlin
      - DB_HOST=bookstack_db:3306
      - DB_DATABASE=bookstackdb
      - DB_USERNAME=bookstack-db-user
      - DB_PASSWORD=<mydbpassword>
    volumes:
      - ./bookstack/app:/config
    ports:
      - 6875:80
    restart: unless-stopped
    depends_on:
      - bookstack_db
    labels:
      traefik.enable: "true"
      traefik.http.routers.bookstack.entrypoints: "http"
      traefik.http.routers.bookstack.rule: "Host(`my-sub.domain.com`)"
      traefik.http.middlewares.bookstack-https-redirect.redirectscheme.scheme: "https"
      traefik.http.routers.bookstack.middlewares: "bookstack-https-redirect"
      traefik.http.routers.bookstack-secure.entrypoints: "https"
      traefik.http.routers.bookstack-secure.rule: "Host(`my-sub.domain.com`)"
      traefik.http.routers.bookstack-secure.tls: "true"
      traefik.http.routers.bookstack-secure.tls.certresolver: "http"
      traefik.http.routers.bookstack-secure.service: "bookstack"
      # traefik.http.services.bookstack.loadbalancer.server.port: "80"
      # traefik.docker.network: "proxy"
    networks:
     - proxy
  bookstack_db:
    image: mariadb:10.9
    container_name: bookstack_db
    environment:
      - TZ=Europe/Berlin
      - MYSQL_ROOT_PASSWORD=<myrootpassword>
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bs-mysql-user
      - MYSQL_PASSWORD=<mypassword>
    volumes:
      - ./bookstack/db:/var/lib/mysql
    ports:
      - 3306:3306
    restart: unless-stopped
    networks:
     - proxy
networks:
  proxy:
    external: true

traefik-compose-file


Code:
version: '3'
services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
      - ./data/dynamic_conf.yml:/dynamic_conf.yml
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`traefik.my-sub.domain.de`)"
      - "traefik.http.middlewares.traefik-auth.basicauth.users=usernameandpassword"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.my-sub.domain.de`)"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=http"
      - "traefik.http.routers.traefik-secure.service=api@internal"
      - "providers.file.filename=/dynamic_conf.yml"
      - "traefik.http.routers.traefik-secure.middlewares=secHeaders@file,traefik-auth"
networks:
  proxy:
    external: true
 
Zuletzt bearbeitet:
Moinsen und willkommen im Forum,
Leider kann ich selber dir da gar nicht konkret helfen. Ich weiß aber, dass hier Menschen im Forum sind, die sich gut mit docker und auch traefik auskennen, von daher...da kommt sicher noch mehr input. (y)

Davon ab: willst nur du auf boockstack zugreifen von extern oder auch andere?
Falls nur du und eine überschaubare Anzahl vertrauter Leute, dann wäre auch vpn eine Alternative. Dann sollte auch das mit dem Netzwerk funktionieren.
Wenn du es natürlich "im Netz" anbieten willst, eher weniger. ;)

So oder so, viel Erfolg...
 
Moinsen nochmal,
Wenn du über den Port 6875 auf boockstack gehst...wo ist das denn in der traefik Konfiguration hinterlegt? Also nur mal so ganz ohne eigene Erfahrung damit gefragt?
:)
 
Moin,

erstmal danke für den Input!
Da Bookstack öffentlich über https://bookstack.my-sub.domain.de erreichbar sein sollen wird, muss es leider per Reverse Proxy gehen.
Guter Hinweis, ich hatte das bis lang so verstanden, dass traefik alle Anfragen über Port 80 bekommt und deshalb in den Flags in Bookstack der traefik "enabled" und der Port 80 als Kommunikationsweg im Netzwerk "proxy" festgelegt wird.

Auch von mir ein herzlich willkommen, Hubort.

Ich schaue mir gleich mal Deine Anhänge an. Es ist immer angenehmer Konfigurationsdateien im Code-Block innerhalb des Posts zu sehen (⋮ Icon > </> icon).
Werde ich auch gleich mal reineditieren, so für die Zukunft :)
 
Ich verwende immer noch Traefik 1.7.x, weil mit 2.x der Support für Consul für das Verteilen der Zertitikate im Swarm-Cluster entfernt wurde.

Du hast die Hauptkonfiguration für Traefik in serparaten Dateien ausgelagert, deren Inhalt jetzt natürlich nicht bekannt ist (bitte acme Informationen nur anonymisiert teilen!). Die wären wichtig, um Dinge wie entrypoint und certresolver überprüfen zu können.

Was mir aber schon mal auffällt ist:
- APP_URL: eine URL gibt http(s) Protokoll und Domain an. Ansonsten ist es keine URL. Dürfte aber eignetlich nur relvant sein, wenn man Pfad-basiertes Reverse Proxying macht, á la https://my-sub.domain.com/bookstack und anhand des Pfads /bookstack geroutet wird. Außerdem Frage ich mich, ob der gemappte Port dann noch funktioniert?
- TZ: der Parameter steht nicht in der Doku. Kann sein das er nichts tut, oder es in einem Base-Image implementiert ist - würde man dann aber eigentlich in der Doku erwarten.
- PUID/PGID: sollten den UID/GID vom Verzeichnis-Besitzer sein, sonst verändert das Entrypoint-Skript die UID/GID des Verzeichnis-Besitzers auf seinen Standardwert.

- Dein Netzwerk-Wunsch ist in der Konfig unten hinterlegt - das Default-Netzwerk hat jetzt den Namen "bookstack-default". Nur bookstack und bookstack_db sind in dem Netzwerk. Port-Publishing bei DB kann man sich sparen, außer man will mit externen Tools darauf.
- Die Traefik Labels sehen erstmal bis auf bookstack-secure.entrypoints gut aus, dort ist die Fragee, was tatsächlich interlegt ist in der Traefik-Konfiguration.
Code:
version: "3.7"
services:
  bookstack:
    image: linuxserver/bookstack:latest
    container_name: bookstack
    environment:
      - APP_URL=https://my-sub.domain.com #
      - TZ=Europe/Berlin # undokumentierter parameter?
      # - PUID= # = stat ./bookstack/app --format "%u"
      # - PGID= # = stat ./bookstack/app --format "%g"
      - DB_HOST=bookstack_db
      - DB_DATABASE=bookstackdb
      - DB_USERNAME=bookstack-db-user
      - DB_PASSWORD=<mydbpassword>
    volumes:
      - ./bookstack/app:/config
    ports:
      - 6875:80
    restart: unless-stopped
    depends_on:
      - bookstack_db
    labels:
      traefik.enable: "true"
      traefik.docker.network: "proxy"
      traefik.http.routers.bookstack.entrypoints: "http" # steht das so in der traefik.yaml?
      traefik.http.routers.bookstack.rule: "Host(`my-sub.domain.com`)"
      traefik.http.middlewares.bookstack-https-redirect.redirectscheme.scheme: "https"
      traefik.http.routers.bookstack.middlewares: "bookstack-https-redirect"
      traefik.http.routers.bookstack-secure.entrypoints: "https" # steht das so in der traefik.yaml?
      traefik.http.routers.bookstack-secure.rule: "Host(`my-sub.domain.com`)"
      traefik.http.routers.bookstack-secure.tls: "true"
      traefik.http.routers.bookstack-secure.tls.certresolver: "http"
      traefik.http.services.bookstack.loadbalancer.server.port: "80"
    networks:
     - default
     - proxy
  bookstack_db:
    image: # welches image? die ENV passen natürlich nur, wenn das Image die auch kennt.
    container_name: bookstack_db
    environment:
      - TZ=Europe/Berlin
      - MYSQL_ROOT_PASSWORD=<myrootpassword>
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bs-mysql-user
      - MYSQL_PASSWORD=<mypassword>
    volumes:
      - ./bookstack/db:/var/lib/mysql
    restart: unless-stopped
    networks:
     - default
networks:
  default:
    name: bookstack-default
  proxy:
    external: true

Ob die Labels alle komplett richtig sind weiss ich ohne einen Blick in die Doku nicht - die Labels bei 1.7 sind deutlich anders.
 
Zuletzt bearbeitet:
Erst einmal vielen Dank!
AFAIK bezeichnet der TZ-Parameter die Zeitzone. Ob das einen Einfluss nimmt, kann ich auch nicht sagen: Wie du schon sagst, nicht dokumentiert.
Ich habe leider die Reverse Proxy nicht selbst aufgesetzt. Mein Kollege hat das Mithilfe dieser Anleitung von goneuland gemacht und für die Nextcloud und Passbolt funktioniert das auch problemfrei.
Ich habe die Labels auch da nochmal mit verglichen, Passbolt & Nextcloud benutzen die ebenfalls.

Das image der bookstack_db habe ich vergessen mit einzutragen: lscr.io/linuxserver/mariadb

Ich habe deinen Code mal umgesetzt und habe jetzt zwar keinen Bad Gateway mehr, komme dafür auf eine weiße (inhaltsleere) Seite. Klingt für mich so, als würde die Kommunikation mit der Datenbank nicht funktionieren?

Danke nochmals!
EDIT: Habe ich das richtig verstanden und ein Einblick in die acme.json-Files würde helfen? Dann würde ich die morgen mal anonymisiert hochladen.
 
Sofern das Image es unterstützt, setzt TZ die Zeitzone - wenn das Image es nicht unterstützt, ist es einfach nur eine ungenutzte System-Variable.


Ich denke der Blick in die Traefik.yaml ist interessanter. Man kann bei Traefik2 eigentlich alles auch über die compose Datei konfigurieren, hier ein Beispiel für den http, https redirect: https://jensknipper.de/blog/traefik-http-to-https-redirect/. Das greift dann für alle Container und muss dann nicht mehr als Label an jedem Container wiederholt hinterlegt werden.

Ein Blick in die Logs sollte eigentlich aufzeigen, ob es ein Datenbankproblem gibt.
 
Blöde Frage, kann es sein, dass das Passwort bzw. der Username für DB_PASSWORD & MYSQL_PASSWORD bzw. DB_USERNAME & MYSQL_USER identisch sein müssen?
Aus dem Log des Bookstack-Containers.

Code:
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service 00-legacy: starting
s6-rc: info: service 00-legacy successfully started
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
cont-init: info: running /etc/cont-init.d/01-envfile
cont-init: info: /etc/cont-init.d/01-envfile exited 0
cont-init: info: running /etc/cont-init.d/01-migrations
[migrations] started
[migrations] 01-nginx-site-confs-default: skipped
[migrations] 02-default-location: skipped
[migrations] done
cont-init: info: /etc/cont-init.d/01-migrations exited 0
cont-init: info: running /etc/cont-init.d/10-adduser
usermod: no changes

-------------------------------------
          _         ()
         | |  ___   _    __
         | | / __| | |  /  \
         | | \__ \ | | | () |
         |_| |___/ |_|  \__/


Brought to you by linuxserver.io
-------------------------------------

To support LSIO projects visit:
https://www.linuxserver.io/donate/
-------------------------------------
GID/UID
-------------------------------------

User uid:    911
User gid:    911
-------------------------------------

cont-init: info: /etc/cont-init.d/10-adduser exited 0
cont-init: info: running /etc/cont-init.d/11-folders
cont-init: info: /etc/cont-init.d/11-folders exited 0
cont-init: info: running /etc/cont-init.d/12-samples
cont-init: info: /etc/cont-init.d/12-samples exited 0
cont-init: info: running /etc/cont-init.d/13-nginx
cont-init: info: /etc/cont-init.d/13-nginx exited 0
cont-init: info: running /etc/cont-init.d/14-php
cont-init: info: /etc/cont-init.d/14-php exited 0
cont-init: info: running /etc/cont-init.d/15-keygen
using keys found in /config/keys
cont-init: info: /etc/cont-init.d/15-keygen exited 0
cont-init: info: running /etc/cont-init.d/20-permissions
cont-init: info: /etc/cont-init.d/20-permissions exited 0
cont-init: info: running /etc/cont-init.d/50-config
App Key found - setting variable for seds
/etc/cont-init.d/50-config: line 103: warning: command substitution: ignored null byte in input
/etc/cont-init.d/50-config: line 103: warning: command substitution: ignored null byte in input

   Illuminate\Database\QueryException

  SQLSTATE[HY000] [1045] Access denied for user 'bookstack-db-user'@'bookstack.bookstack-default' (using password: YES) (SQL: select * from information_schema.tables where table_schema = bookstackdb and table_name = migrations and table_type = 'BASE TABLE')

  at /app/www/vendor/laravel/framework/src/Illuminate/Database/Connection.php:712
    708▕         // If an exception occurs when attempting to run a query, we'll format the error
    709▕         // message to include the bindings with SQL, which will make this exception a
    710▕         // lot more helpful to the developer instead of just the database's errors.
    711▕         catch (Exception $e) {
  ➜ 712▕             throw new QueryException(
    713▕                 $query, $this->prepareBindings($bindings), $e
    714▕             );
    715▕         }
    716▕     }

      +33 vendor frames
  34  /app/www/artisan:37
      Illuminate\Foundation\Console\Kernel::handle()
cont-init: info: /etc/cont-init.d/50-config exited 0
cont-init: info: running /etc/cont-init.d/85-version-checks
cont-init: info: /etc/cont-init.d/85-version-checks exited 0
cont-init: info: running /etc/cont-init.d/99-custom-files
[custom-init] No custom files found, skipping...
cont-init: info: /etc/cont-init.d/99-custom-files exited 0
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service init-mods: starting
s6-rc: info: service init-mods successfully started
s6-rc: info: service init-mods-package-install: starting
s6-rc: info: service init-mods-package-install successfully started
s6-rc: info: service init-mods-end: starting
s6-rc: info: service init-mods-end successfully started
s6-rc: info: service init-services: starting
s6-rc: info: service init-services successfully started
s6-rc: info: service legacy-services: starting
services-up: info: copying legacy longrun cron (no readiness notification)
services-up: info: copying legacy longrun memcached (no readiness notification)
services-up: info: copying legacy longrun nginx (no readiness notification)
services-up: info: copying legacy longrun php-fpm (no readiness notification)
s6-rc: info: service legacy-services successfully started
s6-rc: info: service 99-ci-service-check: starting
[ls.io-init] done.
s6-rc: info: service 99-ci-service-check successfully started

In der traefik.yml findet sich das hier:

Code:
api:
  dashboard: true
entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: "./dynamic_conf.yml"
certificatesResolvers:
  http:
    acme:
      email: meine.email@domain.de #### hier eure E-Mail angeben ####
      storage: acme.json
      httpChallenge:
        entryPoint: http
 
Dank traefik.yml kann man sehen, dass hier für die Endpunkte nicht die „üblichen“ Namen verwendet wurden, sondern abweichende die aber zu den Labels passen. Der Resolver passt auch.:

- Endpunkt für http heisst http und passt zum Label
- Endpunkt für https heisst https und passt zum Label
- Resolver für TLS-Zertifikate heisst http und passt zum Label

Das mit der Datenbank ergibt natürlich Sinn. Wenn die nicht hochkommt, wird auch die Anwendung vermutlich nicht richtig hochkommen.

kann es sein, dass das Passwort bzw. der Username für DB_PASSWORD & MYSQL_PASSWORD bzw. DB_USERNAME & MYSQL_USER identisch sein müssen?

Klar müssen sie das!

Die MYSQL_* Variablen am MariaDB Container richten beim ersten Start des Containers (genauer: wenn der Volume-Bind noch keine Daten enthält) die Datenbank und den Benutzer in der Datenbank ein.

Die DB_* Variablen am BookStack Container müssen dann die Zugangsdaten verwenden, die in der MariaDB vorher angelegt wurden. Sonst lehnt die Datenbank die Verbindung ab, weil sie Datenbank, Benutzer oder Passwort nicht kennt, so wie oben in den Logs zu sehen.

MYSQL_DATABASE == DB_DATABASE
MYSQL_USER == DB_USERNAME
MYSQL_PASSWORT == DB_PASSWORT

Wichtiger Hinweis: wenn du die Konfig glatt gezogen hast, entferne mal das Compose Projekt mit docker compose down und lösche dann den Inhalt der Verzeichnisse ./bookstack/db und vorsichtshalber auch ./bookstack/app, damit beim nächsten docker compose up -d die Entrypoint-Skripte quasi bei Null anfangen und alle notwendigen Einrichtungsarbeiten durchführen. Wenn bei dir das Docker Cli-Plugin für Compose nicht installiert ist, dann benutze docker-compose statt docker compose.
 
Zuletzt bearbeitet:
Guten Morgen,
danke erstmal für deine gesamten Mühen und Erklärungen. Das hilft mir ungemein!

Uuuuund: Es läuft! :)
Grundlegender Fehler natürlich mit der Datenbank, da habe ich die Dokumentation von linuxserver arg missverstanden.

Vielen lieben Dank!

EDIT:
Ich habe jetzt nochmal probiert Mail-Unterstützung an den Start zu bringen und habe deshalb ein .env-File mit folgendem Inhalt erzeugt:
Code:
# Mail system to use
MAIL_DRIVER=smtp

# Mail sending options
MAIL_FROM=username@domain.com
MAIL_FROM_NAME=name

# SMTP mail options
MAIL_HOST=sub.domain.com
MAIL_PORT=587
MAIL_USERNAME=username@domain.com
MAIL_PASSWORD=password
MAIL_ENCRYPTION=STARTTLS

Bekomme da allerdings (sowohl mit, als auch ohne .env-File) die folgende Fehlermeldung:

Connection could not be established with host localhost :stream_socket_client(): Unable to connect to localhost:1025 (Address not available)

Was habe ich übersehen?
 
Zuletzt bearbeitet:
Wo liegt die .env Datei? Im Verzeichnis wo die compose Datei liegt?
Als Volume-Bind in den Container gemapped?

Mit localhost:1025 kann ich bisher nichts anfangen, ist nicht Teil der Konfigurationsdateien gewesen, die Du bisher geteilt hast....
 
Ich habe eben nochmal recherchiert und herausgefunden, dass Bookstack das .env-File nach dem Start des Containers unter var/www/bookstack/ erzeugt.

Der Port ist mir ebenfalls neu und wundere mich, woher Bookstack diesen bezieht.
Werde morgen mal das .env-File suchen und dort nochmal eintragen, laut diesem Entwickler-Kommentar auf Github sollte das danach laufen.
 
Ich würde dir dringend empfehlen immer erst in der Image-Beschreibung zu schauen, da Images IMMER opinionated sind und sich Abweichungen zur Dokumentation der Anwendung ergeben können.

https://hub.docker.com/r/linuxserver/bookstack schrieb:

Advanced Users (full control over the .env file)​

If you wish to use the extra functionality of BookStack such as email, Memcache, LDAP and so on you will need to make your own .env file with guidance from the BookStack documentation.

When you create the container, do not set any arguments for any SQL settings. The container will copy an exemplary .env file to /config/www/.env on your host system for you to edit.

Da du den Host-Pfad ./bookstack/app in den Container-Pfad /config mappst, sollte die Datei bei dir auf dem Host unter ./bookstack/app/www/.env liegen.
 

Zurzeit aktive Besucher

Letzte Anleitungen

Statistik des Forums

Themen
4.378
Beiträge
45.211
Mitglieder
3.976
Neuestes Mitglied
calibra52
Zurück
Oben