Docker: Kombi-Installation Traefik + n8n + MQTT

Dockern8n

Strato V-Server · 3 eigenständige Bausteine · Saubere Struktur


Übersicht & Architektur

Internet
    │
    ├── n8n01.linkemarco.de:443  ──▶  Traefik  ──▶  n8n Container
    │
    └── mqtt.linkemarco.de:8883  ──▶  Traefik  ──▶  Mosquitto Container

~/traefik/     # Baustein 1 – Reverse Proxy, läuft immer, unabhängig
~/n8n/         # Baustein 2 – nur n8n
~/mosquitto/   # Baustein 3 – nur Mosquitto

Gemeinsames Docker-Netzwerk: traefik-network

Designprinzip: Alle drei Bausteine folgen demselben Muster:

  • Ordnername = Dienstname (kein -compose Suffix, keine Technologie im Namen)
  • Daten liegen sichtbar als Bind-Mount im jeweiligen Ordner (data/, config/, log/) – keine versteckten Docker-Volumes mit auto-generierten Namen
  • Jeder Baustein ist unabhängig startbar/stoppbar

Verzeichnisstruktur:

~/traefik/
│   compose.yaml
│   .env
    letsencrypt/          # Zertifikate, sichtbar statt verstecktem Volume

~/n8n/
│   compose.yaml
│   .env
    data/                 # n8n Workflows, Credentials, Settings
    local-files/

~/mosquitto/
    compose.yaml
    .env
    config/
    ├── mosquitto.conf
    ├── passwd
    └── acl
    data/
    log/

Benutzer & Rechte (Übersicht)

BenutzerRolleTopic-Rechte
creedAdminAlle Topics + $SYS/# (Broker-Statistiken)
marcoPower-UserAlle normalen Topics lesen/schreiben
n8nServiceAlle normalen Topics lesen/schreiben
ulanziDeviceNur awtrix/# lesen/schreiben

Teil 0: Bestandsaufnahme & Vorbereitung

Schritt 1: Aktuelles n8n-Datenvolume identifizieren

docker volume ls | grep n8n

Erwartete Ausgabe, z.B.:

local     n8n-compose_n8n_data
local     n8n-compose_traefik_data

Schritt 2: Altes Setup stoppen

cd ~/n8n-compose
docker compose down

down löscht die Volumes nicht – deine Daten sind weiterhin sicher gespeichert, nur der Container ist gestoppt.


Baustein 1: Traefik (eigenständig, sauberer Neuaufbau)

Da das Zertifikat neu ausgestellt werden soll, kein Übernehmen alter Volumes nötig – kompletter sauberer Neustart.

Schritt 1: Netzwerk erstellen

docker network create traefik-network

Schritt 2: Verzeichnis anlegen

mkdir -p ~/traefik/letsencrypt
cd ~/traefik

Schritt 3: .env erstellen

nano .env

Inhalt:

SSL_EMAIL=deine@email.de

Schritt 4: compose.yaml erstellen

nano compose.yaml

Inhalt:

services:
  traefik:
    image: "traefik"
    container_name: traefik
    restart: always
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.mqtt.address=:8883"
      - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
      - "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
      - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8883:8883"
    volumes:
      - ./letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - traefik-network

networks:
  traefik-network:
    external: true

Zertifikate liegen jetzt sichtbar unter ~/traefik/letsencrypt/acme.json – kein verstecktes Docker-Volume mehr, konsistent mit dem Rest der Struktur.

Schritt 5: Traefik starten

docker compose up -d
docker compose logs -f

Baustein 2: n8n (eigenständig, saubere Struktur, Datenmigration)

Schritt 1: Neues Verzeichnis anlegen

mkdir -p ~/n8n/data ~/n8n/local-files

Schritt 2: Alte Daten migrieren

Die alten n8n-Daten aus dem Docker-Volume in den neuen Bind-Mount-Ordner kopieren:

docker run --rm \
  -v n8n-compose_n8n_data:/source \
  -v ~/n8n/data:/target \
  alpine sh -c "cp -av /source/. /target/"

Prüfen ob die Daten angekommen sind:

ls -la ~/n8n/data

Du solltest Dateien wie config, database.sqlite o.ä. sehen.

Schritt 3: Alte local-files migrieren (falls vorhanden)

cp -av ~/n8n-compose/local-files/. ~/n8n/local-files/

Schritt 4: .env erstellen

cd ~/n8n
nano .env

Inhalt:

DOMAIN_NAME=linkemarco.de
SUBDOMAIN=n8n01
GENERIC_TIMEZONE=Europe/Berlin

Schritt 5: compose.yaml erstellen

nano compose.yaml

Inhalt:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: 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=web,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
    environment:
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - N8N_RUNNERS_ENABLED=true
      - NODE_ENV=production
      - WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${GENERIC_TIMEZONE}
    volumes:
      - ./data:/home/node/.n8n
      - ./local-files:/files
    networks:
      - traefik-network

networks:
  traefik-network:
    external: true

Kein volumes:-Top-Level-Eintrag mehr nötig – reine Bind-Mounts, alles sichtbar unter ~/n8n/data/.

Schritt 6: Berechtigungen prüfen

n8n läuft im Container als User node (UID 1000). Falls es beim Start Berechtigungsprobleme gibt:

sudo chown -R 1000:1000 ~/n8n/data

Schritt 7: n8n starten

docker compose up -d
docker compose logs -f

n8n sollte unter https://n8n01.linkemarco.de erreichbar sein, mit allen alten Workflows.

Schritt 8: Nach erfolgreicher Prüfung aufräumen

Erst wenn du bestätigt hast dass alle Workflows/Credentials da sind:

docker volume rm n8n-compose_n8n_data
docker volume rm n8n-compose_traefik_data
rm -rf ~/n8n-compose

Baustein 3: Mosquitto (eigenständig)

Schritt 1: Verzeichnisse anlegen

mkdir -p ~/mosquitto/{config,data,log}
cd ~/mosquitto

Schritt 2: Alle Benutzer anlegen

# creed (Admin) – erster Benutzer, -c erstellt die Datei neu
docker run --rm -v $(pwd)/config:/mosquitto/config \
  eclipse-mosquitto \
  mosquitto_passwd -c /mosquitto/config/passwd creed

# marco (Power-User)
docker run --rm -v $(pwd)/config:/mosquitto/config \
  eclipse-mosquitto \
  mosquitto_passwd /mosquitto/config/passwd marco

# n8n (Service)
docker run --rm -v $(pwd)/config:/mosquitto/config \
  eclipse-mosquitto \
  mosquitto_passwd /mosquitto/config/passwd n8n

# ulanzi (Device)
docker run --rm -v $(pwd)/config:/mosquitto/config \
  eclipse-mosquitto \
  mosquitto_passwd /mosquitto/config/passwd ulanzi

Nur beim ersten Benutzer (creed) das Flag -c verwenden. Alle weiteren ohne -c, sonst wird die Datei überschrieben.

Schritt 3: ACL-Datei erstellen

nano config/acl

Inhalt:

# creed – Admin
user creed
topic readwrite #
topic read $SYS/#

# marco – Power-User
user marco
topic readwrite #

# n8n – Service
user n8n
topic readwrite #

# ulanzi – Device (nur awtrix Topics)
user ulanzi
topic readwrite awtrix/#

Schritt 4: mosquitto.conf erstellen

nano config/mosquitto.conf

Inhalt:

listener 1883

allow_anonymous false
password_file /mosquitto/config/passwd
acl_file /mosquitto/config/acl

persistence true
persistence_location /mosquitto/data/

log_dest file /mosquitto/log/mosquitto.log
log_type error
log_type warning
log_type notice
log_type information

Schritt 5: .env erstellen

nano .env

Inhalt:

MQTT_DOMAIN=mqtt.linkemarco.de

Schritt 6: compose.yaml erstellen

nano compose.yaml

Inhalt:

services:
  mosquitto:
    image: eclipse-mosquitto:latest
    container_name: mosquitto
    restart: unless-stopped
    volumes:
      - ./config/mosquitto.conf:/mosquitto/config/mosquitto.conf
      - ./config/passwd:/mosquitto/config/passwd
      - ./config/acl:/mosquitto/config/acl
      - ./data:/mosquitto/data
      - ./log:/mosquitto/log
    labels:
      - traefik.enable=true
      - traefik.tcp.routers.mqtt.rule=HostSNI(`${MQTT_DOMAIN}`)
      - traefik.tcp.routers.mqtt.entrypoints=mqtt
      - traefik.tcp.routers.mqtt.tls=true
      - traefik.tcp.routers.mqtt.tls.certresolver=mytlschallenge
      - traefik.tcp.services.mqtt.loadbalancer.server.port=1883
    networks:
      - traefik-network

networks:
  traefik-network:
    external: true

Schritt 7: Mosquitto starten

docker compose up -d
docker compose logs -f

Firewall & DNS (einmalig)

Strato Firewall:

ProtokollPortRichtungQuelle
TCP8883Eingehend0.0.0.0/0

DNS A-Record:

mqtt.linkemarco.de  →  A  →  <VPS-IP>  →  TTL: 300

Verbindung testen

# Topic subscriben (als marco)
mosquitto_sub -h mqtt.linkemarco.de -p 8883 \
  --capath /etc/ssl/certs \
  -u marco -P <passwort> \
  -t "#" -v

# Publishen (als n8n)
mosquitto_pub -h mqtt.linkemarco.de -p 8883 \
  --capath /etc/ssl/certs \
  -u n8n -P <passwort> \
  -t "awtrix/custom" \
  -m '{"text": "Setup OK!", "color": "#00FF00"}'

Ulanzi TC001 verbinden

Im AWTRIX Webinterface (http://<ulanzi-ip>) → MQTT:

  • Host: mqtt.linkemarco.de
  • Port: 8883
  • TLS: aktivieren
  • Username: ulanzi
  • Password: <ulanzi-passwort>
  • Prefix: awtrix

n8n MQTT-Credentials

In n8n → Settings → Credentials → New → MQTT:

  • Host: mosquitto (interner Docker-Hostname)
  • Port: 1883
  • Username: n8n
  • Password: <n8n-passwort>

Alltag: Jeder Baustein unabhängig verwaltbar

# Nur Traefik neu starten (n8n & Mosquitto bleiben unberührt)
cd ~/traefik && docker compose restart

# Nur n8n updaten
cd ~/n8n && docker compose pull && docker compose up -d

# Nur Mosquitto neu laden
cd ~/mosquitto && docker compose restart

# Status aller drei Bausteine prüfen
docker ps --format "table {{.Names}}\t{{.Status}}"

# Backup eines Bausteins (Beispiel n8n) – da alles Bind-Mount ist, reicht tar
tar -czvf n8n-backup-$(date +%Y%m%d).tar.gz ~/n8n/data

Nützliche MQTT-Befehle

# Alle Topics beobachten (intern)
docker exec -it mosquitto mosquitto_sub \
  -u creed -P <passwort> -t "#" -v

# Neuen Benutzer hinzufügen
docker run --rm -v $(pwd)/config:/mosquitto/config \
  eclipse-mosquitto \
  mosquitto_passwd /mosquitto/config/passwd neuer-user

# Passwort ändern
docker run --rm -v $(pwd)/config:/mosquitto/config \
  eclipse-mosquitto \
  mosquitto_passwd /mosquitto/config/passwd marco

# Benutzer löschen
docker run --rm -v $(pwd)/config:/mosquitto/config \
  eclipse-mosquitto \
  mosquitto_passwd -D /mosquitto/config/passwd alter-user

# Config neu laden ohne Neustart
docker exec -it mosquitto kill -HUP 1

Zusammenfassung

BausteinVerzeichnisDatenZertifikat/Auth
Traefik~/traefik/letsencrypt/ (Bind-Mount)stellt neues Let’s Encrypt Zertifikat aus
n8n~/n8n/data/ (Bind-Mount, migriert)nutzt Traefik-Zertifikat
Mosquitto~/mosquitto/data/, config/, log/ (Bind-Mount)nutzt Traefik-Zertifikat, eigene User/ACL

Konsistenzprinzip: Alle drei Ordner folgen demselben Aufbau – Dienstname als Ordnername, .env für Variablen, compose.yaml für die Definition, alle persistenten Daten sichtbar als Unterordner statt versteckter Docker-Volumes.