Archive for October, 2026

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
  • 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
#!/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:

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