Obsah

Traefik - reverzní proxy

Tento návod ukazuji, jak jsem nasadil Traefik jako reverse proxy v Dockeru a použil jej pro přístup k aplikaci Grafana. Vysvětluji, jak Traefik funguje, popisuji klíčové části konfiguračních souborů a krok za krokem ukazuji proces nasazení.

Jak Traefik funguje?

Traefik je reverse proxy a load balancer, který:

V tomto návodu budete přistupovat ke Grafaně přes URL:

https://<IP_adresa>/grafana

Namísto přístupu přes konkrétní port, jako je:

http://<IP_adresa>:3000

Zjistil jsem, že Traefik lze použít i pro vygenerování a správu certifikátů z Let's Encrypt - včetně použití pro generování certifikátu pro vnitřní síť. O tento postup jsem rozšířil návod.

Předpoklady

Struktura projektu

Ve složce projektu budete mít následující soubory:

.
├── docker-compose.yml
├── traefik.yml
├── certs/
│   ├── selfsigned.crt
│   ├── selfsigned.key

Konfigurace

1. docker-compose.yml

Tento soubor definuje Traefik jako proxy a službu Grafana. Uvnitř jsou klíčové části:

version: "3.9"

services:
  grafana:
    container_name: grafana-teslamate
    image: teslamate/grafana:latest
    restart: always
    environment:
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
    labels:
      # aktivace Traefiku pro tento kontejner
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.grafana-insecure.rule: "PathPrefix(`/grafana`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.grafana.rule: "PathPrefix(`/grafana`)"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.services.grafana.loadbalancer.server.port: "3000"

    volumes:
      - teslamate-grafana-data:/var/lib/grafana

  proxy:
    image: traefik
    restart: always
    container_name: traefik
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./certs:/etc/traefik/certs:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro

volumes:
  teslamate-grafana-data:
  certs:

Vysvětlení klíčových částí:

`grafana` služba:

`proxy` služba (Traefik):

2. traefik.yml

Soubor traefik.yml obsahuje statickou konfiguraci pro Traefik:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls: {}

tls:
  certificates:
    - certFile: "/etc/traefik/certs/selfsigned.crt"
      keyFile: "/etc/traefik/certs/selfsigned.key"

providers:
  docker:
    exposedByDefault: false

global:
  sendAnonymousUsage: false

Vysvětlení klíčových částí:

3. Certifikáty (certs/)

Do složky certs/ umístěte vaše self-signed certifikáty:

Pokud nemáte certifikáty, můžete je vytvořit pomocí OpenSSL:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certs/selfsigned.key -out certs/selfsigned.crt

Nasazení

docker-compose up -d
https://<IP_adresa>/grafana

Pokud máte potíže s přihlášením do Grafany, resetujte heslo administrátora pomocí následujícího příkazu:

docker compose exec grafana grafana-cli admin reset-admin-password

Limity se self-signed certifikáty

Během konfigurace Teslamate jsem narazil na několik omezení a problémů spojených se self-signed certifikáty:

Abych tento problém vyřešil, plánuji v interní síti zavést DNS server. Tento server zajistí, že při přístupu na veřejnou doménu, například `teslamate.nosekpetr.cz`, bude interním klientům vrácena lokální IP adresa serveru. Díky této konfiguraci mohu pro každou službu použít vlastní subdoménu a zároveň zajistit validní certifikát od Let's Encrypt.

Konfigurace Docker-Compose - s omezením pro self signed certifikáty

Následující je aktuální konfigurace Docker-Compose s komentáři vysvětlujícími jednotlivé části:

version: "3.3"  # Verze Docker Compose

services:
  teslamate:
    container_name: teslamate
    image: teslamate/teslamate:latest
    restart: always
    environment:
      - ENCRYPTION_KEY=*** # Bezpečný klíč pro šifrování Tesla API tokenů
      - DATABASE_USER=***
      - DATABASE_PASS=*** # Heslo pro přístup k databázi
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - VIRTUAL_HOST=192.168.1.123 # Hostitel pro Traefik
      - MQTT_HOST=192.168.1.150 # MQTT broker
      - MQTT_PORT=8883 # Port pro MQTT
      - MQTT_USERNAME=***
      - MQTT_PASSWORD=***
      - MQTT_TLS=true # Povolení šifrovaného připojení k MQTT
      - MQTT_TLS_ACCEPT_INVALID_CERTS=true # Akceptování self-signed certifikátů pro MQTT
      - TZ=Europe/Prague # Nastavení časové zóny
    volumes:
      - ./import:/opt/app/import
    labels:
      traefik.enable: "true" # Aktivace Traefiku pro tento kontejner
 
      # Nastavení přesměrování HTTP -> HTTPS
      traefik.http.routers.teslamate-insecure.rule: "Host(`192.168.1.123`)"
      traefik.http.routers.teslamate-insecure.entrypoints: "web"
      traefik.http.routers.teslamate-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování HTTP -> HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # Nastavení pro WebSocket Teslamate
      traefik.http.routers.teslamate-ws.rule: "Host(`192.168.1.123`) && Path(`/live/websocket`)"
      traefik.http.routers.teslamate-ws.entrypoints: "websecure"
 
      # Zabezpečení pomocí Basic Auth
      traefik.http.routers.teslamate.middlewares: "teslamate-auth"
      traefik.http.routers.teslamate.rule: "Host(`192.168.1.123`)"
      traefik.http.routers.teslamate.entrypoints: "websecure"
      traefik.http.services.teslamate.loadbalancer.server.port: "4000"
      traefik.http.middlewares.teslamate-auth.basicauth.realm: "teslamate"
      traefik.http.middlewares.teslamate-auth.basicauth.usersfile: "/auth/.htpasswd"

    cap_drop:
      - all # Omezení privilegovaných operací kontejneru

  database:
    container_name: postgres
    image: postgres:17
    restart: always
    environment:
      - POSTGRES_USER=***
      - POSTGRES_PASSWORD=*** # Heslo k databázi
      - POSTGRES_DB=***
      - TZ=Europe/Prague # Nastavení časové zóny
    volumes:
      - teslamate-db:/var/lib/postgresql/data

  grafana:
    container_name: grafana-teslamate
    image: teslamate/grafana:latest
    restart: always
    environment:
      - DATABASE_USER=***
      - DATABASE_PASS=*** # Heslo k databázi
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - TZ=Europe/Prague
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana # URL pro Grafanu
      - GF_SERVER_SERVE_FROM_SUB_PATH=true # Povolení subcesty pro Grafanu
    labels:
      traefik.enable: "true" # Aktivace Traefiku
 
      # Přesměrování HTTP -> HTTPS
      traefik.http.routers.grafana-insecure.rule: "Host(`192.168.1.123`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"

      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.grafana.rule: "Host(`192.168.1.123`) && (Path(`/grafana`) || PathPrefix(`/grafana/`))"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.services.grafana.loadbalancer.server.port: "3000"
    volumes:
      - teslamate-grafana-data:/var/lib/grafana

  proxy:
    image: traefik
    restart: always
    container_name: traefik
    ports:
      - "443:443" # HTTPS
      - "80:80"   # HTTP (přesměrování na HTTPS)
    volumes:
      - ./.htpasswd:/auth/.htpasswd # Soubor pro Basic Auth
      - /var/run/docker.sock:/var/run/docker.sock # Přístup k Docker API
      - ./certs:/etc/traefik/certs:ro # Certifikáty
      - ./traefik.yml:/etc/traefik/traefik.yml:ro # Konfigurace Traefiku

volumes:
  teslamate-db:
  teslamate-grafana-data:
  certs:
  

Certifikáty z Let's Encrypt pro uzavřenou síť

Traefik je výborný na správu a generování certifikátů z Let's Encrypt. Pro veřejnou IP adresu bych použil HTTP Challenge, ale pro IP adresu, kterou používám čistě ve vnitřní síti potřebuji použít tzv. DNS-01 chalenge pro získání certifikátů. Jak to funguje jsem popsal zde: Certifikáty Let's Encrypt v uzavřené síti.

docker-compose.yml:

version: "3.3"  # Nastavení verze Docker Compose

services:
  teslamate:
    container_name: teslamate
    image: teslamate/teslamate:latest
    restart: always
    environment:
      - ENCRYPTION_KEY=*** #replace with a secure key to encrypt your Tesla API tokens
      - DATABASE_USER=***
      - DATABASE_PASS=*** #insert your secure database password!
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - VIRTUAL_HOST=teslamate.nosekpetr.cz
        #- MQTT_HOST=mosquitto
      - MQTT_HOST=192.168.1.150
      - MQTT_PORT=8883
      - MQTT_USERNAME=***
      - MQTT_PASSWORD=***
      - MQTT_TLS=true
      - MQTT_TLS_ACCEPT_INVALID_CERTS=true
      - TZ=Europe/Prague
    volumes:
      - ./import:/opt/app/import
    labels:
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.teslamate-insecure.rule: "Host(`teslamate.nosekpetr.cz`)"
      traefik.http.routers.teslamate-insecure.entrypoints: "web"
      traefik.http.routers.teslamate-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.teslamate-ws.rule: "Host(`teslamate.nosekpetr.cz`) && Path(`/live/websocket`)"
      traefik.http.routers.teslamate-ws.entrypoints: "websecure"
      traefik.http.routers.teslamate-ws.tls: ""

      traefik.http.routers.teslamate.middlewares: "teslamate-auth"
      traefik.http.routers.teslamate.rule: "Host(`teslamate.nosekpetr.cz`)"
      traefik.http.routers.teslamate.entrypoints: "websecure"
      traefik.http.routers.teslamate.tls.certresolver: "tmdnschallenge"
      traefik.http.services.teslamate.loadbalancer.server.port: "4000"
 

      traefik.http.middlewares.teslamate-auth.basicauth.realm: "teslamate"
      traefik.http.middlewares.teslamate-auth.basicauth.usersfile: "/auth/.htpasswd"

    cap_drop:
      - all

  database:
    container_name: postgres
    image: postgres:17
    restart: always
    environment:
      - POSTGRES_USER=***
      - POSTGRES_PASSWORD=*** #insert your secure database password!
      - POSTGRES_DB=***
      - TZ=Europe/Prague
    volumes:
      - teslamate-db:/var/lib/postgresql/data

  grafana:
    container_name: grafana-teslamate
    image: teslamate/grafana:latest
    restart: always
    environment:
      - DATABASE_USER=***
      - DATABASE_PASS=*** #insert your secure database password!
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - TZ=Europe/Prague
      - GF_SERVER_DOMAIN=teslamate.nosekpetr.cz
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
    labels:
      # aktivace Traefiku pro tento kontejner
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.grafana-insecure.rule: "Host(`teslamate.nosekpetr.cz`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.grafana.rule: "Host(`teslamate.nosekpetr.cz`) && (Path(`/grafana`) || PathPrefix(`/grafana/`))"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.routers.grafana.tls.certresolver: "tmdnschallenge"
      traefik.http.services.grafana.loadbalancer.server.port: "3000"
    volumes:
      - teslamate-grafana-data:/var/lib/grafana

  proxy:
    image: traefik
    restart: always
    container_name: traefik
    ports:
      - "443:443" # HTTPS
      - "80:80"   # HTTP (případně přesměrování na HTTPS)
    environment:
      - CLOUDFLARE_EMAIL=***
      - CLOUDFLARE_DNS_API_TOKEN=***
      - LETSENCRYPT_EMAIL=***
    volumes:
      - ./acme/:/etc/acme/
      - ./.htpasswd:/auth/.htpasswd
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yml:/etc/traefik/traefik.yml:ro

traefik.yml:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: tmdnschallenge # Přidání certifikátového resolveru pro HTTPS
 

certificatesResolvers:
  tmdnschallenge:
    acme:
      email: "***"  # Email pro komunikaci s Let's Encrypt
      storage: "/etc/acme/acme.json"  # Úložiště certifikátů
      dnsChallenge:
        provider: cloudflare  # Použití Cloudflare API
        delayBeforeCheck: 10  # Volitelná hodnota pro zpoždění ověření

providers:
  docker:
    exposedByDefault: false

global:
  sendAnonymousUsage: false

Přesné URL pro přístup

Důležité parametry Grafany

Následující proměnné prostředí jsou klíčové pro správnou konfiguraci a umožňují provoz Grafany na specifické subcestě:

Tato proměnná definuje doménu, na které je Grafana dostupná. Umožňuje Traefiku směrovat požadavky na správný kontejner.

Nastavuje kořenovou URL pro Grafanu včetně specifikace subcesty `/grafana`. Díky tomu je možné Grafanu provozovat na této subcestě bez konfliktů s jinými službami.

Aktivuje podporu pro provozování Grafany na specifikované subcestě (`/grafana`). Bez této proměnné by Grafana nebyla správně dostupná.

Důležité proměnné pro Traefik

Pro správné fungování DNS-01 Challenge s Let's Encrypt prostřednictvím Cloudflare je potřeba nastavit následující proměnné prostředí:

Emailová adresa spojená s vaším účtem Cloudflare. Používá se pro autentizaci k API.

API token s dostatečnými oprávněními k úpravě DNS záznamů vaší domény. Tento token musí být vytvořen v Cloudflare.

Emailová adresa použitá pro registraci u Let's Encrypt. Slouží k notifikacím, například o expiraci certifikátu.

Nextcloud

Příprava Postgres databáze

Rozhodl jsem se využít existující docker s Postgres. Pro připojení k databázovému serveru Postgres jsem použil následující kroky.

Nejprve jsem se připojil k běžícímu kontejneru s Postgres pomocí příkazu:

docker exec -it postgres bash

V rámci kontejneru jsem se připojil k databázovému serveru s těmito údaji:

psql -U ev_tesla_user -d postgres

Pro zobrazení aktuálně existujících uživatelů jsem použil příkaz:

\du

Výsledek byl následující:

                                 List of roles
   Role name    |                         Attributes                         
----------------+------------------------------------------------------------
 ev_tesla_user  | Superuser, Create role, Create DB, Replication, Bypass RLS

Následně jsem vypsal všechny dostupné databáze příkazem:

\l

Nakonec jsem vytvořil nového uživatele, databázi a přiřadil jsem uživateli práva k této databázi následujícími příkazy:

CREATE USER nextcloud_user WITH PASSWORD 'secure_password';
CREATE DATABASE nextcloud_db OWNER nextcloud_user;

Konfigurace Nextcloud v Dockeru

Protože všechno poběží na Raspberry Pi, není možné použít oficiální kontejner Nextcloud AIO. Ten není bohužel pro ARM architekturu. Proto jsem použil nextcloud a čerpal z hlavní dokumentace a doplňující dokumentace k reverzní proxy.

Zároveň jsem nastavil cacheovací server redis. Do docker-file.yml jsem tedy přidal:

 nextcloud:
   container_name: nextcloud
   image: nextcloud:apache
   restart: always
   extra_hosts:
     - "nextcloud.nosekpetr.cz:192.168.1.123"
   environment:
     - POSTGRES_DB=nextcloud_db
     - POSTGRES_USER=nextcloud_user
     - POSTGRES_PASSWORD=*** #replace with your secure password
     - POSTGRES_HOST=database
     - TRUSTED_PROXIES=192.168.1.0/24
     - OVERWRITEHOST=nextcloud.nosekpetr.cz
     - OVERWRITEPROTOCOL=https
     - APACHE_DISABLE_REWRITE_IP=1
     - OVERWRITECLIURL=https://nextcloud.nosekpetr.cz
     - REDIS_HOST=redis
     - REDIS_HOST_PASSWORD=***
   volumes:
     - ./nextcloud:/var/www/html
   labels:
     traefik.enable: "true"
 
     # Redirect HTTP to HTTPS
     traefik.http.routers.nextcloud-insecure.rule: "Host(`nextcloud.nosekpetr.cz`)"
     traefik.http.routers.nextcloud-insecure.entrypoints: "web"
     traefik.http.routers.nextcloud-insecure.middlewares: "redirect"
 
     # Middleware for HTTPS redirection
     traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
     # HTTPS router
     traefik.http.routers.nextcloud.rule: "Host(`nextcloud.nosekpetr.cz`)"
     traefik.http.routers.nextcloud.entrypoints: "websecure"
     traefik.http.routers.nextcloud.tls.certresolver: "tmdnschallenge"
     traefik.http.services.nextcloud.loadbalancer.server.port: "80"
 
     # Middleware for CalDAV/CardDAV redirects
     traefik.http.middlewares.nextcloud_redirectregex.redirectregex.permanent: "true"
     traefik.http.middlewares.nextcloud_redirectregex.redirectregex.regex: "https://(.*)/.well-known/(?:card|cal)dav"
     traefik.http.middlewares.nextcloud_redirectregex.redirectregex.replacement: "https://$${1}/remote.php/dav"
 
     # Add HSTS header
     traefik.http.middlewares.nextcloud-hsts.headers.stsSeconds: "15552000"
     traefik.http.middlewares.nextcloud-hsts.headers.stsIncludeSubdomains: "true"
     traefik.http.middlewares.nextcloud-hsts.headers.stsPreload: "true"

     traefik.http.routers.nextcloud.middlewares: "nextcloud-hsts"

   depends_on:
     - database
     - redis

 redis:
   container_name: redis
   image: redis:alpine
   restart: always
   command: ["redis-server", "--requirepass", "***"] #replace with your secure password
   volumes:
     - redis-data:/data
 

volumes:
 teslamate-db:
 teslamate-grafana-data:
 redis-data:

extra_hosts

Konfigurace extra_hosts umožňuje nastavit vlastní DNS záznamy uvnitř kontejneru. To se hodí například pro řešení situací, kdy je třeba přiřadit určitou IP adresu k doménovému jménu, aniž by se tato změna musela provádět na externím DNS serveru.

extra_hosts:
  - "nextcloud.nosekpetr.cz:192.168.1.123"

Znamená, že požadavky na `nextcloud.nosekpetr.cz` budou směrovány na IP adresu `192.168.1.123`.

Proměnné prostředí

TRUSTED_PROXIES, OVERWRITEHOST, OVERWRITEPROTOCOL, APACHE_DISABLE_REWRITE_IP, a OVERWRITECLIURL slouží k zajištění správné funkce Nextcloudu za reverzní proxy.

Tento parametr definuje seznam důvěryhodných proxy serverů. Je nezbytné, aby byla IP adresa reverzní proxy zahrnuta do tohoto seznamu, jinak může Nextcloud blokovat přístup.

Tato hodnota přepisuje hodnotu `HTTP_HOST` tak, aby odpovídala doméně, přes kterou je Nextcloud přístupný.

Přepíše protokol `HTTP` na `HTTPS`, což zajistí, že aplikace bude fungovat výhradně přes zabezpečené spojení.

Zakazuje přepisování IP adres uživatele, které Apache provádí na základě hlaviček `X-Forwarded-For`.

Nastavuje základní URL, kterou bude Nextcloud používat pro CLI operace. Toto je užitečné například při spouštění úloh cron nebo při komunikaci s externími API.

Middleware pro CalDAV/CardDAV

Protokoly CalDAV a CardDAV jsou používány k synchronizaci kalendářů a kontaktů. Následující middleware zajišťuje, že tyto služby fungují správně i za reverzní proxy:

traefik.http.middlewares.nextcloud_redirectregex.redirectregex.permanent: "true"
traefik.http.middlewares.nextcloud_redirectregex.redirectregex.regex: "https://(.*)/.well-known/(?:card|cal)dav"
traefik.http.middlewares.nextcloud_redirectregex.redirectregex.replacement: "https://$${1}/remote.php/dav"

Zajišťuje, že přesměrování je trvalé (kód 301).

Regular expression `https:(.*)/.well-known/(?:card|cal)dav` zachytí požadavky na CalDAV/CardDAV endpointy a přesměruje je na `https:<domain>/remote.php/dav`, což je správná cesta v Nextcloudu.

Middleware pro HSTS

HTTP Strict Transport Security (HSTS) chrání uživatele tím, že vyžaduje, aby byla veškerá komunikace vedena přes HTTPS:

traefik.http.middlewares.nextcloud-hsts.headers.stsSeconds: "15552000"
traefik.http.middlewares.nextcloud-hsts.headers.stsIncludeSubdomains: "true"
traefik.http.middlewares.nextcloud-hsts.headers.stsPreload: "true"

Udává dobu (v sekundách), po kterou má být HSTS aktivní. Zde je nastavena na 180 dní.

Rozšiřuje politiku HSTS i na všechny subdomény.

Označuje doménu pro zařazení do seznamu HSTS preload, který používají webové prohlížeče.

Middleware je následně přidán k routeru pomocí:

traefik.http.routers.nextcloud.middlewares: "nextcloud-hsts"

OCC v NextCloudu

OCC (OwnCloud Command) je příkazový nástroj, který umožňuje správu a údržbu Nextcloudu z příkazové řádky. Tento nástroj je velmi užitečný pro provádění různých administrativních úkolů, jako je oprava dat, správa uživatelů, aktualizace databáze nebo nastavení systému.

Takto jsem spouštěl příkaz v Dockeru:

docker exec -u 33 -it nextcloud php /var/www/html/occ maintenance:repair --include-expensive

Update Nextcloud Dockeru na novější verzi dle dokumentace

Při aktualizaci Nextcloudu v Dockeru je potřeba postupovat podle následujících kroků. Upozornění: Aktualizovat je možné pouze o jednu hlavní verzi najednou. Například při přechodu z verze 14 na 16 je nutné nejdříve aktualizovat z 14 na 15 a poté z 15 na 16.

Krok 1: Stažení nové verze kontejneru Stáhněte nejnovější verzi obrazu Nextcloudu pomocí příkazu:

docker pull nextcloud

Krok 2: Zastavení a odstranění starého kontejneru Zastavte běžící kontejner Nextcloudu:

docker stop <jméno_vašeho_kontejneru>

Odstraňte starý kontejner:

docker rm <jméno_vašeho_kontejneru>

Krok 3: Spuštění nového kontejneru Spusťte nový kontejner se stejnými parametry, které byly použity při původním spuštění (například volumes, porty, apod.):

docker run <OPTIONS> -d nextcloud

Ujistěte se, že při spuštění zahrnete všechny svazky (volumes), aby nedošlo ke ztrátě dat. Startup skript automaticky zjistí nesoulad mezi verzí v uložených datech a verzí kontejneru a spustí proces aktualizace.

Použití Docker Compose

Pokud používáte Docker Compose, je aktualizace jednodušší, protože všechny parametry jsou již zapsány v souboru `docker-compose.yml`. Postupujte podle těchto kroků:

1. Stáhněte aktualizovaný obraz:

docker compose pull

2. Restartujte kontejnery s novou verzí:

docker compose up -d

Tímto způsobem dojde k aktualizaci Nextcloudu na novější verzi bez ztráty dat. Nezapomeňte zkontrolovat, zda všechny funkce fungují správně po aktualizaci.

Problém s certifikáty v Chrome

Tato chyba byla velmi zrádná, protože komplikace vycházela z problému na straně DNS serveru, což jsem nečekal. K tomu se přidalo, že ve Firefoxu vše fungovalo správně, ale v Chrome se problém projevoval. Zároveň se chyba objevovala pouze u Nextcloudu, což situaci ještě více zkomplikovalo.

Chrome odmítl stránku zobrazit a hlásil chybu certifikátu: ERR_ECH_FALLBACK_CERTIFICATE_INVALID.

Zjistil jsem, že příčinou je chybějící podpora ECH (Encrypted Client Hello). ECH je moderní technologie šifrování, kterou prohlížeč Chrome očekává. Pokud server neposkytuje správně nakonfigurovaný certifikát, Chrome to detekuje a hlásí chybu.

Dočasným řešením bylo spustit Chrome s vypnutou funkcí ECH:

google-chrome --disable-features=EncryptedClientHello

Tato úprava umožnila správné zobrazení stránky.

Při dalším pátrání jsem v diskusi na Traefik fóru - ERR_ECH_FALLBACK_CERTIFICATE_INVALID errors zjistil, že problém může být způsoben Cloudflare. Cloudflare využívá HTTPS záznamy pro „opportunistic HTTPS to HTTPS upgrade“, ECH a další funkce, které způsobují přidání vnější vrstvy k požadavkům ECH s doménou cloudflare-ech.com.

Dalším faktorem byla omezená podpora TLS 1.3 v Traefiku, jak potvrzují dokumentace a plány na implementaci v Go 1.24, což má být dostupné v únoru 2025.

Na základě doporučení v diskusi jsem v Traefik konfiguraci přešel na TLS 1.2. Takto vypadá můj soubor `traefik.yml`:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: tmdnschallenge
        options: maxtls12

certificatesResolvers:
  tmdnschallenge:
    acme:
      email: "" 
      storage: "/etc/acme/acme.json"
      dnsChallenge:
        provider: cloudflare
        delayBeforeCheck: 10

providers:
  docker:
    exposedByDefault: false

global:
  sendAnonymousUsage: false

tls:
  options:
    maxtls12:
      minVersion: VersionTLS12
      maxVersion: VersionTLS12
      sniStrict: true

Nicméně ani toto řešení nestačilo. Nakonec jsem upravil nastavení v Cloudflare. V menu SSL/TLS → Edge Certificates jsem deaktivoval podporu TLS 1.3:

TLS 1.3  
Enable the latest version of the TLS protocol for improved security and performance.

Výsledek se projevil až po několika hodinách (cca 6 hodin až den). Tato kombinace kroků vedla k úspěšnému vyřešení problému.

PHP s Apache

version: "3.3"

services:
  php-apache:
    container_name: php-apache
    image: php:apache
    restart: always
    volumes:
      - ./smarthome:/var/www/html:ro  # Statické soubory a PHP aplikace
    labels:
      traefik.enable: "true"
 
      # Redirect HTTP to HTTPS
      traefik.http.routers.smarthome-insecure.rule: "Host(`smarthome.nosekpetr.cz`)"
      traefik.http.routers.smarthome-insecure.entrypoints: "web"
      traefik.http.routers.smarthome-insecure.middlewares: "redirect"
 
      # Middleware for HTTPS redirection
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS router
      traefik.http.routers.smarthome.rule: "Host(`smarthome.nosekpetr.cz`)"
      traefik.http.routers.smarthome.entrypoints: "websecure"
      traefik.http.routers.smarthome.tls.certresolver: "tmdnschallenge"
      traefik.http.services.smarthome.loadbalancer.server.port: "80"

Nodered

  nodered:
    container_name: nodered
    build:
      context: ../IOTstack/services/nodered/.
      args:
      - DOCKERHUB_TAG=latest
      - EXTRA_PACKAGES=
    restart: unless-stopped
    user: "0"
    environment:
      - TZ=Europe/Prague
    volumes:
      - ./nodered/data:/data
      - ./nodered/ssh:/root/.ssh
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
    devices:
      - "/dev/ttyAMA0:/dev/ttyAMA0"
      - "/dev/vcio:/dev/vcio"
      - "/dev/gpiomem:/dev/gpiomem"
    labels:
      traefik.enable: "true"
 
      # Přesměrování HTTP na HTTPS
      traefik.http.routers.nodered-insecure.rule: "Host(`nodered.nosekpetr.cz`)"
      traefik.http.routers.nodered-insecure.entrypoints: "web"
      traefik.http.routers.nodered-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS konfigurace
      traefik.http.routers.nodered.rule: "Host(`nodered.nosekpetr.cz`)"
      traefik.http.routers.nodered.entrypoints: "websecure"
      traefik.http.routers.nodered.tls.certresolver: "tmdnschallenge"
      traefik.http.services.nodered.loadbalancer.server.port: "1880"
 

Grafana

Grafana pro jiné použití než vykreslení grafů z Teslamate.

  grafana:
    container_name: grafana
    image: grafana/grafana
    restart: unless-stopped
    user: "0"
    environment:
      - TZ=Europe/Prague
      - GF_PATHS_DATA=/var/lib/grafana
      - GF_PATHS_LOGS=/var/log/grafana
    volumes:
      - ./grafana/data:/var/lib/grafana
      - ./grafana/log:/var/log/grafana
    healthcheck:
      test: ["CMD", "wget", "-O", "/dev/null", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    labels:
      traefik.enable: "true"
 
      # Redirect HTTP to HTTPS
      traefik.http.routers.grafana-insecure.rule: "Host(`grafana.nosekpetr.cz`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"
 
      # Middleware for HTTPS redirection
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS router
      traefik.http.routers.grafana.rule: "Host(`grafana.nosekpetr.cz`)"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.routers.grafana.tls.certresolver: "tmdnschallenge"
 
      # Define service port
      traefik.http.services.grafana.loadbalancer.server.port: "3000"

Gitea

Na správu projektů, zdrojového kódu, verzování, klientských dat atp. jsem chtěl používat git. Chtěl jsem na serveru jednoduchou službu, abych si mohl zobrazit přehled repozitářů. Podobně jako GitHub. A úplně perfektní pro moje potřeby se ukázal projekt Gitea.

K mojí smůle mám aktuálně Raspberry Pi s 32bitovým OS. Nemůžu teď přecházet na 64bit a Gitea není na hub.docker.com v 32bitové verzi, pouze 64bitové. Takže to vypadalo špatně. Nicméně jako schůdné řešení se ukázalo stáhnout zdrojové kódy Gitea a vytvořit Dockerfile pro 32bit ARM.

Stáhl jsem zdrojové soubory a udělal jsem docker build pro ARM:

git clone https://github.com/go-gitea/gitea.git
cd gitea
docker build --platform linux/arm/v7 -t gitea-arm32 .

Pro informaci:

Podařilo se mi úspěšně sestavit a rozběhnout.

Tady je moje konfigurace do `docker-compose.yml`:

gitea:
  image: gitea-arm32
  container_name: gitea
  restart: "no"
  environment:
    - USER_UID=1000
    - USER_GID=1000
    - GITEA__database__DB_TYPE=postgres
    - GITEA__database__HOST=postgres
    - GITEA__database__NAME=${GITEA_DB}
    - GITEA__database__USER=${GITEA_DB_USER}
    - GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
  restart: always
  volumes:
    - ${GITEA_VOLUME_PATH}:/data
    - /etc/timezone:/etc/timezone:ro
    - /etc/localtime:/etc/localtime:ro
  depends_on:
    - postgres
  labels:
    traefik.enable: "true"
    traefik.http.routers.gitea-insecure.rule: "Host(`${FQDN_GITEA}`)"
    traefik.http.routers.gitea-insecure.entrypoints: "web"
    traefik.http.routers.gitea-insecure.middlewares: "redirect"
    traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
    traefik.http.routers.gitea.rule: "Host(`${FQDN_GITEA}`)"
    traefik.http.routers.gitea.entrypoints: "websecure"
    traefik.http.routers.gitea.tls.certresolver: "tmdnschallenge"
    traefik.http.services.gitea.loadbalancer.server.port: "3000"

Problémy se kterými jsem se setkal při provozu Gitea

Při nahrávání většího repozitáře do Gitea (cca 2 GB) se objevila tato chyba:

error: RPC failed; HTTP 500 curl 22 The requested URL returned error: 500 Internal Server Error

Po hledání řešení jsem zjistil následující:

1. Zvýšil jsem globální Git buffer:

git config --global http.postBuffer 157286400

Nebo pouze pro daný repozitář:

cd your_repo
git config http.postBuffer 157286400

Více informací o tomto nastavení naleznete na: https://stackoverflow.com/questions/44780221/git-push-failing-http-500-curl-22-the-requested-url-returned-error-500-internal

2: Nastavil jsem `http.keepAlive`:

git config http.keepAlive true

Pro obnovení výchozího stavu (pokud člověk nastaví parametry globálně):

git config --global --unset http.postBuffer
git config --global --unset http.keepAlive

3. V konfiguraci Traefiku jsem přidal část transport:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: tmdnschallenge
        options: maxtls12
    transport:
      respondingTimeouts:
        readTimeout: 30m

Zdroj informací: https://community.traefik.io/t/increase-maximum-body-size-without-buffering/15928/6

Po těchto úpravách jsem mohl nahrát i objemnější repozitář.

Zabezpečení Gitea a přihlašování bez hesla

I když provozuji server lokálně, tak jede přes HTTPS a nechci, aby byl repozitář volně přístupný v síti. Ovšem na počítači, kde mám stažený repozitář, nechci při každé aktualizaci zadávat heslo.

V Gitea jsem vytvořil token pro přihlašování místo hesla. Na svém počítači jsem si v domovském adresáři vytvořil soubor `.netrc`:

machine gitea.servername.cz
login muj_uzivatel_gitea
password sem_prijde_token

Tím jsem dosáhl pohodlného přihlašování bez opakovaného zadávání hesla.

Zabezpečení Solar Assistanta na portu 80 přes Traefik

Potřeboval jsem zabezpečit Solar Assistanta, který běží na Raspberry Pi na portu 80, a rozhodl jsem se k tomu využít Traefik. Vytvořil jsem tunel pomocí `autossh` ze serveru, na kterém běží Traefik.

SSH tunel je šifrovaný a web Solar Assistanta byl po propojení dostupný na adrese `localhost:8089`. Cílem tedy bylo nastavit Traefik tak, aby přistupoval na `localhost:8089`. Problém však byl, že Traefik v kontejneru nemůže přistupovat na `localhost:8089`, protože tam nic není – Traefik se musí dostat mimo kontejner.

V tomto případě se konfigurace odehrává pomocí labelů u kontejneru Traefik, protože se nebude přistupovat do jiného kontejneru.

Konfigurace Docker Compose

proxy:
  image: traefik
  restart: always
  container_name: traefik
  ports:
    - "443:443" # HTTPS
    - "80:80"
  extra_hosts:
    - "host.docker.internal:host-gateway"
  environment:
    - CLOUDFLARE_EMAIL=${CLOUDFLARE_EMAIL}
    - CLOUDFLARE_DNS_API_TOKEN=${CLOUDFLARE_DNS_API_TOKEN}
    - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
  volumes:
    - ${VOLUME_PATH}/traefik/acme:/etc/acme/
    - ${VOLUME_PATH}/traefik/.htpasswd:/auth/.htpasswd
    - /var/run/docker.sock:/var/run/docker.sock
    - ./traefik.yml:/etc/traefik/traefik.yml:ro
    - ./traefik-dynamic-config:/etc/traefik/dynamic-config:ro
  labels:
    traefik.enable: "true"
 
    # Přesměrování HTTP na HTTPS
    traefik.http.routers.solar-insecure.rule: "Host(`${FQDN_SOLAR}`)"
    traefik.http.routers.solar-insecure.entrypoints: "web"
    traefik.http.routers.solar-insecure.middlewares: "redirect"
 
    # Middleware pro přesměrování na HTTPS
    traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
    # HTTPS router
    traefik.http.routers.solar.rule: "Host(`${FQDN_SOLAR}`)"
    traefik.http.routers.solar.entrypoints: "websecure"
    traefik.http.routers.solar.tls.certresolver: "tmdnschallenge"
    traefik.http.routers.solar.service: "solar@file"

Dynamická konfigurace

Do `docker-compose.yml` jsem musel přidat odkaz na dynamické nastavení:

traefik.http.routers.solar.service: "solar@file"

Soubor `traefik.yml` jsem upravil takto:

providers:
  docker:
    exposedByDefault: false
  file:
    directory: "/etc/traefik/dynamic-config"
    watch: true  # Sleduje změny v souboru

Ve složce `traefik-dynamic-config` se nachází soubor `dynamic-config.yml`:

http:
  services:
    solar:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:8089"

Postup konfigurace

1. Musel jsem připravit dynamickou konfiguraci a přidat složku:

./traefik-dynamic-config:/etc/traefik/dynamic-config:ro

Docker neumí pracovat se změnami, pokud je definován pouze soubor – je třeba nasměrovat na celou složku.

2. Změna se projeví bez restartu Traefiku díky nastavení `watch: true`.

3. Původně jsem se snažil nadefinovat přesměrování přímo v `docker-compose.yml`, ale dle článku What does loadbalancer.server.url really mean je nutné využít dynamický konfigurační soubor.

4. Nasledováním rady z Traefik not reloading configuration when dynamic file was changed jsem pochopil, že musím použít celou složku pro dynamickou konfiguraci.

Dokud jsem nepřesunul nastavení do dynamického konfiguračního souboru, konfigurace nefungovala.

Shrnutí a závěrečná konfigurace

Traefik funguje jako reverse proxy, který směruje požadavky na služby podle pravidel definovaných v Docker label a konfiguračním souboru traefik.yml. Díky této integraci můžete snadno přidávat další služby (např. Nextcloud, Node-RED) bez nutnosti dalšího složitého nastavování.

Inspiroval jsem se v těchto zdrojích:

Protože je projekt stále živý a od napsání jsem stihl už udělat dost změn, přidávám aktuální soubory. Ve složce mám ještě soubor .env kde jsou proměnné s hesly a cestami, abych je nemusel psát do docker-compose.yml a mohl si je u jiného systému změnit.

Tady je docker-compose.yml:

version: "3.7"
 
 

services:


  proxy:
    image: traefik
    restart: always
    container_name: traefik
    ports:
      - "443:443" # HTTPS
      - "80:80"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - CLOUDFLARE_EMAIL=${CLOUDFLARE_EMAIL}
      - CLOUDFLARE_DNS_API_TOKEN=${CLOUDFLARE_DNS_API_TOKEN}
      - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL}
    volumes:
      - ${VOLUME_PATH}/traefik/acme:/etc/acme/
      - ${VOLUME_PATH}/traefik/.htpasswd:/auth/.htpasswd
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./traefik-dynamic-config:/etc/traefik/dynamic-config:ro
    labels:
      traefik.enable: "true"
 
      # Redirect HTTP to HTTPS
      traefik.http.routers.solar-insecure.rule: "Host(`${FQDN_SOLAR}`)"
      traefik.http.routers.solar-insecure.entrypoints: "web"
      traefik.http.routers.solar-insecure.middlewares: "redirect"
 
      # Middleware for HTTPS redirection
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS router
      traefik.http.routers.solar.rule: "Host(`${FQDN_SOLAR}`)"
      traefik.http.routers.solar.entrypoints: "websecure"
      traefik.http.routers.solar.tls.certresolver: "tmdnschallenge"
      traefik.http.routers.solar.service: "solar@file"
 
 

  postgres:
    container_name: postgres
    image: postgres:17
    restart: always
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - TZ=${DOCKER_TZ}
    volumes:
      - ${VOLUME_PATH}/postgres:/var/lib/postgresql/data
 

  php-apache:
    container_name: php-apache
    image: php:apache
    restart: always
    volumes:
      - ${VOLUME_PATH}/www:/var/www/html:ro 
    labels:
      traefik.enable: "true"
 
      # Redirect HTTP to HTTPS
      traefik.http.routers.smarthome-insecure.rule: "Host(`${FQDN_PHP_APACHE}`)"
      traefik.http.routers.smarthome-insecure.entrypoints: "web"
      traefik.http.routers.smarthome-insecure.middlewares: "redirect"
 
      # Middleware for HTTPS redirection
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS router
      traefik.http.routers.smarthome.rule: "Host(`${FQDN_PHP_APACHE}`)"
      traefik.http.routers.smarthome.entrypoints: "websecure"
      traefik.http.routers.smarthome.tls.certresolver: "tmdnschallenge"
      traefik.http.services.smarthome.loadbalancer.server.port: "80"
 

  mosquitto:
    container_name: mosquitto
    build:
      context: ${IOT_STACK}/.templates/mosquitto/.
      args:
      - MOSQUITTO_BASE=eclipse-mosquitto:latest
    restart: unless-stopped
    environment:
    - TZ=${DOCKER_TZ}
    ports:
    - "8883:8883"
    volumes:
    - ${VOLUME_PATH}/mosquitto/config:/mosquitto/config
    - ${VOLUME_PATH}/mosquitto/data:/mosquitto/data
    - ${VOLUME_PATH}/mosquitto/log:/mosquitto/log
    - ${VOLUME_PATH}/mosquitto/pwfile:/mosquitto/pwfile

  influxdb:
    container_name: influxdb
    image: "influxdb:1.8"
    restart: unless-stopped
    ports:
      - "127.0.0.1:8086:8086"
    environment:
    - TZ=${DOCKER_TZ}
    - INFLUXDB_HTTP_FLUX_ENABLED=false
    - INFLUXDB_REPORTING_DISABLED=false
    - INFLUXDB_HTTP_AUTH_ENABLED=false
    - INFLUXDB_MONITOR_STORE_ENABLED=FALSE
    - INFLUX_USERNAME=${INFLUX_USERNAME}
    - INFLUX_PASSWORD=${INFLUX_PASSWORD}
    volumes:
    - ${VOLUME_PATH}/influxdb/data:/var/lib/influxdb
    - ${VOLUME_PATH}/backups/influxdb/db:/var/lib/influxdb/backup
    healthcheck:
      test: ["CMD", "curl", "http://localhost:8086"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
 

  nodered:
    container_name: nodered
    build:
      context: ${IOT_STACK}/services/nodered/.
      args:
      - DOCKERHUB_TAG=latest
      - EXTRA_PACKAGES=ffmpeg
    restart: unless-stopped
    user: "0"
    environment:
      - TZ=${DOCKER_TZ}
    volumes:
      - ${VOLUME_PATH}/nodered/data:/data
      - ${VOLUME_PATH}/nodered/ssh:/root/.ssh
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
    devices:
      - "/dev/ttyAMA0:/dev/ttyAMA0"
      - "/dev/vcio:/dev/vcio"
      - "/dev/gpiomem:/dev/gpiomem"
    labels:
      traefik.enable: "true"
 
      # Přesměrování HTTP na HTTPS
      traefik.http.routers.nodered-insecure.rule: "Host(`${FQDN_NODERED}`)"
      traefik.http.routers.nodered-insecure.entrypoints: "web"
      traefik.http.routers.nodered-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS konfigurace
      traefik.http.routers.nodered.rule: "Host(`${FQDN_NODERED}`)"
      traefik.http.routers.nodered.entrypoints: "websecure"
      traefik.http.routers.nodered.tls.certresolver: "tmdnschallenge"
      traefik.http.services.nodered.loadbalancer.server.port: "1880"

  grafana:
    container_name: grafana
    image: grafana/grafana
    restart: unless-stopped
    user: "0"
    environment:
      - TZ=${DOCKER_TZ}
      - GF_PATHS_DATA=/var/lib/grafana
      - GF_PATHS_LOGS=/var/log/grafana
      - GF_SECURITY_ALLOW_EMBEDDING=true
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_NAME=Hajany
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
      - GF_SECURITY_HIDE_VERSION=true
    volumes:
      - ${VOLUME_PATH}/grafana/data:/var/lib/grafana
      - ${VOLUME_PATH}/grafana/log:/var/log/grafana
    healthcheck:
      test: ["CMD", "wget", "-O", "/dev/null", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    labels:
      traefik.enable: "true"
 
      # Redirect HTTP to HTTPS
      traefik.http.routers.grafana-insecure.rule: "Host(`${FQDN_GRAFANA}`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"
 
      # Middleware for HTTPS redirection
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS router
      traefik.http.routers.grafana.rule: "Host(`${FQDN_GRAFANA}`)"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.routers.grafana.tls.certresolver: "tmdnschallenge"
 
      # Define service port
      traefik.http.services.grafana.loadbalancer.server.port: "3000"
 

  teslamate:
    container_name: teslamate
    image: teslamate/teslamate:latest
    restart: always
    environment:
      - ENCRYPTION_KEY=${TM_ENCRYPTION_KEY}
      - DATABASE_USER=${TM_DB_USER}
      - DATABASE_PASS=${TM_DB_PASSWORD}
      - DATABASE_NAME=${TM_DB}
      - DATABASE_HOST=postgres
      - VIRTUAL_HOST=${FQDN_TESLAMATE}
        #- MQTT_HOST=mosquitto
      - MQTT_HOST=${MAJORDOMUS_MQTT_HOST}
      - MQTT_PORT=${MAJORDOMUS_MQTT_PORT}
      - MQTT_USERNAME=${MAJORDOMUS_MQTT_USERNAME}
      - MQTT_PASSWORD=${MAJORDOMUS_MQTT_PASSWORD}
      - MQTT_TLS=true
      - MQTT_TLS_ACCEPT_INVALID_CERTS=true
      - TZ=${DOCKER_TZ}
    #volumes:
    #  - ./import:/opt/app/import
    labels:
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.teslamate-insecure.rule: "Host(`${FQDN_TESLAMATE}`)"
      traefik.http.routers.teslamate-insecure.entrypoints: "web"
      traefik.http.routers.teslamate-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.teslamate-ws.rule: "Host(`${FQDN_TESLAMATE}`) && Path(`/live/websocket`)"
      traefik.http.routers.teslamate-ws.entrypoints: "websecure"
      traefik.http.routers.teslamate-ws.tls: ""

      traefik.http.routers.teslamate.middlewares: "teslamate-auth"
      traefik.http.routers.teslamate.rule: "Host(`${FQDN_TESLAMATE}`)"
      traefik.http.routers.teslamate.entrypoints: "websecure"
      traefik.http.routers.teslamate.tls.certresolver: "tmdnschallenge"
      traefik.http.services.teslamate.loadbalancer.server.port: "4000"
 

      traefik.http.middlewares.teslamate-auth.basicauth.realm: "teslamate"
      traefik.http.middlewares.teslamate-auth.basicauth.usersfile: "/auth/.htpasswd"

    cap_drop:
      - all
 

  grafana-teslamate:
    container_name: grafana-teslamate
    image: teslamate/grafana:latest
    restart: always
    environment:
      - DATABASE_USER=${TM_DB_USER}
      - DATABASE_PASS=${TM_DB_PASSWORD}
      - DATABASE_NAME=${TM_DB}
      - DATABASE_HOST=postgres
      - TZ=${DOCKER_TZ}
      - GF_SERVER_DOMAIN=${FQDN_TESLAMATE}
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
    labels:
      # aktivace Traefiku pro tento kontejner
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.grafana-teslamate-insecure.rule: "Host(`${FQDN_TESLAMATE}`) && (Path(`/grafana`) || PathPrefix(`/grafana/`))"
      traefik.http.routers.grafana-teslamate-insecure.entrypoints: "web"
      traefik.http.routers.grafana-teslamate-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.grafana-teslamate.rule: "Host(`${FQDN_TESLAMATE}`) && (Path(`/grafana`) || PathPrefix(`/grafana/`))"
      traefik.http.routers.grafana-teslamate.entrypoints: "websecure"
      traefik.http.routers.grafana-teslamate.tls.certresolver: "tmdnschallenge"
      traefik.http.services.grafana-teslamate.loadbalancer.server.port: "3000"
    volumes:
      - grafana-teslamate:/var/lib/grafana
 

  redis:
    container_name: redis
    image: redis:alpine
    restart: always
    command: ["redis-server", "--requirepass", "${REDIS_HOST_PASSWORD}"] #replace with your secure password
    volumes:
      - redis-data:/data
 

  nextcloud:
    container_name: nextcloud
    image: nextcloud:apache
    build:
      context: ./Dockerfiles/nextcloud/.
    restart: "no"
    extra_hosts:
      - "${FQDN_NEXTCLOUD}:${NEXTCLOUD_EXTRA_HOST}"
    environment:
      - POSTGRES_DB=${NEXTCLOUD_DB}
      - POSTGRES_USER=${NEXTCLOUD_DB_USER}
      - POSTGRES_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
      - POSTGRES_HOST=postgres
      - TRUSTED_PROXIES=${TRUSTED_PROXIES}
      - OVERWRITEHOST=${FQDN_NEXTCLOUD}
      - OVERWRITEPROTOCOL=https
      - APACHE_DISABLE_REWRITE_IP=1
      - OVERWRITECLIURL=https://${FQDN_NEXTCLOUD}
      - REDIS_HOST=redis
      - REDIS_HOST_PASSWORD=${REDIS_HOST_PASSWORD}
      - PHP_MEMORY_LIMIT=1024M
    volumes:
      - ${NEXTCLOUD_VOLUME_PATH}:/var/www/html
      - type: tmpfs
        target: /tmp
        tmpfs:
          mode: "1777" # Volitelně: nastavení oprávnění
    labels:
      traefik.enable: "true"
 
      # Redirect HTTP to HTTPS
      traefik.http.routers.nextcloud-insecure.rule: "Host(`${FQDN_NEXTCLOUD}`)"
      traefik.http.routers.nextcloud-insecure.entrypoints: "web"
      traefik.http.routers.nextcloud-insecure.middlewares: "redirect"
 
      # Middleware for HTTPS redirection
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # HTTPS router
      traefik.http.routers.nextcloud.rule: "Host(`${FQDN_NEXTCLOUD}`)"
      traefik.http.routers.nextcloud.entrypoints: "websecure"
      traefik.http.routers.nextcloud.tls.certresolver: "tmdnschallenge"
      traefik.http.services.nextcloud.loadbalancer.server.port: "80"
 
      # Middleware for CalDAV/CardDAV redirects
      traefik.http.middlewares.nextcloud_redirectregex.redirectregex.permanent: "true"
      traefik.http.middlewares.nextcloud_redirectregex.redirectregex.regex: "https://(.*)/.well-known/(?:card|cal)dav"
      traefik.http.middlewares.nextcloud_redirectregex.redirectregex.replacement: "https://$${1}/remote.php/dav"
 
      # Add HSTS header
      traefik.http.middlewares.nextcloud-hsts.headers.stsSeconds: "15552000"
      traefik.http.middlewares.nextcloud-hsts.headers.stsIncludeSubdomains: "true"
      traefik.http.middlewares.nextcloud-hsts.headers.stsPreload: "true"

      traefik.http.routers.nextcloud.middlewares: "nextcloud-hsts"

    depends_on:
      - postgres
      - redis
 

  gitea:
    image: gitea-arm32
    container_name: gitea
    restart: "no"
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=postgres
      - GITEA__database__NAME=${GITEA_DB}
      - GITEA__database__USER=${GITEA_DB_USER}
      - GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
    restart: always
    volumes:
      - ${GITEA_VOLUME_PATH}:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    #ports:
      #- "3000:3000"
      #- "2222:22"
    depends_on:
      - postgres
    labels:
      # Aktivace Traefiku pro tuto službu
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.gitea-insecure.rule: "Host(`${FQDN_GITEA}`)"
      traefik.http.routers.gitea-insecure.entrypoints: "web"
      traefik.http.routers.gitea-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # Router pro HTTPS připojení
      traefik.http.routers.gitea.rule: "Host(`${FQDN_GITEA}`)"
      traefik.http.routers.gitea.entrypoints: "websecure"
      traefik.http.routers.gitea.tls.certresolver: "tmdnschallenge"
      traefik.http.services.gitea.loadbalancer.server.port: "3000"
 
        #traefik.http.middlewares.upload-limit.buffering.maxRequestBodyBytes: "0"
        #traefik.http.middlewares.upload-limit.buffering.maxResponseBodyBytes: "0"
        #traefik.http.middlewares.upload-limit.buffering.memRequestBodyBytes: "0"
        #traefik.http.middlewares.upload-limit.buffering.memResponseBodyBytes: "0"
        #traefik.http.middlewares.upload-limit.buffering.retryExpression: "IsNetworkError()"
        #traefik.http.routers.gitea.middlewares: "upload-limit"
 
 

volumes:
  grafana-teslamate:
  redis-data:
  

Konfigurace traefik.yml:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: tmdnschallenge # Přidání certifikátového resolveru pro HTTPS
        options: maxtls12 # Vynucení TLS 1.2
    transport:
      respondingTimeouts:
        readTimeout: 30m
 

certificatesResolvers:
  tmdnschallenge:
    acme:
      email: "mujemail@example.com"  # Email pro komunikaci s Let's Encrypt
      storage: "/etc/acme/acme.json"  # Úložiště certifikátů
      dnsChallenge:
        provider: cloudflare  # Použití Cloudflare API
        delayBeforeCheck: 10  # Volitelná hodnota pro zpoždění ověření

providers:
  docker:
    exposedByDefault: false
  file:
    directory: "/etc/traefik/dynamic-config"
    watch: true  # Sleduje změny v souboru

global:
  sendAnonymousUsage: false

tls:
  options:
    maxtls12:
      minVersion: VersionTLS12
      maxVersion: VersionTLS12
      sniStrict: true           # Zajištění přísného ověřování SNI

Konfigurace dynamic-config.yml:

http:
  services:
    solar:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:8088"