From 8afa5d60f522a62c4d3ecc76a7d1da7634cdb840 Mon Sep 17 00:00:00 2001 From: Rocket-Quack <202538874+Rocket-Quack@users.noreply.github.com> Date: Fri, 16 Jan 2026 12:52:20 +0100 Subject: [PATCH 1/9] chore(traefik): migrate compose overrides to v3 traefik --- overrides/compose.custom-domain.yaml | 3 +- overrides/compose.https.yaml | 57 ++++++++-------- overrides/compose.multi-bench-ssl.yaml | 5 +- overrides/compose.multi-bench.yaml | 3 +- overrides/compose.proxy.yaml | 33 +++++----- overrides/compose.traefik-ssl.yaml | 91 +++++++++++++------------- overrides/compose.traefik.yaml | 81 ++++++++++++----------- 7 files changed, 140 insertions(+), 133 deletions(-) diff --git a/overrides/compose.custom-domain.yaml b/overrides/compose.custom-domain.yaml index e7c82f90..7ca31776 100644 --- a/overrides/compose.custom-domain.yaml +++ b/overrides/compose.custom-domain.yaml @@ -14,7 +14,8 @@ services: - traefik.http.services.${ROUTER?ROUTER not set}.loadbalancer.server.port=2016 - traefik.http.routers.${ROUTER}.service=${ROUTER} - traefik.http.routers.${ROUTER}.entrypoints=http - - traefik.http.routers.${ROUTER}.rule=Host(${SITES?SITES not set}) + - traefik.http.routers.${ROUTER}.ruleSyntax=v3 + - traefik.http.routers.${ROUTER}.rule=${SITES_RULE?SITES_RULE not set} - traefik.http.middlewares.${ROUTER}.headers.customrequestheaders.Host=${BASE_SITE?BASE_SITE not set} - traefik.http.routers.${ROUTER}.middlewares=${ROUTER} networks: diff --git a/overrides/compose.https.yaml b/overrides/compose.https.yaml index 9096e560..55b453e8 100644 --- a/overrides/compose.https.yaml +++ b/overrides/compose.https.yaml @@ -1,32 +1,33 @@ -services: - frontend: - labels: +services: + frontend: + labels: - traefik.enable=true - traefik.http.services.frontend.loadbalancer.server.port=8080 - traefik.http.routers.frontend-http.entrypoints=websecure - traefik.http.routers.frontend-http.tls.certresolver=main-resolver - - traefik.http.routers.frontend-http.rule=Host(${SITES:?List of sites not set}) - - proxy: - image: traefik:v2.11 - restart: unless-stopped - command: - - --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 - - --certificatesResolvers.main-resolver.acme.httpChallenge=true - - --certificatesResolvers.main-resolver.acme.httpChallenge.entrypoint=web - - --certificatesResolvers.main-resolver.acme.email=${LETSENCRYPT_EMAIL:?No Let's Encrypt email set} - - --certificatesResolvers.main-resolver.acme.storage=/letsencrypt/acme.json - ports: - - ${HTTP_PUBLISH_PORT:-80}:80 - - ${HTTPS_PUBLISH_PORT:-443}:443 - volumes: - - cert-data:/letsencrypt - - /var/run/docker.sock:/var/run/docker.sock:ro - -volumes: - cert-data: + - traefik.http.routers.frontend-http.ruleSyntax=v3 + - traefik.http.routers.frontend-http.rule=${SITES_RULE:?SITES_RULE not set} + + proxy: + image: traefik:v3.6 + restart: unless-stopped + command: + - --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 + - --certificatesResolvers.main-resolver.acme.httpChallenge=true + - --certificatesResolvers.main-resolver.acme.httpChallenge.entrypoint=web + - --certificatesResolvers.main-resolver.acme.email=${LETSENCRYPT_EMAIL:?No Let's Encrypt email set} + - --certificatesResolvers.main-resolver.acme.storage=/letsencrypt/acme.json + ports: + - ${HTTP_PUBLISH_PORT:-80}:80 + - ${HTTPS_PUBLISH_PORT:-443}:443 + volumes: + - cert-data:/letsencrypt + - /var/run/docker.sock:/var/run/docker.sock:ro + +volumes: + cert-data: diff --git a/overrides/compose.multi-bench-ssl.yaml b/overrides/compose.multi-bench-ssl.yaml index 158d22bd..e38da19b 100644 --- a/overrides/compose.multi-bench-ssl.yaml +++ b/overrides/compose.multi-bench-ssl.yaml @@ -4,8 +4,9 @@ services: # ${ROUTER}-http to use the middleware to redirect to https - traefik.http.routers.${ROUTER}-http.middlewares=https-redirect # ${ROUTER}-https the actual router using HTTPS - # Uses the environment variable SITES - - traefik.http.routers.${ROUTER}-https.rule=Host(${SITES?SITES not set}) + # Uses the environment variable SITES_RULE + - traefik.http.routers.${ROUTER}-https.ruleSyntax=v3 + - traefik.http.routers.${ROUTER}-https.rule=${SITES_RULE?SITES_RULE not set} - traefik.http.routers.${ROUTER}-https.entrypoints=https - traefik.http.routers.${ROUTER}-https.tls=true # Use the service ${ROUTER} with the frontend diff --git a/overrides/compose.multi-bench.yaml b/overrides/compose.multi-bench.yaml index 7e681a18..542e7716 100644 --- a/overrides/compose.multi-bench.yaml +++ b/overrides/compose.multi-bench.yaml @@ -9,7 +9,8 @@ services: - traefik.http.services.${ROUTER?ROUTER not set}.loadbalancer.server.port=8080 - traefik.http.routers.${ROUTER}-http.service=${ROUTER} - traefik.http.routers.${ROUTER}-http.entrypoints=http - - traefik.http.routers.${ROUTER}-http.rule=Host(${SITES?SITES not set}) + - traefik.http.routers.${ROUTER}-http.ruleSyntax=v3 + - traefik.http.routers.${ROUTER}-http.rule=${SITES_RULE?SITES_RULE not set} configurator: networks: - bench-network diff --git a/overrides/compose.proxy.yaml b/overrides/compose.proxy.yaml index 32ce9fab..b3c467c7 100644 --- a/overrides/compose.proxy.yaml +++ b/overrides/compose.proxy.yaml @@ -1,19 +1,20 @@ -services: - frontend: - labels: +services: + frontend: + labels: - traefik.enable=true - traefik.http.services.frontend.loadbalancer.server.port=8080 - traefik.http.routers.frontend-http.entrypoints=web - - traefik.http.routers.frontend-http.rule=HostRegexp(`{any:.+}`) - - proxy: - image: traefik:v2.11 - command: - - --providers.docker - - --providers.docker.exposedbydefault=false - - --entrypoints.web.address=:80 - ports: - - ${HTTP_PUBLISH_PORT:-80}:80 - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - userns_mode: host + - traefik.http.routers.frontend-http.ruleSyntax=v3 + - traefik.http.routers.frontend-http.rule=HostRegexp(`^.+$`) + + proxy: + image: traefik:v3.6 + command: + - --providers.docker + - --providers.docker.exposedbydefault=false + - --entrypoints.web.address=:80 + ports: + - ${HTTP_PUBLISH_PORT:-80}:80 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + userns_mode: host diff --git a/overrides/compose.traefik-ssl.yaml b/overrides/compose.traefik-ssl.yaml index b83cb8e4..19083bf2 100644 --- a/overrides/compose.traefik-ssl.yaml +++ b/overrides/compose.traefik-ssl.yaml @@ -1,48 +1,49 @@ -services: - traefik: - labels: - # https-redirect middleware to redirect HTTP to HTTPS - # It can be reused by other stacks in other Docker Compose files - - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https - - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true - # traefik-http to use the middleware to redirect to https - - traefik.http.routers.traefik-public-http.middlewares=https-redirect +services: + traefik: + labels: + # https-redirect middleware to redirect HTTP to HTTPS + # It can be reused by other stacks in other Docker Compose files + - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true + # traefik-http to use the middleware to redirect to https + - traefik.http.routers.traefik-public-http.middlewares=https-redirect # traefik-https the actual router using HTTPS # Uses the environment variable DOMAIN + - traefik.http.routers.traefik-public-https.ruleSyntax=v3 - traefik.http.routers.traefik-public-https.rule=Host(`${TRAEFIK_DOMAIN}`) - - traefik.http.routers.traefik-public-https.entrypoints=https - - traefik.http.routers.traefik-public-https.tls=true - # Use the special Traefik service api@internal with the web UI/Dashboard - - traefik.http.routers.traefik-public-https.service=api@internal - # Use the "le" (Let's Encrypt) resolver created below - - traefik.http.routers.traefik-public-https.tls.certresolver=le - # Enable HTTP Basic auth, using the middleware created above - - traefik.http.routers.traefik-public-https.middlewares=admin-auth - command: - # Enable Docker in Traefik, so that it reads labels from Docker services - - --providers.docker=true - # Do not expose all Docker services, only the ones explicitly exposed - - --providers.docker.exposedbydefault=false - # Create an entrypoint http listening on port 80 - - --entrypoints.http.address=:80 - # Create an entrypoint https listening on port 443 - - --entrypoints.https.address=:443 - # Create the certificate resolver le for Let's Encrypt, uses the environment variable EMAIL - - --certificatesresolvers.le.acme.email=${EMAIL:?No EMAIL set} - # Store the Let's Encrypt certificates in the mounted volume - - --certificatesresolvers.le.acme.storage=/certificates/acme.json - # Use the TLS Challenge for Let's Encrypt - - --certificatesresolvers.le.acme.tlschallenge=true - # Enable the access log, with HTTP requests - - --accesslog - # Enable the Traefik log, for configurations and errors - - --log - # Enable the Dashboard and API - - --api - ports: - - ${HTTPS_PUBLISH_PORT:-443}:443 - volumes: - - cert-data:/certificates - -volumes: - cert-data: + - traefik.http.routers.traefik-public-https.entrypoints=https + - traefik.http.routers.traefik-public-https.tls=true + # Use the special Traefik service api@internal with the web UI/Dashboard + - traefik.http.routers.traefik-public-https.service=api@internal + # Use the "le" (Let's Encrypt) resolver created below + - traefik.http.routers.traefik-public-https.tls.certresolver=le + # Enable HTTP Basic auth, using the middleware created above + - traefik.http.routers.traefik-public-https.middlewares=admin-auth + command: + # Enable Docker in Traefik, so that it reads labels from Docker services + - --providers.docker=true + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.docker.exposedbydefault=false + # Create an entrypoint http listening on port 80 + - --entrypoints.http.address=:80 + # Create an entrypoint https listening on port 443 + - --entrypoints.https.address=:443 + # Create the certificate resolver le for Let's Encrypt, uses the environment variable EMAIL + - --certificatesresolvers.le.acme.email=${EMAIL:?No EMAIL set} + # Store the Let's Encrypt certificates in the mounted volume + - --certificatesresolvers.le.acme.storage=/certificates/acme.json + # Use the TLS Challenge for Let's Encrypt + - --certificatesresolvers.le.acme.tlschallenge=true + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + # Enable the Dashboard and API + - --api + ports: + - ${HTTPS_PUBLISH_PORT:-443}:443 + volumes: + - cert-data:/certificates + +volumes: + cert-data: diff --git a/overrides/compose.traefik.yaml b/overrides/compose.traefik.yaml index 7765e19d..01ad3c5f 100644 --- a/overrides/compose.traefik.yaml +++ b/overrides/compose.traefik.yaml @@ -1,45 +1,46 @@ -services: - traefik: - image: "traefik:v2.11" - restart: unless-stopped - labels: - # Enable Traefik for this service, to make it available in the public network - - traefik.enable=true - # Use the traefik-public network (declared below) - - traefik.docker.network=traefik-public +services: + traefik: + image: "traefik:v3.6" + restart: unless-stopped + labels: + # Enable Traefik for this service, to make it available in the public network + - traefik.enable=true + # Use the traefik-public network (declared below) + - traefik.docker.network=traefik-public # admin-auth middleware with HTTP Basic auth # Using the environment variables USERNAME and HASHED_PASSWORD - traefik.http.middlewares.admin-auth.basicauth.users=admin:${HASHED_PASSWORD:?No HASHED_PASSWORD set} # Uses the environment variable TRAEFIK_DOMAIN + - traefik.http.routers.traefik-public-http.ruleSyntax=v3 - traefik.http.routers.traefik-public-http.rule=Host(`${TRAEFIK_DOMAIN:?No TRAEFIK_DOMAIN set}`) - - traefik.http.routers.traefik-public-http.entrypoints=http - # Use the special Traefik service api@internal with the web UI/Dashboard - - traefik.http.routers.traefik-public-http.service=api@internal - # Enable HTTP Basic auth, using the middleware created above - - traefik.http.routers.traefik-public-http.middlewares=admin-auth - # Define the port inside of the Docker service to use - - traefik.http.services.traefik-public.loadbalancer.server.port=8080 - command: - # Enable Docker in Traefik, so that it reads labels from Docker services - - --providers.docker=true - # Do not expose all Docker services, only the ones explicitly exposed - - --providers.docker.exposedbydefault=false - # Create an entrypoint http listening on port 80 - - --entrypoints.http.address=:80 - # Enable the access log, with HTTP requests - - --accesslog - # Enable the Traefik log, for configurations and errors - - --log - # Enable the Dashboard and API - - --api - ports: - - ${HTTP_PUBLISH_PORT:-80}:80 - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - networks: - - traefik-public - -networks: - traefik-public: - name: traefik-public - external: false + - traefik.http.routers.traefik-public-http.entrypoints=http + # Use the special Traefik service api@internal with the web UI/Dashboard + - traefik.http.routers.traefik-public-http.service=api@internal + # Enable HTTP Basic auth, using the middleware created above + - traefik.http.routers.traefik-public-http.middlewares=admin-auth + # Define the port inside of the Docker service to use + - traefik.http.services.traefik-public.loadbalancer.server.port=8080 + command: + # Enable Docker in Traefik, so that it reads labels from Docker services + - --providers.docker=true + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.docker.exposedbydefault=false + # Create an entrypoint http listening on port 80 + - --entrypoints.http.address=:80 + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + # Enable the Dashboard and API + - --api + ports: + - ${HTTP_PUBLISH_PORT:-80}:80 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - traefik-public + +networks: + traefik-public: + name: traefik-public + external: false From 108fb0e6af7ce572c29e054a5f2ff85e267ef954 Mon Sep 17 00:00:00 2001 From: Rocket-Quack <202538874+Rocket-Quack@users.noreply.github.com> Date: Fri, 16 Jan 2026 12:53:48 +0100 Subject: [PATCH 2/9] docs(env): switch SITES to SITES_RULE for v3 traefik --- example.env | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/example.env b/example.env index 5d58a451..a1d0f520 100644 --- a/example.env +++ b/example.env @@ -49,7 +49,8 @@ PROXY_READ_TIMEOUT= # Necessary if the upload limit in the frappe application is increased CLIENT_MAX_BODY_SIZE= -# List of sites for letsencrypt certificates quoted with backtick (`) and separated by comma (,) +# Single site: SITES_RULE=Host(`erp.example.com`) +# Multiple sites: SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`) # More https://doc.traefik.io/traefik/routing/routers/#rule # About acme https://doc.traefik.io/traefik/https/acme/#domain-definition -SITES=`erp.example.com` +SITES_RULE=Host(`erp.example.com`) From 9a4a078bdeec08c78545b423bd3e971e41f3cf28 Mon Sep 17 00:00:00 2001 From: Rocket-Quack <202538874+Rocket-Quack@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:04:34 +0100 Subject: [PATCH 3/9] docs: update Traefik v3 SITES_RULE usage and examples --- docs/02-setup/04-env-variables.md | 10 +++++----- docs/02-setup/05-overrides.md | 2 +- docs/02-setup/06-setup-examples.md | 4 ++-- docs/02-setup/07-single-server-example.md | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/02-setup/04-env-variables.md b/docs/02-setup/04-env-variables.md index 1001c888..c566ecd9 100644 --- a/docs/02-setup/04-env-variables.md +++ b/docs/02-setup/04-env-variables.md @@ -48,16 +48,16 @@ Then edit `.env` and set variables according to your needs. | Variable | Purpose | Default | When to Set | | ------------------- | ------------------------------------------------ | ------- | ---------------------------------------- | | `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | — | Required if using HTTPS override | -| `SITES` | List of domains for SSL certificates | — | Required if using reverse proxy override | +| `SITES_RULE` | List of domains for SSL (Traefik rule for TLS domain routing) | — | Required if using reverse proxy override | -**Format for `SITES`:** +**Format for `SITES_RULE`:** ```bash # Single site -SITES=`mysite.example.com` +SITES_RULE=Host(`mysite.example.com`) -# Wildcard (any subdomain) -SITES=`{any:.+}` +# Multiple sites +SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`) ``` --- diff --git a/docs/02-setup/05-overrides.md b/docs/02-setup/05-overrides.md index 9188489d..156a6834 100644 --- a/docs/02-setup/05-overrides.md +++ b/docs/02-setup/05-overrides.md @@ -14,7 +14,7 @@ docker compose -f compose.yaml -f overrides/compose.mariadb.yaml -f overrides/co | **Proxy** | | | | compose.noproxy.yaml | Exposes the application directly on port `:8080` without a reverse proxy | | | compose.proxy.yaml | Uses Traefik as HTTP reverse proxy on port `:80` | You can change the published port by setting `HTTP_PUBLISH_PORT` | -| compose.https.yaml | Uses Traefik as HTTPS reverse proxy on Port `:443` with automatic HTTP-to-HTTPS redirect | `SITES` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. | +| compose.https.yaml | Uses Traefik as HTTPS reverse proxy on Port `:443` with automatic HTTP-to-HTTPS redirect | `SITES_RULE` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. | | **Redis** | | | | compose.redis.yaml | Adds Redis service for caching and background job queuing | | **TBD** | **The following overrides are available but lack documentation. If you use them and understand their purpose, please consider contributing to this documentation.** | diff --git a/docs/02-setup/06-setup-examples.md b/docs/02-setup/06-setup-examples.md index becf9442..5d9b569a 100644 --- a/docs/02-setup/06-setup-examples.md +++ b/docs/02-setup/06-setup-examples.md @@ -78,7 +78,7 @@ Setup Frappe/ERPNext using containerized MariaDB and Redis with Let's Encrypt SS **Requirements:** -- Set `LETSENCRYPT_EMAIL` and `SITES` environment variables +- Set `LETSENCRYPT_EMAIL` and `SITES_RULE` environment variables - DNS must point to your server IP ```sh @@ -93,7 +93,7 @@ docker compose -f compose.yaml \ docker compose --project-name -f ~/gitops/docker-compose.yml up -d ``` -> **Note:** Ensure your `SITES` variable is properly formatted. See [environment variables](04-env-variables.md) for the correct format. +> **Note:** Ensure your `SITES_RULE` variable is properly formatted. See [environment variables](04-env-variables.md) for the correct format. ## Create First Site diff --git a/docs/02-setup/07-single-server-example.md b/docs/02-setup/07-single-server-example.md index d4a459c9..6827dda6 100644 --- a/docs/02-setup/07-single-server-example.md +++ b/docs/02-setup/07-single-server-example.md @@ -142,7 +142,7 @@ cp example.env ~/gitops/erpnext-one.env sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext-one.env sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-one.env sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-one.env -sed -i 's/SITES=`erp.example.com`/SITES=\`one.example.com\`,\`two.example.com\`/g' ~/gitops/erpnext-one.env +sed -i 's/SITES_RULE=Host(`erp.example.com`)/SITES_RULE=Host(`one.example.com`) || Host(`two.example.com`)/g' ~/gitops/erpnext-one.env echo 'ROUTER=erpnext-one' >> ~/gitops/erpnext-one.env echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/erpnext-one.env ``` @@ -204,7 +204,7 @@ sed -i 's/DB_PASSWORD=123/DB_PASSWORD=changeit/g' ~/gitops/erpnext-two.env sed -i 's/DB_HOST=/DB_HOST=mariadb-database/g' ~/gitops/erpnext-two.env sed -i 's/DB_PORT=/DB_PORT=3306/g' ~/gitops/erpnext-two.env echo "ROUTER=erpnext-two" >> ~/gitops/erpnext-two.env -echo "SITES=\`three.example.com\`,\`four.example.com\`" >> ~/gitops/erpnext-two.env +echo 'SITES_RULE=Host(`three.example.com`) || Host(`four.example.com`)' >> ~/gitops/erpnext-two.env echo "BENCH_NETWORK=erpnext-two" >> ~/gitops/erpnext-two.env ``` @@ -253,7 +253,7 @@ Create environment file ```shell echo "ROUTER=custom-one-example" > ~/gitops/custom-one-example.env -echo "SITES=\`custom-one.example.com\`" >> ~/gitops/custom-one-example.env +echo 'SITES_RULE=Host(`custom-one.example.com`)' >> ~/gitops/custom-one-example.env echo "BASE_SITE=one.example.com" >> ~/gitops/custom-one-example.env echo "BENCH_NETWORK=erpnext-one" >> ~/gitops/custom-one-example.env ``` @@ -262,7 +262,7 @@ Note: - Change the file name from `custom-one-example.env` to a logical one. - Change `ROUTER` variable from `custom-one.example.com` to the one being added. -- Change `SITES` variable from `custom-one.example.com` to the one being added. You can add multiple sites quoted in backtick (`) and separated by commas. +- Change `SITES_RULE` variable to the one being added. You can add multiple sites with `Host(...) || Host(...)`. - Change `BASE_SITE` variable from `one.example.com` to the one which is being pointed to. - Change `BENCH_NETWORK` variable from `erpnext-one` to the one which was created with the bench. From c5525bf021446a843eb38f6ec3260055172d278b Mon Sep 17 00:00:00 2001 From: Rocket-Quack <202538874+Rocket-Quack@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:27:10 +0100 Subject: [PATCH 4/9] docs: add Traefik v2 to v3 migration guide --- docs/06-migration/02-traefik-v3-migration.md | 70 ++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/06-migration/02-traefik-v3-migration.md diff --git a/docs/06-migration/02-traefik-v3-migration.md b/docs/06-migration/02-traefik-v3-migration.md new file mode 100644 index 00000000..9e4ed21f --- /dev/null +++ b/docs/06-migration/02-traefik-v3-migration.md @@ -0,0 +1,70 @@ +# Migrate an existing Traefik v2 instance to v3 + +Use this guide if you already run Traefik v2 with `frappe_docker` and want to upgrade to v3. It focuses on the image upgrade and the v3 routing rule changes that affect existing setups. + +### Before you start +Before migrating anything, it is always recommended to create a backup. Better safe than sorry. In particular, compose and .env should be backed up. + +### Quick upgrade summary + +1) Pull the updated repo +2) Update env variables especially the updated `SITES` to `SITES_RULE` +3) Regenerate the compose config and restart the stack + +#### Multiple hostnames + +v2 allowed comma-separated host lists inside `Host(...)`. In v3 traefik uses logical OR + +**Before (v2):** +``` +Host(`a.example.com`,`b.example.com`) +``` + +**After (v3):** +``` +Host(`a.example.com`) || Host(`b.example.com`) +``` + +### Step 1: Replace `SITES` with `SITES_RULE` + +All Traefik routing for HTTPS and multi-bench setups now uses `SITES_RULE`, which is a full v3 rule expression + +**Single site:** +``` +SITES_RULE=Host(`erp.example.com`) +``` + +**Multiple sites:** +``` +SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`) +``` + +### Step 2: Regenerate and start your compose config + +Example for HTTPS: + +```sh +docker compose --env-file .env \ + -f compose.yaml \ + -f overrides/compose.mariadb.yaml \ + -f overrides/compose.redis.yaml \ + -f overrides/compose.https.yaml \ + config > ~/gitops/docker-compose.yml +``` +```sh +docker compose --project-name -f ~/gitops/docker-compose.yml up -d +``` + +See [Single Server Example](../02-setup/07-single-server-example.md) + +### Step 3: Verify Traefik + +After restarting, Traefik will be used in the new supported version 3.6 and the same URLs will be used for the instances when making adjustments. After that, the pages should be accessible as before via the proxy and, if using HTTPS, via HTTPS. + +### Rollback + +If you need to rollback: + +1) Revert Traefik image to `v2.11` +2) Restore the old `SITES` variable format and v2 rules +3) Regenerate the compose config and restart From 80a11fb47f1da37bbe2b9ddd156ae632792dda83 Mon Sep 17 00:00:00 2001 From: RocketQuack <202538874+Rocket-Quack@users.noreply.github.com> Date: Mon, 19 Jan 2026 19:31:55 +0100 Subject: [PATCH 5/9] chore: run pre-commit fixes --- docs/02-setup/04-env-variables.md | 8 +- docs/02-setup/05-overrides.md | 36 ++++---- docs/06-migration/02-traefik-v3-migration.md | 18 ++-- overrides/compose.https.yaml | 54 ++++++------ overrides/compose.proxy.yaml | 30 +++---- overrides/compose.traefik-ssl.yaml | 90 ++++++++++---------- overrides/compose.traefik.yaml | 80 ++++++++--------- 7 files changed, 161 insertions(+), 155 deletions(-) diff --git a/docs/02-setup/04-env-variables.md b/docs/02-setup/04-env-variables.md index c566ecd9..22a25790 100644 --- a/docs/02-setup/04-env-variables.md +++ b/docs/02-setup/04-env-variables.md @@ -45,10 +45,10 @@ Then edit `.env` and set variables according to your needs. ## HTTPS & SSL Configuration -| Variable | Purpose | Default | When to Set | -| ------------------- | ------------------------------------------------ | ------- | ---------------------------------------- | -| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | — | Required if using HTTPS override | -| `SITES_RULE` | List of domains for SSL (Traefik rule for TLS domain routing) | — | Required if using reverse proxy override | +| Variable | Purpose | Default | When to Set | +| ------------------- | ------------------------------------------------------------- | ------- | ---------------------------------------- | +| `LETSENCRYPT_EMAIL` | Email for Let's Encrypt certificate registration | — | Required if using HTTPS override | +| `SITES_RULE` | List of domains for SSL (Traefik rule for TLS domain routing) | — | Required if using reverse proxy override | **Format for `SITES_RULE`:** diff --git a/docs/02-setup/05-overrides.md b/docs/02-setup/05-overrides.md index 156a6834..f1e274b1 100644 --- a/docs/02-setup/05-overrides.md +++ b/docs/02-setup/05-overrides.md @@ -4,24 +4,24 @@ Overrides extend the base compose.yaml with additional services or modify existi docker compose -f compose.yaml -f overrides/compose.mariadb.yaml -f overrides/compose.redis.yaml config > compose.custom.yaml ``` -| Overrider | Purpose | Additional Info | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| **Database** | | | -| compose.mariadb.yaml | Adds MariaDB database service | set `DB_PASSWORD` or default Password will be used | -| compose.mariadb-secrets.yaml | Adds MariaDB with password from a secret file instead of environment variable | Set `DB_PASSWORD_SECRETS_FILE` to the path of your secret file | -| compose.mariadb-shared.yaml | Makes MariaDB available on a shared network (mariadb-network) for other services | set `DB_PASSWORD` | -| compose.postgres.yaml | Uses PostgreSQL instead of MariaDB as the database | set `DB_PASSWORD` | -| **Proxy** | | | -| compose.noproxy.yaml | Exposes the application directly on port `:8080` without a reverse proxy | | -| compose.proxy.yaml | Uses Traefik as HTTP reverse proxy on port `:80` | You can change the published port by setting `HTTP_PUBLISH_PORT` | +| Overrider | Purpose | Additional Info | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| **Database** | | | +| compose.mariadb.yaml | Adds MariaDB database service | set `DB_PASSWORD` or default Password will be used | +| compose.mariadb-secrets.yaml | Adds MariaDB with password from a secret file instead of environment variable | Set `DB_PASSWORD_SECRETS_FILE` to the path of your secret file | +| compose.mariadb-shared.yaml | Makes MariaDB available on a shared network (mariadb-network) for other services | set `DB_PASSWORD` | +| compose.postgres.yaml | Uses PostgreSQL instead of MariaDB as the database | set `DB_PASSWORD` | +| **Proxy** | | | +| compose.noproxy.yaml | Exposes the application directly on port `:8080` without a reverse proxy | | +| compose.proxy.yaml | Uses Traefik as HTTP reverse proxy on port `:80` | You can change the published port by setting `HTTP_PUBLISH_PORT` | | compose.https.yaml | Uses Traefik as HTTPS reverse proxy on Port `:443` with automatic HTTP-to-HTTPS redirect | `SITES_RULE` and `LETSENCRYPT_EMAIL` must be set. `HTTP_PUBLISH_PORT` and `HTTPS_PUBLISH_PORT` can be set. | -| **Redis** | | | +| **Redis** | | | | compose.redis.yaml | Adds Redis service for caching and background job queuing | | **TBD** | **The following overrides are available but lack documentation. If you use them and understand their purpose, please consider contributing to this documentation.** | -| compose.backup-cron.yaml | | | -| compose.custom-domain-ssl.yaml | | | -| compose.custom-domain.yaml | | | -| compose.multi-bench-ssl.yaml | | | -| compose.multi-bench.yaml | | | -| compose.traefik-ssl.yaml | | | -| compose.traefik.yaml | | | +| compose.backup-cron.yaml | | | +| compose.custom-domain-ssl.yaml | | | +| compose.custom-domain.yaml | | | +| compose.multi-bench-ssl.yaml | | | +| compose.multi-bench.yaml | | | +| compose.traefik-ssl.yaml | | | +| compose.traefik.yaml | | | diff --git a/docs/06-migration/02-traefik-v3-migration.md b/docs/06-migration/02-traefik-v3-migration.md index 9e4ed21f..b452779c 100644 --- a/docs/06-migration/02-traefik-v3-migration.md +++ b/docs/06-migration/02-traefik-v3-migration.md @@ -3,24 +3,27 @@ Use this guide if you already run Traefik v2 with `frappe_docker` and want to upgrade to v3. It focuses on the image upgrade and the v3 routing rule changes that affect existing setups. ### Before you start + Before migrating anything, it is always recommended to create a backup. Better safe than sorry. In particular, compose and .env should be backed up. ### Quick upgrade summary -1) Pull the updated repo -2) Update env variables especially the updated `SITES` to `SITES_RULE` -3) Regenerate the compose config and restart the stack +1. Pull the updated repo +2. Update env variables especially the updated `SITES` to `SITES_RULE` +3. Regenerate the compose config and restart the stack #### Multiple hostnames v2 allowed comma-separated host lists inside `Host(...)`. In v3 traefik uses logical OR **Before (v2):** + ``` Host(`a.example.com`,`b.example.com`) ``` **After (v3):** + ``` Host(`a.example.com`) || Host(`b.example.com`) ``` @@ -30,11 +33,13 @@ Host(`a.example.com`) || Host(`b.example.com`) All Traefik routing for HTTPS and multi-bench setups now uses `SITES_RULE`, which is a full v3 rule expression **Single site:** + ``` SITES_RULE=Host(`erp.example.com`) ``` **Multiple sites:** + ``` SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`) ``` @@ -51,6 +56,7 @@ docker compose --env-file .env \ -f overrides/compose.https.yaml \ config > ~/gitops/docker-compose.yml ``` + ```sh docker compose --project-name -f ~/gitops/docker-compose.yml up -d ``` @@ -65,6 +71,6 @@ After restarting, Traefik will be used in the new supported version 3.6 and the If you need to rollback: -1) Revert Traefik image to `v2.11` -2) Restore the old `SITES` variable format and v2 rules -3) Regenerate the compose config and restart +1. Revert Traefik image to `v2.11` +2. Restore the old `SITES` variable format and v2 rules +3. Regenerate the compose config and restart diff --git a/overrides/compose.https.yaml b/overrides/compose.https.yaml index 55b453e8..c9c0f525 100644 --- a/overrides/compose.https.yaml +++ b/overrides/compose.https.yaml @@ -1,33 +1,33 @@ -services: - frontend: - labels: +services: + frontend: + labels: - traefik.enable=true - traefik.http.services.frontend.loadbalancer.server.port=8080 - traefik.http.routers.frontend-http.entrypoints=websecure - traefik.http.routers.frontend-http.tls.certresolver=main-resolver - traefik.http.routers.frontend-http.ruleSyntax=v3 - traefik.http.routers.frontend-http.rule=${SITES_RULE:?SITES_RULE not set} - - proxy: - image: traefik:v3.6 - restart: unless-stopped - command: - - --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 - - --certificatesResolvers.main-resolver.acme.httpChallenge=true - - --certificatesResolvers.main-resolver.acme.httpChallenge.entrypoint=web - - --certificatesResolvers.main-resolver.acme.email=${LETSENCRYPT_EMAIL:?No Let's Encrypt email set} - - --certificatesResolvers.main-resolver.acme.storage=/letsencrypt/acme.json - ports: - - ${HTTP_PUBLISH_PORT:-80}:80 - - ${HTTPS_PUBLISH_PORT:-443}:443 - volumes: - - cert-data:/letsencrypt - - /var/run/docker.sock:/var/run/docker.sock:ro - -volumes: - cert-data: + + proxy: + image: traefik:v3.6 + restart: unless-stopped + command: + - --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 + - --certificatesResolvers.main-resolver.acme.httpChallenge=true + - --certificatesResolvers.main-resolver.acme.httpChallenge.entrypoint=web + - --certificatesResolvers.main-resolver.acme.email=${LETSENCRYPT_EMAIL:?No Let's Encrypt email set} + - --certificatesResolvers.main-resolver.acme.storage=/letsencrypt/acme.json + ports: + - ${HTTP_PUBLISH_PORT:-80}:80 + - ${HTTPS_PUBLISH_PORT:-443}:443 + volumes: + - cert-data:/letsencrypt + - /var/run/docker.sock:/var/run/docker.sock:ro + +volumes: + cert-data: diff --git a/overrides/compose.proxy.yaml b/overrides/compose.proxy.yaml index b3c467c7..feada975 100644 --- a/overrides/compose.proxy.yaml +++ b/overrides/compose.proxy.yaml @@ -1,20 +1,20 @@ -services: - frontend: - labels: +services: + frontend: + labels: - traefik.enable=true - traefik.http.services.frontend.loadbalancer.server.port=8080 - traefik.http.routers.frontend-http.entrypoints=web - traefik.http.routers.frontend-http.ruleSyntax=v3 - traefik.http.routers.frontend-http.rule=HostRegexp(`^.+$`) - - proxy: - image: traefik:v3.6 - command: - - --providers.docker - - --providers.docker.exposedbydefault=false - - --entrypoints.web.address=:80 - ports: - - ${HTTP_PUBLISH_PORT:-80}:80 - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - userns_mode: host + + proxy: + image: traefik:v3.6 + command: + - --providers.docker + - --providers.docker.exposedbydefault=false + - --entrypoints.web.address=:80 + ports: + - ${HTTP_PUBLISH_PORT:-80}:80 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + userns_mode: host diff --git a/overrides/compose.traefik-ssl.yaml b/overrides/compose.traefik-ssl.yaml index 19083bf2..0bdf45fe 100644 --- a/overrides/compose.traefik-ssl.yaml +++ b/overrides/compose.traefik-ssl.yaml @@ -1,49 +1,49 @@ -services: - traefik: - labels: - # https-redirect middleware to redirect HTTP to HTTPS - # It can be reused by other stacks in other Docker Compose files - - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https - - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true - # traefik-http to use the middleware to redirect to https - - traefik.http.routers.traefik-public-http.middlewares=https-redirect +services: + traefik: + labels: + # https-redirect middleware to redirect HTTP to HTTPS + # It can be reused by other stacks in other Docker Compose files + - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true + # traefik-http to use the middleware to redirect to https + - traefik.http.routers.traefik-public-http.middlewares=https-redirect # traefik-https the actual router using HTTPS # Uses the environment variable DOMAIN - traefik.http.routers.traefik-public-https.ruleSyntax=v3 - traefik.http.routers.traefik-public-https.rule=Host(`${TRAEFIK_DOMAIN}`) - - traefik.http.routers.traefik-public-https.entrypoints=https - - traefik.http.routers.traefik-public-https.tls=true - # Use the special Traefik service api@internal with the web UI/Dashboard - - traefik.http.routers.traefik-public-https.service=api@internal - # Use the "le" (Let's Encrypt) resolver created below - - traefik.http.routers.traefik-public-https.tls.certresolver=le - # Enable HTTP Basic auth, using the middleware created above - - traefik.http.routers.traefik-public-https.middlewares=admin-auth - command: - # Enable Docker in Traefik, so that it reads labels from Docker services - - --providers.docker=true - # Do not expose all Docker services, only the ones explicitly exposed - - --providers.docker.exposedbydefault=false - # Create an entrypoint http listening on port 80 - - --entrypoints.http.address=:80 - # Create an entrypoint https listening on port 443 - - --entrypoints.https.address=:443 - # Create the certificate resolver le for Let's Encrypt, uses the environment variable EMAIL - - --certificatesresolvers.le.acme.email=${EMAIL:?No EMAIL set} - # Store the Let's Encrypt certificates in the mounted volume - - --certificatesresolvers.le.acme.storage=/certificates/acme.json - # Use the TLS Challenge for Let's Encrypt - - --certificatesresolvers.le.acme.tlschallenge=true - # Enable the access log, with HTTP requests - - --accesslog - # Enable the Traefik log, for configurations and errors - - --log - # Enable the Dashboard and API - - --api - ports: - - ${HTTPS_PUBLISH_PORT:-443}:443 - volumes: - - cert-data:/certificates - -volumes: - cert-data: + - traefik.http.routers.traefik-public-https.entrypoints=https + - traefik.http.routers.traefik-public-https.tls=true + # Use the special Traefik service api@internal with the web UI/Dashboard + - traefik.http.routers.traefik-public-https.service=api@internal + # Use the "le" (Let's Encrypt) resolver created below + - traefik.http.routers.traefik-public-https.tls.certresolver=le + # Enable HTTP Basic auth, using the middleware created above + - traefik.http.routers.traefik-public-https.middlewares=admin-auth + command: + # Enable Docker in Traefik, so that it reads labels from Docker services + - --providers.docker=true + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.docker.exposedbydefault=false + # Create an entrypoint http listening on port 80 + - --entrypoints.http.address=:80 + # Create an entrypoint https listening on port 443 + - --entrypoints.https.address=:443 + # Create the certificate resolver le for Let's Encrypt, uses the environment variable EMAIL + - --certificatesresolvers.le.acme.email=${EMAIL:?No EMAIL set} + # Store the Let's Encrypt certificates in the mounted volume + - --certificatesresolvers.le.acme.storage=/certificates/acme.json + # Use the TLS Challenge for Let's Encrypt + - --certificatesresolvers.le.acme.tlschallenge=true + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + # Enable the Dashboard and API + - --api + ports: + - ${HTTPS_PUBLISH_PORT:-443}:443 + volumes: + - cert-data:/certificates + +volumes: + cert-data: diff --git a/overrides/compose.traefik.yaml b/overrides/compose.traefik.yaml index 01ad3c5f..15885363 100644 --- a/overrides/compose.traefik.yaml +++ b/overrides/compose.traefik.yaml @@ -1,46 +1,46 @@ -services: - traefik: - image: "traefik:v3.6" - restart: unless-stopped - labels: - # Enable Traefik for this service, to make it available in the public network - - traefik.enable=true - # Use the traefik-public network (declared below) - - traefik.docker.network=traefik-public +services: + traefik: + image: "traefik:v3.6" + restart: unless-stopped + labels: + # Enable Traefik for this service, to make it available in the public network + - traefik.enable=true + # Use the traefik-public network (declared below) + - traefik.docker.network=traefik-public # admin-auth middleware with HTTP Basic auth # Using the environment variables USERNAME and HASHED_PASSWORD - traefik.http.middlewares.admin-auth.basicauth.users=admin:${HASHED_PASSWORD:?No HASHED_PASSWORD set} # Uses the environment variable TRAEFIK_DOMAIN - traefik.http.routers.traefik-public-http.ruleSyntax=v3 - traefik.http.routers.traefik-public-http.rule=Host(`${TRAEFIK_DOMAIN:?No TRAEFIK_DOMAIN set}`) - - traefik.http.routers.traefik-public-http.entrypoints=http - # Use the special Traefik service api@internal with the web UI/Dashboard - - traefik.http.routers.traefik-public-http.service=api@internal - # Enable HTTP Basic auth, using the middleware created above - - traefik.http.routers.traefik-public-http.middlewares=admin-auth - # Define the port inside of the Docker service to use - - traefik.http.services.traefik-public.loadbalancer.server.port=8080 - command: - # Enable Docker in Traefik, so that it reads labels from Docker services - - --providers.docker=true - # Do not expose all Docker services, only the ones explicitly exposed - - --providers.docker.exposedbydefault=false - # Create an entrypoint http listening on port 80 - - --entrypoints.http.address=:80 - # Enable the access log, with HTTP requests - - --accesslog - # Enable the Traefik log, for configurations and errors - - --log - # Enable the Dashboard and API - - --api - ports: - - ${HTTP_PUBLISH_PORT:-80}:80 - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - networks: - - traefik-public - -networks: - traefik-public: - name: traefik-public - external: false + - traefik.http.routers.traefik-public-http.entrypoints=http + # Use the special Traefik service api@internal with the web UI/Dashboard + - traefik.http.routers.traefik-public-http.service=api@internal + # Enable HTTP Basic auth, using the middleware created above + - traefik.http.routers.traefik-public-http.middlewares=admin-auth + # Define the port inside of the Docker service to use + - traefik.http.services.traefik-public.loadbalancer.server.port=8080 + command: + # Enable Docker in Traefik, so that it reads labels from Docker services + - --providers.docker=true + # Do not expose all Docker services, only the ones explicitly exposed + - --providers.docker.exposedbydefault=false + # Create an entrypoint http listening on port 80 + - --entrypoints.http.address=:80 + # Enable the access log, with HTTP requests + - --accesslog + # Enable the Traefik log, for configurations and errors + - --log + # Enable the Dashboard and API + - --api + ports: + - ${HTTP_PUBLISH_PORT:-80}:80 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - traefik-public + +networks: + traefik-public: + name: traefik-public + external: false From a69b002881758e906a60cd362c64b1be17e93035 Mon Sep 17 00:00:00 2001 From: RocketQuack <202538874+Rocket-Quack@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:23:30 +0100 Subject: [PATCH 6/9] tests: set SITES_RULE correctly for https override --- tests/conftest.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index c6ff166a..24c9f750 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,11 +26,19 @@ def _add_version_var(name: str, env_path: Path): def _add_sites_var(env_path: Path): with open(env_path, "r+") as f: content = f.read() + sites = ( + "tests.localhost", + "test-erpnext-site.localhost", + "test-pg-site.localhost", + ) + sites_list = ",".join(f"`{site}`" for site in sites) + sites_rule = " || ".join(f"Host(`{site}`)" for site in sites) content = re.sub( rf"SITES=.*", - f"SITES=`tests.localhost`,`test-erpnext-site.localhost`,`test-pg-site.localhost`", + f"SITES={sites_list}", content, ) + content = re.sub(rf"SITES_RULE=.*", f"SITES_RULE={sites_rule}", content) f.seek(0) f.truncate() f.write(content) From 269e44e2cdb91338a79672e38d001fd673912870 Mon Sep 17 00:00:00 2001 From: RocketQuack <202538874+Rocket-Quack@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:30:48 +0100 Subject: [PATCH 7/9] feat: drop rule syntax and v2 override --- overrides/compose.custom-domain.yaml | 1 - overrides/compose.https.yaml | 4 ---- overrides/compose.multi-bench-ssl.yaml | 1 - overrides/compose.multi-bench.yaml | 1 - overrides/compose.proxy.yaml | 4 ---- overrides/compose.traefik-ssl.yaml | 4 ---- overrides/compose.traefik.yaml | 4 ---- 7 files changed, 19 deletions(-) diff --git a/overrides/compose.custom-domain.yaml b/overrides/compose.custom-domain.yaml index 7ca31776..ca1b54f7 100644 --- a/overrides/compose.custom-domain.yaml +++ b/overrides/compose.custom-domain.yaml @@ -14,7 +14,6 @@ services: - traefik.http.services.${ROUTER?ROUTER not set}.loadbalancer.server.port=2016 - traefik.http.routers.${ROUTER}.service=${ROUTER} - traefik.http.routers.${ROUTER}.entrypoints=http - - traefik.http.routers.${ROUTER}.ruleSyntax=v3 - traefik.http.routers.${ROUTER}.rule=${SITES_RULE?SITES_RULE not set} - traefik.http.middlewares.${ROUTER}.headers.customrequestheaders.Host=${BASE_SITE?BASE_SITE not set} - traefik.http.routers.${ROUTER}.middlewares=${ROUTER} diff --git a/overrides/compose.https.yaml b/overrides/compose.https.yaml index 13555363..32ef2bae 100644 --- a/overrides/compose.https.yaml +++ b/overrides/compose.https.yaml @@ -5,7 +5,6 @@ services: - traefik.http.services.frontend.loadbalancer.server.port=8080 - traefik.http.routers.frontend-http.entrypoints=websecure - traefik.http.routers.frontend-http.tls.certresolver=main-resolver - - traefik.http.routers.frontend-http.ruleSyntax=v3 - traefik.http.routers.frontend-http.rule=${SITES_RULE:?SITES_RULE not set} proxy: @@ -14,9 +13,6 @@ services: command: - --providers.docker=true - --providers.docker.exposedbydefault=false - # Keep v2 rule syntax for now - # Ensure compatibility with existing labels (will be removed if v3 transition is complete) - - --core.defaultRuleSyntax=v2 - --entrypoints.web.address=:80 - --entrypoints.web.http.redirections.entrypoint.to=websecure - --entrypoints.web.http.redirections.entrypoint.scheme=https diff --git a/overrides/compose.multi-bench-ssl.yaml b/overrides/compose.multi-bench-ssl.yaml index e38da19b..1c5e3dac 100644 --- a/overrides/compose.multi-bench-ssl.yaml +++ b/overrides/compose.multi-bench-ssl.yaml @@ -5,7 +5,6 @@ services: - traefik.http.routers.${ROUTER}-http.middlewares=https-redirect # ${ROUTER}-https the actual router using HTTPS # Uses the environment variable SITES_RULE - - traefik.http.routers.${ROUTER}-https.ruleSyntax=v3 - traefik.http.routers.${ROUTER}-https.rule=${SITES_RULE?SITES_RULE not set} - traefik.http.routers.${ROUTER}-https.entrypoints=https - traefik.http.routers.${ROUTER}-https.tls=true diff --git a/overrides/compose.multi-bench.yaml b/overrides/compose.multi-bench.yaml index 542e7716..d499686b 100644 --- a/overrides/compose.multi-bench.yaml +++ b/overrides/compose.multi-bench.yaml @@ -9,7 +9,6 @@ services: - traefik.http.services.${ROUTER?ROUTER not set}.loadbalancer.server.port=8080 - traefik.http.routers.${ROUTER}-http.service=${ROUTER} - traefik.http.routers.${ROUTER}-http.entrypoints=http - - traefik.http.routers.${ROUTER}-http.ruleSyntax=v3 - traefik.http.routers.${ROUTER}-http.rule=${SITES_RULE?SITES_RULE not set} configurator: networks: diff --git a/overrides/compose.proxy.yaml b/overrides/compose.proxy.yaml index a47b3a7e..4b084eaa 100644 --- a/overrides/compose.proxy.yaml +++ b/overrides/compose.proxy.yaml @@ -4,7 +4,6 @@ services: - traefik.enable=true - traefik.http.services.frontend.loadbalancer.server.port=8080 - traefik.http.routers.frontend-http.entrypoints=web - - traefik.http.routers.frontend-http.ruleSyntax=v3 - traefik.http.routers.frontend-http.rule=HostRegexp(`^.+$`) proxy: @@ -12,9 +11,6 @@ services: command: - --providers.docker - --providers.docker.exposedbydefault=false - # Keep v2 rule syntax for now - # Ensure compatibility with existing labels (will be removed if v3 transition is complete) - - --core.defaultRuleSyntax=v2 - --entrypoints.web.address=:80 ports: - ${HTTP_PUBLISH_PORT:-80}:80 diff --git a/overrides/compose.traefik-ssl.yaml b/overrides/compose.traefik-ssl.yaml index 2563363c..b83cb8e4 100644 --- a/overrides/compose.traefik-ssl.yaml +++ b/overrides/compose.traefik-ssl.yaml @@ -9,7 +9,6 @@ services: - traefik.http.routers.traefik-public-http.middlewares=https-redirect # traefik-https the actual router using HTTPS # Uses the environment variable DOMAIN - - traefik.http.routers.traefik-public-https.ruleSyntax=v3 - traefik.http.routers.traefik-public-https.rule=Host(`${TRAEFIK_DOMAIN}`) - traefik.http.routers.traefik-public-https.entrypoints=https - traefik.http.routers.traefik-public-https.tls=true @@ -24,9 +23,6 @@ services: - --providers.docker=true # Do not expose all Docker services, only the ones explicitly exposed - --providers.docker.exposedbydefault=false - # Keep v2 rule syntax for now - # Ensure compatibility with existing labels (will be removed if v3 transition is complete) - - --core.defaultRuleSyntax=v2 # Create an entrypoint http listening on port 80 - --entrypoints.http.address=:80 # Create an entrypoint https listening on port 443 diff --git a/overrides/compose.traefik.yaml b/overrides/compose.traefik.yaml index e36fcd2b..3dd2069f 100644 --- a/overrides/compose.traefik.yaml +++ b/overrides/compose.traefik.yaml @@ -11,7 +11,6 @@ services: # Using the environment variables USERNAME and HASHED_PASSWORD - traefik.http.middlewares.admin-auth.basicauth.users=admin:${HASHED_PASSWORD:?No HASHED_PASSWORD set} # Uses the environment variable TRAEFIK_DOMAIN - - traefik.http.routers.traefik-public-http.ruleSyntax=v3 - traefik.http.routers.traefik-public-http.rule=Host(`${TRAEFIK_DOMAIN:?No TRAEFIK_DOMAIN set}`) - traefik.http.routers.traefik-public-http.entrypoints=http # Use the special Traefik service api@internal with the web UI/Dashboard @@ -25,9 +24,6 @@ services: - --providers.docker=true # Do not expose all Docker services, only the ones explicitly exposed - --providers.docker.exposedbydefault=false - # Keep v2 rule syntax for now - # Ensure compatibility with existing labels (will be removed if v3 transition is complete) - - --core.defaultRuleSyntax=v2 # Create an entrypoint http listening on port 80 - --entrypoints.http.address=:80 # Enable the access log, with HTTP requests From bfb040e5a6c348bf4c563f8263ac540091a5a263 Mon Sep 17 00:00:00 2001 From: RocketQuack <202538874+Rocket-Quack@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:31:46 +0100 Subject: [PATCH 8/9] docs: mark v3 migration complete --- docs/02-setup/04-env-variables.md | 5 ++--- docs/06-migration/02-traefik-v3-migration.md | 7 +++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/02-setup/04-env-variables.md b/docs/02-setup/04-env-variables.md index 29aa94ec..a9c73a8a 100644 --- a/docs/02-setup/04-env-variables.md +++ b/docs/02-setup/04-env-variables.md @@ -60,9 +60,8 @@ SITES_RULE=Host(`mysite.example.com`) SITES_RULE=Host(`a.example.com`) || Host(`b.example.com`) ``` -> Note: `SITES` currently supplies the host list that goes into the `Host(...)` rules. Once the Traefik v3 transition is completed, the plan is to switch to `SITES_RULE` (full rule expression) and deprecate `SITES` to map the Traefik Rule Syntax. - -> Currently the used syntax is pinned to v2 via `--core.defaultRuleSyntax=v2`). +> Note: The Traefik v3 migration is complete. Use `SITES_RULE` as a full v3 rule expression; `SITES` is deprecated. +> Rule syntax now defaults to v3, so no `core.defaultRuleSyntax` or per-router `ruleSyntax` settings are required. --- diff --git a/docs/06-migration/02-traefik-v3-migration.md b/docs/06-migration/02-traefik-v3-migration.md index b452779c..cb0eee11 100644 --- a/docs/06-migration/02-traefik-v3-migration.md +++ b/docs/06-migration/02-traefik-v3-migration.md @@ -2,6 +2,9 @@ Use this guide if you already run Traefik v2 with `frappe_docker` and want to upgrade to v3. It focuses on the image upgrade and the v3 routing rule changes that affect existing setups. +> Note: The Traefik v2 -> v3 migration is complete. The provided overrides no longer set `core.defaultRuleSyntax` or per-router `ruleSyntax` labels, because v3 is the default rule syntax. +> Note: If you have a system that must continue to run on v2 despite EOL, you can pin v2 rule syntax with `--core.defaultRuleSyntax=v2` in your Traefik service. + ### Before you start Before migrating anything, it is always recommended to create a backup. Better safe than sorry. In particular, compose and .env should be backed up. @@ -14,7 +17,7 @@ Before migrating anything, it is always recommended to create a backup. Better s #### Multiple hostnames -v2 allowed comma-separated host lists inside `Host(...)`. In v3 traefik uses logical OR +v2 allowed comma-separated host lists inside `Host(...)`. In v3 Traefik uses logical OR. **Before (v2):** @@ -30,7 +33,7 @@ Host(`a.example.com`) || Host(`b.example.com`) ### Step 1: Replace `SITES` with `SITES_RULE` -All Traefik routing for HTTPS and multi-bench setups now uses `SITES_RULE`, which is a full v3 rule expression +All Traefik routing for HTTPS and multi-bench setups now uses `SITES_RULE`, which is a full v3 rule expression. **Single site:** From a022f94591642ceb6a9b6aef4e15122724edc83d Mon Sep 17 00:00:00 2001 From: RocketQuack <202538874+Rocket-Quack@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:33:02 +0100 Subject: [PATCH 9/9] tests: stop writing legacy SITES in env --- tests/conftest.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 24c9f750..63169e37 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -31,13 +31,7 @@ def _add_sites_var(env_path: Path): "test-erpnext-site.localhost", "test-pg-site.localhost", ) - sites_list = ",".join(f"`{site}`" for site in sites) sites_rule = " || ".join(f"Host(`{site}`)" for site in sites) - content = re.sub( - rf"SITES=.*", - f"SITES={sites_list}", - content, - ) content = re.sub(rf"SITES_RULE=.*", f"SITES_RULE={sites_rule}", content) f.seek(0) f.truncate()