+++ title = "Time to deploy our static blog" author = ["Elia el Lazkani"] date = 2021-07-10 lastmod = 2021-07-10 tags = ["docker", "dockerfile", "linux", "traefik", "nginx", "ssl", "letsencrypt"] categories = ["container"] draft = false +++ In the previous post, entitled "[Let's play with Traefik]({{< relref "let-s-play-with-traefik" >}})", we deployed _Traefik_ and configured it. We left it in a running state but we haven't _really_ used it properly yet. Let's put it to some good use this time around. ## Pre-requisites {#pre-requisites} This blog post assumes that you already have a generated static _website_ or _blog_. There are multiple tools in the sphere which allows you to statically generate your blog. You can find a list of them on the [Awesome Static Web Site Generators](https://github.com/myles/awesome-static-generators). Once we have the directory on disk, we can move forward. ## Components {#components} Let's talk components a tiny bit and see what we have and what we need. We already a _static site_. We can expose our _site_ using _Traefik_. We can also generate an _SSL certificate_ for the exposed _site_. What we don't have, is a way to _serve_ our _static site_. _Traefik_ is only a _reverse proxy_ server. A _reverse proxy_, sort of, routes into and out of sockets. These sockets could be open local ports, or they could, also, be other containers. ## Nginx {#nginx} That's where [_nginx_](https://nginx.org/) comes into the picture. > nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a > generic TCP/UDP proxy server, originally written by Igor Sysoev. We can find an _nginx_ docker image on [dockerhub](https://hub.docker.com/_/nginx). But, if we look around carefully we can see a section that mentions "_running nginx as a non-root user_". This led me to a small discovery which made me look for an alternative of that image. Luckily for us, _nginxinc_ also releases an _unprivileged_ version of that image under the name of [nginx-unprivileged](https://hub.docker.com/r/nginxinc/nginx-unprivileged). ### Configuration {#configuration} The _nginx_ docker image can be configured using a _template_ configuration file which can be mounted into the container. The configuration can include _variables_ which will be replaced by _environment variables_ we inject into the container. Let's look at an example configuration `default.conf.template`. ```cfg server { listen ${NGINX_BLOG_PORT}; server_name localhost; root /usr/share/nginx/html/${NGINX_BLOG_HOST}; location / { index index.html; try_files $uri $uri/ =404; } } ``` In the example above, we use `NGINX_BLOG_HOST` and `NGINX_BLOG_PORT` as _environment variables_ to be replaced in the _nginx_ configuration. ## Container {#container} After creating our _nginx_ configuration, we need to run an _nginx_ container and serve our blog to the users. In the [previous post]({{< relref "let-s-play-with-traefik" >}}), we used _docker-compose_ to deploy _Traefik_. We will continue with that and deploy our _nginx_ container alongside. ### docker-compose {#docker-compose} Before we go ahead and create another service in the _docker-compose_ file, let's talk a bit about what we need. We need to deploy an _unprivileged nginx_ container, first and foremost. We need to inject a few _environment variables_ into the container to be included in the _nginx_ templated configuration. We, also, need not forget to include the _labels_ required for _Traefik_ to route our container properly, and generate an _SSL certificate_. Finally, we need to mount both the _nginx configuration template_ and, of course, our _static blog_. Now let's head to work. ```yaml nginx: container_name: nginx image: nginxinc/nginx-unprivileged:alpine restart: unless-stopped mem_limit: 8m command: ["nginx", "daemon off;"] volumes: - "./blog/static/:/usr/share/nginx/html/blog:ro" - "./blog/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro" environment: - NGINX_BLOG_PORT=80 - NGINX_BLOG_HOST=blog.example.com labels: - "traefik.http.routers.blog-http.rule=Host(`blog.example.com`)" - "traefik.http.routers.blog-http.service=blog-http" - "traefik.http.services.blog-http.loadbalancer.server.port=80" - "traefik.http.routers.blog-http.middlewares=blog-main" - "traefik.http.middlewares.blog-main.chain.middlewares=frame-deny,browser-xss-filter,ssl-redirect" - "traefik.http.middlewares.frame-deny.headers.framedeny=true" - "traefik.http.middlewares.browser-xss-filter.headers.browserxssfilter=true" - "traefik.http.middlewares.ssl-redirect.headers.sslredirect=true" - "traefik.http.routers.blog-http.tls.certresolver=cloudflareresolver" ``` If we look at the _Traefik_ configuration we can see the following important configurations. `traefik.http.routers.blog-http.rule` : This configures the `hostname` _Traefik_ should be listening on for our _nginx_ container. `traefik.http.routers.blog-http.service` : This configures the _router_ to use our _service_. `traefik.http.services.blog-http.loadbalancer.server.port` : We configure the _service_ `port`. `traefik.http.routers.blog-http.middlewares` : We configure the _router_ to use our `middleware`. `traefik.http.middlewares.blog-main.chain.middlewares` : We configure all the `middleware` chain. `traefik.http.middlewares.ssl-redirect.headers.sslredirect` : We always redirect `http` to `https`. `traefik.http.routers.blog-http.tls.certresolver` : We configure the _resolver_ to use to generate our _SSL certificate_. We can also see our _static blog_ and the _nginx template_ being mounted as _read-only_ inside the container to their right paths. Finally, we verify that our `NGINX_BLOG_HOST` and `NGINX_BLOG_PORT` are configured correctly. ## Final steps {#final-steps} After putting everything in place, we do a quick last check that everything is correctly in place. Once we are satisfied with the results, we run ! ```shell docker-compose up -d ``` And we're good to go. If we point our `/etc/hosts` to our site, we can test that everything works. ```cfg 192.168.0.1 blog.example.com ```

Note

Replace `192.168.0.1` with your public server's IP address. This is an example of an IP unroutable on the internet.
If everything is configured properly, we should see our _site_ pop up momentarily. The _SSL certificate_ will fail for a few minutes until _Traefik_ is able to generate a new one and serve it. Give it some time. Once everything up and running, you can enjoy your _blog_ being served by _Traefik_ through an _nginx_ container. ## Conclusion {#conclusion} You can serve your _static blog_ with _Traefik_ and _nginx_ easily. Make sure to take the necessary measures to run container _safely_ and it should be easy as pie. _Traefik_ makes it possible to route to multiple containers this way, allowing us to add more services to the _docker-compose_ file. At the same time, _nginx_, with the _templating feature_, offers us another flexible way to serve a big variety of _static sites_. Using them in combination open a wide range of possibilities.