Goal: Make OpenCloud available behind a NAT-router and from outside.
Use docker with Traefik; with ipv4 and ipv6.
Only OpenCloud is accessible from outside; other https services running as docker containers are only available from inside.
Required (using QTS 5.2.10):
- cross compile linux kernel module ip6table_nat.ko (reference) and load it by autorun.sh
- edit .qpkg/container-station/etc/docker.json and set
“ip6tables”: true - split dns to access opencloud from inside and outside
Used:
- Dockhand to manage docker containers and stacks
- Filebrowser Quantum to edit config files
- ssh access for qnap admin user
- mc as text editor when using ssh
- Entware to install mc
- Let’s Encrypt wildcard certificates created by Traefik using DNS-01 Challenge
- a DNS-Provider supported by lego (ACME client and ACME library used by Traefik)
- configure Traefik with multiple https EntryPoints
- a dynamic dns provider with support for ipv6 (AAAA records)
- a DNS server with A records pointing to YOURDOMAIN.TLD and to *.YOURDOMAIN.TLD with the ipv4 address of qnap ts-216g
Detailed Steps
Warning: The configuration described below is meant for a local network with trusted users!
- The Traefik dashboard and Dockhand, the docker management web interface run without password protection.
QNAP-Configuration
- Enable ipv6 (controlpanel/network/adapter): stateless, slaac with secret key. (Make sure your router firewall is blocking ipv6 access from outside)
- Enable ssh
- Enable admin user; (this asks for admin password; keep a secure cpy of this password.)
- Create (non admin) user container
- which gets uid:gid=1001:100, if this is the first user created after running setup
- to display uid, gid run by ssh command:
id container
- Enable autorun.sh (controlpanel/hardware)
- Install Entware:
- Download Entware
- App Center / “+” (upper right corner) / Install manually
- login as admin by ssh: opkg update; opk install mc
- Install ContainerStation with default shared folder /Container
- configure ContainerStation with default settings
- with QTS 5.2.10 (ipv6 is not available as builtin feature)
- docker compose files are located in subfolders of
/share/Container/container-station-data/application/
- Move https qnap webadmin to port 4443 (controlpanel/system/general settings)
Docker ipv6 on QNAP
- ssh into qnap and get the compiled version of ip6table_nat.ko (kernel version
5.10.60-qnap; md5 sum
8bca1a42748535c60a0ca0835a766b8c):
wget https://spblinux.de/qnap/ts-216g/ip6table_nat.ko
(If you prefer to compile this module yourself, see instructions below.)- modprobe ip6tables.ko
- insmod ./ip6table_nat.ko
- If both modules load successfully docker ipv6 can be enabled.
- use mc to navigate to
/share/CACHEDEV1_DATA/.qpkg/container-station/etc/ - and edit docker.json
- replace: “ip6tables”: false
- by: “ip6tables”: true
- restart ContainerStation
- /etc/init.d/container-station.sh stop
- /etc/init.d/container-station.sh start
- use mc to navigate to
- Create ipv6 enabled docker network traefik-net
- docker network create –ipv6 traefik-net
- (if command docker is not found, exit from ssh and login again.)
- If traefik-net has been created successfully docker runs with ipv6 support:
- docker network inspect traefik-net
- Make docker ipv6 support permanent:
- mount the config partition of qnap flash storage
- /etc/init.d/init_disk.sh mount_flash_config
- cd /tmp/nasconfig_tmp/
- touch autorun.sh
- chmod +x autorun.sh
- copy ip6table_nas.ko to /tmp/nasconfig_tmp/
- open autorun.sh with mc editor and paste the code below into this file
- save autorun.sh
- /etc/init.d/init_disk.sh umount_flash_config
- make sure autorun.sh is enabled in controlpanel/hardware
- reboot the qnap nas and check if ip6table_nat.ko has been loaded
- lsmod | grep ip6
- docker network inspect traefik-net
- mount the config partition of qnap flash storage
#!/bin/sh
#
# to edit this file use:
# /etc/init.d/init_disk.sh mount_flash_config
# edit /tmp/nasconfig_tmp/autorun.sh
# /etc/init.d/init_disk.sh umount_flash_config
#
modprobe ip6_tables
[ -d /tmp/nasconfig_tmp2 ] || mkdir /tmp/nasconfig_tmp2
mount /dev/mmcblk0p5 /tmp/nasconfig_tmp2
[ -f /tmp/nasconfig_tmp2/ip6table_nat.ko ] && insmod /tmp/nasconfig_tmp2/ip6table_nat.ko
umount /dev/mmcblk0p5 /tmp/nasconfig_tmp2
Install Traefik with socketproxy
Use ssh as admin user:
- cd /share/Container/container-station-data/application
- mkdir traefik
- cd traefik
- either use mc to create and edit the configuration files or instal qnap App Text Editor
- this configuration is based on https://github.com/Haxxnet/Compose-Examples/tree/main/examples/traefik
docker-compose.yml
(replace 192.168.224.252 by your qnap ipv4 address)
services:
traefik:
env_file:
- path: /share/Container/container-station-data/application/traefik/.env
image: traefik:v3.7
container_name: traefik
restart: always
ports:
# web, webs is set as default
- 192.168.224.252:80:80/tcp # web
- 192.168.224.252:443:443/tcp # webs
- 192.168.224.252:443:443/udp # https http3 quic: webs
# label: - traefik.routers.ROUTERNAME.entrypoints=web2,webs2
# - 192.168.226.252:80:81/tcp # web2
- 192.168.224.252:65480:81/tcp # web2, limit to 1 network adapter
#- 192.168.226.252:443:444/tcp # webs2
- 192.168.224.252:65443:444/tcp # webs2, limit to 1 network adapter
# label: - traefik.routers.ROUTERNAME.entrypoints=web2,webs2,webs3
- "[::]:443:445/tcp" # webs3, limit to ipv6, all network adapters
#- 192.168.226.252:443:444/udp # https http3 quic: webs2
- 8081:8080 # http api dashboard; also at https://YOURDOMAIN.TLD
volumes:
#- /var/run/docker.sock:/var/run/docker.sock:ro # better use socket-proxy instead
- /share/Container/container-station-data/application/traefik:/etc/traefik/ # put the provided traefik.yml and fileConfig.yml files at this location
- /share/Container/container-station-data/application/traefik/logs:/logs
environment:
- TZ=Europe/Zurich
labels:
- traefik.enable=true
- traefik.http.routers.api.rule=Host(`YOURDOMAIN.TLD`) # Define the (sub)domain for the traefik dashboard.
- traefik.http.routers.api.service=api@internal # Enable Traefik API.
- traefik.http.routers.traefik.middlewares=rate-limit@file
#- traefik.http.routers.api.middlewares=local-ipwhitelist@file,basic-auth@file # protect dashboard with basic auth and restrict access to private class subnets only
#- traefik.http.middlewares.basic-auth-global.basicauth.users=admin:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0 # protect the traefik dashboard by basic auth (pw=password)
# extra_hosts:
# - host.docker.internal:172.17.0.1
security_opt:
- no-new-privileges:true
networks:
- traefik-net
- socketproxy-net
socket-proxy:
image: lscr.io/linuxserver/socket-proxy:latest
container_name: socket-proxy
environment:
- CONTAINERS=1
- EVENTS=1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socketproxy-net
restart: always
labels:
- com.centurylinklabs.watchtower.enable=false
read_only: true
tmpfs:
- /run
networks:
traefik-net:
external: true # docker network ls; docker network create --ipv6 traefik-net
socketproxy-net:
internal: true
traefik.yml
# Traefik global configuration
global:
checkNewVersion: true
sendAnonymousUsage: false
# Enable traefik ui dashboard
api:
dashboard: true
insecure: true
# Log level INFO|DEBUG|ERROR
log:
level: INFO
# crowdsec bouncer
#experimental:
# plugins:
# bouncer:
# moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
# version: v1.7.1
#metrics:
# influxDB2:
# address: http://influxdb2:8086
# token: my-secure-token-secret
# org: influx-org
# bucket: influx-bucket
# addEntryPointsLabels: true
# addRoutersLabels: true
# addServicesLabels: true
# pushInterval: 60s
# Configuring Multiple Filters
accessLog:
filePath: "/logs/traefik.log"
format: json
filters:
statusCodes:
- "200-299" # log successful http requests
- "300-399" # log redirect http requests
- "400-599" # log failed http requests
#retryAttempts: true
#minDuration: "10ms"
# collect logs as in-memory buffer before writing into log file
bufferingSize: 0
fields:
headers:
defaultMode: drop # drop all headers per default
names:
User-Agent: keep # log user agent strings
# The setting below is to allow insecure backend connections.
serverTransport:
insecureSkipVerify: true
# Traefik entrypoints (network ports) configuration
entryPoints:
# asDefault: true # in use unless changed by label traefik.http.routers.ROUTERNAME.entrypoints=web,webs
# Not used in apps, but redirect everything from HTTP to HTTPS
web:
asDefault: true
address: :80
# forwardedHeaders:
http:
# https://doc.traefik.io/traefik/migrate/v3/#underscoreheadersstrategy
underscoreHeadersStrategy: delete
redirections:
entryPoint:
to: webs
scheme: https
web2:
address: :81
# forwardedHeaders:
http:
# https://doc.traefik.io/traefik/migrate/v3/#underscoreheadersstrategy
underscoreHeadersStrategy: delete
redirections:
entryPoint:
to: webs2
scheme: https
web3:
address: "[::]:82"
# forwardedHeaders:
http:
# https://doc.traefik.io/traefik/migrate/v3/#underscoreheadersstrategy
underscoreHeadersStrategy: delete
redirections:
entryPoint:
to: webs3
scheme: https
# HTTPS endpoint, with domain wildcard
webs:
asDefault: true
address: :443
forwardedHeaders:
# reuse list of Cloudflare Trusted IP's above for HTTPS requests
# trustedIPs: *trustedIps
# enable HTTP3 QUIC via UDP/443
http3:
advertisedPort: '443'
http:
# https://doc.traefik.io/traefik/migrate/v3/#underscoreheadersstrategy
aliasHeadersStrategy: delete
# use dynamically generated self-signed certificate
#tls: {}
# or use a pre-defined certificate resolver from below
# if so, remove above line with tls: {}
tls:
certResolver: myresolver-dns
domains:
- main: YOURDOMAIN.TLD # change this to your proxy domain
sans:
- '*.YOURDOMAIN.TLD' # change this to your proxy domain
middlewares:
- security-headers@file # reference to a dynamic middleware for setting http security headers per default
webs2:
address: :444
forwardedHeaders:
# reuse list of Cloudflare Trusted IP's above for HTTPS requests
# trustedIPs: *trustedIps
# enable HTTP3 QUIC via UDP/443
#http3:
# advertisedPort: '444'
http:
# https://doc.traefik.io/traefik/migrate/v3/#underscoreheadersstrategy
aliasHeadersStrategy: delete
# use dynamically generated self-signed certificate
#tls: {}
# or use a pre-defined certificate resolver from below
# if so, remove above line with tls: {}
tls:
certResolver: myresolver-dns
domains:
- main: YOURDOMAIN.TLD # change this to your proxy domain
sans:
- '*.YOURDOMAIN.TLD' # change this to your proxy domain
middlewares:
- security-headers@file # reference to a dynamic middleware for setting http security headers per default
webs3:
address: "[::]:445"
forwardedHeaders:
# reuse list of Cloudflare Trusted IP's above for HTTPS requests
# trustedIPs: *trustedIps
# enable HTTP3 QUIC via UDP/443
#http3:
# advertisedPort: '444'
http:
# https://doc.traefik.io/traefik/migrate/v3/#underscoreheadersstrategy
aliasHeadersStrategy: delete
# use dynamically generated self-signed certificate
#tls: {}
# or use a pre-defined certificate resolver from below
# if so, remove above line with tls: {}
tls:
certResolver: myresolver-dns
domains:
- main: YOURDOMAIN.TLD # change this to your proxy domain
sans:
- '*.YOURDOMAIN.TLD' # change this to your proxy domain
middlewares:
- security-headers@file # reference to a dynamic middleware for setting http security headers per default
providers:
providersThrottleDuration: 2s
# File provider for connecting things that are outside of docker / defining middleware
file:
filename: /etc/traefik/fileConfig.yml
watch: true
# Docker provider for connecting all apps that are inside of the docker network
docker:
watch: true
network: traefik-net # add Your Docker Network Name Here
endpoint: "tcp://socket-proxy:2375" # use socket-proxy for secure access to docker api
defaultRule: "Host(`{{ index .Labels \"com.docker.compose.service\"}}.YOURDOMAIN.TLD`)" # subdomain==service name; change 'YOURDOMAIN.TLD' to your proxy domain
# defaultRule: "Host(`{{ .Name }}.YOURDOMAIN.TLD`)" # subdomain==SERVICENAME-CONTAINERNAME; change 'YOURDOMAIN.TLD' to your proxy domain
exposedByDefault: false # only containers with label traefik.enable=true are routed by traefik
# Use letsencrypt to generate ssl certificates
certificatesResolvers:
# ACME HTTP CHALLENGE
myresolver-http:
acme:
email: osth@sunrise.ch # the email address used for ssl certificate registration
storage: /etc/traefik/acme.json
httpChallenge: # acme http challenge; requires port 80 and proper dns entries
entryPoint: web # specify the entry point for the HTTP challenge (adjust if needed)
# ACME DNS CHALLENGE
myresolver-dns:
acme:
email: YOU@MAIL.ORG # the email address used for ssl certificate registration
storage: /etc/traefik/acme.json
dnsChallenge: # acme dns challenge; requires api token of dns provider
# provider: YOURDNSHOST2
provider: desec
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
fileConfig.yml
http:
## EXTERNAL ROUTING EXAMPLE - Only use if you want to proxy something manually ##
# routers:
# test3:
# entryPoints:
# - https
# - http
# rule: 'Host(`test3.YOURDOMAIN.TLD`)'
# service: test3
# middlewares:
# - "local-ipwhitelist@file"
#routers:
# homeassistant:
# entryPoints:
# - https
# - http
# rule: 'Host(`ha.example.com`)'
# service: homeassistant
# middlewares:
# - "local-ipwhitelist@file"
# pve:
# entryPoints:
# - https
# - http
# rule: 'Host(`pve.example.com`)'
# service: pve
# middlewares:
# - "local-ipwhitelist@file"
## SERVICES EXAMPLE ##
# services:
# test3:
# loadBalancer:
# serversTransport: insecureTransport
# servers:
# - url: https://t4.YOURDOMAIN.TLD
# homeassistant:
# loadBalancer:
# serversTransport: insecureTransport
# servers:
# - url: http://192.168.1.10:8123
# pve:
# loadBalancer:
# serversTransport: insecureTransport
# servers:
# - url: https://192.168.1.20:8006
# allow self-signed certificates for proxied web services
serversTransports:
insecureTransport:
insecureSkipVerify: true
## MIDDLEWARES ##
middlewares:
# UNUSED remove X-Frame-Options header by plugin because of only office
#custom-allow-iframe:
# plugin:
# htransformation:
# Rules:
# - Header: 'X-Frame-Options'
# Name: 'allow iframe'
# Type: 'Del'
# SetOnResponse: 'True'
# Only Allow Local networks
local-ipwhitelist:
ipAllowList:
sourceRange:
- 127.0.0.1/32 # localhost
- 10.0.0.0/8 # private class A
- 172.16.0.0/12 # private class B
- 192.168.0.0/16 # private class C
#ipstrategy: # enable this when cloudflare proxy in use
# depth: 1 # enable this when cloudflare proxy in use
# Security headers
security-headers:
headers:
customResponseHeaders: # field names are case-insensitive
#X-Robots-Tag: "all,noarchive,nosnippet,notranslate,noimageindex"
Server: "" # prevent version disclosure
X-Powered-By: "" # prevent version disclosure
X-Forwarded-Proto: "https"
#Permissions-Policy: "accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), identity-credentials-get=(), idle-detection=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), otp-credentials=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), storage-access=(), usb=(), web-share=(), window-management=(), xr-spatial-tracking=()"
#Cross-Origin-Embedder-Policy: "unsafe-none"
#Cross-Origin-Opener-Policy: "same-origin"
#Cross-Origin-Resource-Policy: "same-site"
#X-Permitted-Cross-Domain-Policies: "none"
sslProxyHeaders:
X-Forwarded-Proto: "https"
hostsProxyHeaders:
- "X-Forwarded-Host"
#customRequestHeaders:
# X-Forwarded-Proto: "https"
contentTypeNosniff: true # X-Content-Type-Options
customFrameOptionsValue: "SAMEORIGIN" # X-Frame-Options
browserXssFilter: false # X-XSS-Protection; deprecated
referrerPolicy: "strict-origin-when-cross-origin" # Referrer-Policy
forceSTSHeader: true # HTTP-Strict-Transport-Security (HSTS)
stsIncludeSubdomains: true # HTTP-Strict-Transport-Security (HSTS)
stsSeconds: 63072000 # HTTP-Strict-Transport-Security (HSTS)
stsPreload: true # HTTP-Strict-Transport-Security (HSTS)
#contentSecurityPolicy: "default-src 'self'; form-action 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests; block-all-mixed-content" # Content-Security-Policy (CSP)
# Authelia guard
#authelia:
# forwardauth:
# address: http://authelia:9091/api/authz/forward-auth # replace example.com with your domain name
# trustForwardHeader: true
# authResponseHeaders:
# - Remote-User
# - Remote-Groups
# - Remote-Name
# - Remote-Email
#crowdsec:
# plugin:
# bouncer:
# enabled: true
# defaultDecisionSeconds: 60
# crowdsecMode: live
# crowdsecAppsecEnabled: false
# crowdsecAppsecHost: crowdsec:7422
# crowdsecAppsecFailureBlock: true
# crowdsecAppsecUnreachableBlock: true
# crowdsecLapiKey: FIXME-LAPI-KEY # <--- replace this later
# crowdsecLapiHost: crowdsec:8080
# crowdsecLapiScheme: http
# crowdsecLapiTLSInsecureVerify: false
# banFilePath: /ban.html
# captchaProvider: hcaptcha
# captchaSiteKey: YourSiteKey
# captchaSecretKey: YourSecretKey
# captchaGracePeriodSeconds: 1800
# captchaFilePath: /captcha.html
# forwardedHeadersTrustedIPs:
# # private class ranges
# - 10.0.0.0/8
# - 172.16.0.0/12
# - 192.168.0.0/16
# ## cloudflare ipv4
# #- 103.21.244.0/22
# #- 103.22.200.0/22
# #- 103.31.4.0/22
# #- 104.16.0.0/13
# #- 104.24.0.0/14
# #- 108.162.192.0/18
# #- 131.0.72.0/22
# #- 141.101.64.0/18
# #- 162.158.0.0/15
# #- 172.64.0.0/13
# #- 173.245.48.0/20
# #- 188.114.96.0/20
# #- 190.93.240.0/20
# #- 197.234.240.0/22
# #- 198.41.128.0/17
# ## cloudflare ipv6
# #- 2400:cb00::/32
# #- 2606:4700::/32
# #- 2803:f800::/32
# #- 2405:b500::/32
# #- 2405:8100::/32
# #- 2a06:98c0::/29
# #- 2c0f:f248::/32
# clientTrustedIPs:
# # private class ranges
# - 10.0.0.0/8
# - 172.16.0.0/12
# - 192.168.0.0/16
# rate limiting
rate-limit:
rateLimit:
average: 50
period: 1s
burst: 100
# uncomment the next lines if behind cloudflare
#sourceCriterion:
# requestHeaderName: CF-Connecting-IP
onlyoffice-ratelimit:
rateLimit:
average: 200
period: 1s
burst: 400
# basic auth popup
basic-auth:
basicAuth:
# https://hostingcanada.org/htpasswd-generator/
users: "admin:$apr1$76boeqjp$tVQUCSSQky300AS.kgbZX/" # admin:password
# Only use secure ciphers - https://ssl-config.mozilla.org/#server=traefik&version=2.9&config=intermediate&guideline=5.6
tls:
options:
default:
#sniStrict: true # prevents leaking default cert; see https://doc.traefik.io/traefik/v2.2/https/tls/#strict-sni-checking
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- use ssh and
- cd /share/Container/container-station-data/application/traefik
- touch .env
- put your dns credentials into this file, e.g.
DESEC_TOKEN=’123456789abcdefgh’ - reference
- try to start traefik
- cd /share/Container/container-station-data/application/traefik
- docker compose up -d
- docker compose logs -f
(ctrl + c to stop log viewer) - on the first run it will take a while until traefik gets certificates from Let’s Encrypt
- open https://YOURDOMAIN.TLD
Install Dockhand
- use ssh and
- cd /share/Container/container-station-data/application/
- mkdir dockhand
- cd dockhand
- touch .env
- use mc editor to write into file .env
DATA_DIR=/share/Container/container-station-data/application/dockhand
STACKS_DIR=/share/Container/container-station-data/application - touch docker-compose.yml
and enter the code below into file docker-compose.yml - docker compose up -d
- docker compose logs -f
- open https://dockhand.YOURDOMAIN.TLD and add environment “/var/run/docker.sock”
- use Dockhand to create other (docker compose) Stacks
services:
dockhand:
env_file:
- path: /share/Container/container-station-data/application/dockhand/.env
image: fnsys/dockhand:latest
container_name: dockhand
restart: unless-stopped
user: 0:0
ports:
- 127.0.0.1:3000:3000
# use https://dockhand.YOURDOMAIN.TLD
networks:
- traefik-net
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- dockhand_data:/app/data
- /share/Container/container-station-data/application/dockhand:/share/Container/container-station-data/application/dockhand
- /share/Container/container-station-data/application:/share/Container/container-station-data/application
labels:
- traefik.enable=true
- traefik.http.services.dockhand.loadbalancer.server.port=3000
# Assign middleware: default rate-limit,...
- traefik.http.routers.dockhand.middlewares=rate-limit@file
volumes:
dockhand_data: null
networks:
traefik-net:
external: true
First test case: hello world with nginx
- logged in by ssh
- cd /share/Container/container-station-data/application
- mkdir test1
- cd test1
- mkdir html
- echo “hello world” >html/index.html
- mkdir etcnginxconfd
- touch etcnginxconfd/test1.conf
- use mc to edit test1.conf:
server {
listen 80;
server_name: test1.YOURDOMAIN.TLD
index index.html;
root /usr/share/nginx/html;
}
- Use Dockhand / Stacks and create a new stack “test1”
- the content of compose.yaml is shown below
- “Create & Start” this stack
and open https://test1.YOURDOMAIN.TLD - Debugging:
- Dockhand / Stacks allows to view the logs
- Dockhand / Containers allows to open a shell inside a running container
- https://YOURDOMAIN.TLD: Traefik / HTTP Routers has to show
Host(`test1.YOURDOMAIN.TLD`) with Entrypoint webs
compose.yaml:
services:
test1:
image: nginx:alpine
# ports:
# - "127.0.0.1:8083:80"
# use https://test1.YOURDOMAIN.TLD
networks:
- traefik-net
environment:
- APP_ENV=${APP_ENV:-production}
volumes:
# - /share/YOURQNAPFOLDER/opt/test1/html:/usr/share/nginx/html:ro
- /share/Container/container-station-data/application/test1/html:/usr/share/nginx/html:ro
- /share/Container/container-station-data/application/test1/etcnginxconfd:/etc/nginx/conf.d
restart: unless-stopped
labels:
- traefik.enable=true
- traefik.http.services.test1.loadbalancer.server.port=80
# - traefik.http.routers.test1.rule=Host(`t1.YOURDOMAIN.TLD`) # override default == SERVICENAME
# Assign middleware: default rate-limit,...
- traefik.http.routers.test1.middlewares=rate-limit@file
- traefik.http.routers.test1.entrypoints=web,webs
networks:
traefik-net:
external: true
to be continued
Leave a Reply