edit: please see this post for most recent deployment instructions
Here's a docker-compose.yml file to deploy Node-RED and Traefik, a reverse proxy that automates fetching, issuing, and renewing free SSL certificates from Let's Encrypt.
There are two ways Lets Encrypt verifies that you are the owner of a domain so they can give you a free cert.
-
HTTP validation. Let's Encrypt gives you a token, and you put a file on your web server at http://<YOUR_DOMAIN>/.well-known/acme-challenge/token.... Lets Encrypt comes and checks that the token is there. Therefore, Let's Encrypt needs access to your server on port 80, which means you have to leave that port open and forwarded to your docker host.
-
DNS validation. Let's Encrypt doesn't need to access your IP at all. You run a piece of software that connects to your DNS host, and enters a temporary TXT record with a token. Lets Encrypt hits the DNS and looks for the record. After 5 mins the record expires.
By far, DNS validation is the best way, and the only way I recommend. Only use HTTP validation if for some reason you can't change your DNS registrar.
I have some domains bought on google domains, some bought on namecheap, some bought all over. Namecheap, for example, has an API that traefik can use. But it really doesn't matter what DNS provider you use... log into it and forward it to Cloudflare. Cloudflare works with Traefik, every time. So go there and make an account and enter in your domain and follow the instructions you see. For now leave it on "DNS ONLY".
So, here are the requirements:
- You own a domain that you have either pointed to cloudflare for the dns method, or otherwise pointed to your public IP.
- If using cloudflare, you have a wildcard 'A' record pointing to your public IP, and have a zone API key and main API key
- If HTTP validation, port 80 and 443 are forwarded to your docker host
- You have a docker host ready and have deployed swarm mode (you only need one host)
- You have created a docker overlay network named 'traefik-public'
once those requirements are met, ssh into your docker host, and create a file called nodered.yml with the below contents (fill in all the variables)
[user@docker1 ~]$ docker stack deploy -c nodered.yml nodered
## nodered.yml
## node red + traefik v2 stack
## create traefik overlay network first
## forward port 80 to your swarm master node if using http challenge
## forward port 443 to your swarm for remote access
## no port forwards needed if using DNS challenge unless you need remote access
## node red will be available at https://nodered.yourdomain.tld
## deploy to a docker swarm, single node or more
services:
version: '3.7'
# TRAEFIK 2.2
# DO NOT USE LATEST
services:
traefik:
image: traefik:v2.2
command:
- "--log.level=DEBUG" #CHANGE TO INFO AFTER SUCCESS
- "--global.sendAnonymousUsage=true"
- "--accessLog=true"
- "--accessLog.filePath=/var/log/docker/traefik.log"
- "--accessLog.bufferingSize=100"
- "--metrics.influxdb=false"
- "--serversTransport.insecureSkipVerify=false"
- "--api.dashboard=true"
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=traefik-public"
- "--providers.file.directory=/etc"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80" #port 80 ingress optional or if you need http challenge
- "--entrypoints.websecure.address=:443" #port 443 ingress if you want remote access
# PICK ONE TYPE OF SSL CHALLENGE
# 1 LETSENCRYPT HTTP CHALLENGE
# - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
# - "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=web"
# LETSENCRYPT OPTIONS
# - "--certificatesresolvers.letsencryptresolver.acme.email=your@email"
# - "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
# - "--certificatesresolvers.letsencryptresolver.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory"
# 2 CLOUDFLARE DNS CHALLENGE
- "--certificatesresolvers.letsencryptresolver.acme.email=your@email"
- "--certificatesresolvers.letsencryptresolver.acme.dnschallenge=true"
- "--certificatesresolvers.letsencryptresolver.acme.dnschallenge.provider=cloudflare"
- "--certificatesresolvers.letsencryptresolver.acme.dnschallenge.delaybeforecheck=90"
- "--certificatesresolvers.letsencryptresolver.acme.dnsChallenge.resolvers=1.1.1.1:53,1.0.0.1:53"
- "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
# USE THE STAGING SERVER UNTIL YOU GET ISSUED A LETSENCRYPT STAGING CERT, THEN COMMENT BELOW LINE AND RUN AGAIN
- "--certificatesresolvers.letsencryptresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
# FOR CLOUDFLARE DNS CHALLENGE
environment:
- CF_API_EMAIL=your@email
- CF_DNS_API_TOKEN=your_cloudflare_api_token
- CF_ZONE_API_TOKEN=your_cloudflare_zone_token
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
volumes:
- traefik-certificates:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik-public
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
#global redirect to https
labels:
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
# NODE-RED
nodered:
image: nodered/node-red:latest-12
volumes:
- nodered:/data
environment:
- TZ=America/Vancouver
networks:
- traefik-public
- bridge
deploy:
replicas: 1
restart_policy:
condition: on-failure
labels:
- "traefik.enable=true"
- "traefik.http.routers.nodered.rule=Host(`nodered.yourdomain.tld`)" #insert your domain here
- "traefik.http.routers.nodered.entrypoints=websecure"
- "traefik.http.routers.nodered.tls=true"
- "traefik.http.routers.nodered.tls.certresolver=letsencryptresolver"
- "traefik.docker.network=traefik-public"
- "traefik.http.services.nodered.loadbalancer.server.port=1880"
- "traefik.http.middlewares.nodered.headers.SSLRedirect=true"
- "traefik.http.middlewares.nodered.headers.STSSeconds=315360000"
- "traefik.http.middlewares.nodered.headers.browserXSSFilter=true"
- "traefik.http.middlewares.nodered.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.nodered.headers.forceSTSHeader=true"
- "traefik.http.middlewares.nodered.headers.STSIncludeSubdomains=true"
- "traefik.http.middlewares.nodered.headers.STSPreload=true"
- "traefik.http.middlewares.nodered.headers.frameDeny=true"
volumes:
nodered:
traefik-certificates:
networks:
traefik-public:
external: true
bridge:
external: true
your reward: