Deploying to Production with Traefik
So, you have your new app running locally and you want to show it off to the world. The next step is to get it deployed and in this article I will show you how to cover the following requirements easily using docker and Traefik.
- Use an edge terminating SSL proxy so your applications can just use http behind the proxy
- Support for http2 with no extra effort
- No hassle SSL certificate acquisition and renewal
- Minimum grade A security rating from Qualys SSL Labs
- Easily handle multiple applications through the same proxy
Prerequisites
You will need a public Linux VM (I am using Azure) with docker installed - for more info on how to do this follow the instructions in the Docker docs.
Traefik
I prefer to have my proxy in a separate git repository to my applications because then I can add whatever additional new applications I like in the future independent of the proxy (mostly).
So, firstly create a new git repository and an empty traefik directory.
> git init host-proxy
> cd host-proxy
> mkdir traefik
> cd traefik
Create a new file called traefik.yml
and paste in the configuration below (make sure to add your email address!).
log:
level: "WARN"
providers:
docker:
exposedByDefault: false
file:
directory: "/etc/traefik/dynamic"
entryPoints:
http:
address: ":80"
https:
address: ":443"
certificatesResolvers:
lets-encrypt:
acme:
storage: "/etc/traefik/acme.json"
email: [ENTER YOUR EMAIL ADDRESS HERE]
tlsChallenge: {}
This tells traefik where to look for any dynamic content, to listen on ports 80 and 443 and to use lets encrypt for SSL certificates. Notice the json file referenced here. You should create that file but leave it empty for now, you will need to grant read write permissions on your VM to this file later.
> touch acme.json
Now we need to create the dynamic configuration file that will force and configure SSL for all of your services.
> mkdir dynamic
> cd dynamic
Create the file https.yml
and paste the following content into it.
http:
routers:
force-https:
entryPoints:
- "http"
middlewares:
- "force-https"
- "testHeader"
rule: "HostRegexp(`{any:.+}`)"
service: "noop"
middlewares:
force-https:
redirectScheme:
scheme: "https"
testHeader:
headers:
frameDeny: true
sslRedirect: true
stsPreload: true
services:
# noop service, the URL will be never called
noop:
loadBalancer:
servers:
- url: "http://192.168.0.1"
tls:
options:
default:
minVersion: "VersionTLS12"
mintls13:
minVersion: "VersionTLS13"
This ensures that all http requests are forced to use https and to set the HSTS header which ensures that browsers will use https for all future requests. It also ensures that the server uses either the TLS 1.2 or 1.3 protocol and that the server will prefer it's ciphers over the clients.
Finally we need a docker-compose
file to finish up the magic.
> cd ../../
Create the file docker-compose.yml
and paste in the following.
version: '3.4'
services:
traefik:
image: traefik
restart: always
ports:
- '80:80'
- '443:443'
volumes:
- ./traefik:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock:ro
That's it for the proxy. Just push your repository to a remote source control, log on to your VM and pull the repository and compose it up.
> ssh my-remote-server
> git pull host-proxy
> cd host-proxy
Remember the acme.json
from earlier?
> chmod 600 acme.json
Finally...
> docker-compose up -d
If you docker ps
you should see your traefik proxy up and running.
Register your Application with Traefik
This part is now deceptively simple, all you need to do is to add the following labels to your application compose file and replace the {YOUR SERIVCE NAME HERE}
and {YOUR DOMAIN NAME HERE}
with your values.
version: '3.4'
# ~~SNIP~~
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.{YOUR SERIVCE NAME HERE}.rule=Host(`{YOUR DOMAIN NAME HERE}`, `www.{YOUR DOMAIN NAME HERE}`)'
- 'traefik.http.routers.{YOUR SERIVCE NAME HERE}.tls=true'
- 'traefik.http.routers.{YOUR SERIVCE NAME HERE}.tls.certresolver=lets-encrypt'
When you docker-compose up -d
your service traefik will automatically register it, ensure you have a valid SSL certificate from lets encrypt and start routing https traffic to your service.
Feel free to submit you url to Qualys SSL Labs and you should get an A rating.
If you curl https://yoururl.com
you will also see it's using http2.
You can add as many additional services as your server can handle and traefik will manage it all for you.
I will come back to traefik in another post on more of the awesome things it can do for you.
Full Example Application Compose File
version: '3.4'
services:
demo-app-mariadb:
image: mariadb
restart: always
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: shh-it's-secret
MYSQL_DATABASE: MyDatabase
MYSQL_USER: NoobUser
MYSQL_PASSWORD: lots-of-random-chars
volumes:
- /datadrive/demo-app-mariadb:/var/lib/mysql
demo-app:
image: myawesomereg/myawesomereg:demo-app
restart: always
build:
context: .
dockerfile: demo-app/Dockerfile
ports:
- "61405:80"
depends_on:
- "demo-app-mariadb"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- MYSQL_USERID=NoobUser
- MYSQL_PASSWORD=lots-of-random-chars
- MYSQL_SERVER=demo-app-mariadb
- MYSQL_DATABASE=MyDatabase
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.demo-app.rule=Host(`demo-app.co.uk`, `www.demo-app.co.uk`)'
- 'traefik.http.routers.demo-app.tls=true'
- 'traefik.http.routers.demo-app.tls.certresolver=lets-encrypt'