Time to deploy our static blog

4 minute read

In the previous post, entitled “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.


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.

Once we have the directory on disk, we can move forward.


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.


That’s where nginx 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. 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.


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.

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.


After creating our nginx configuration, we need to run an nginx container and serve our blog to the users.

In the previous post, we used docker-compose to deploy Traefik. We will continue with that and deploy our nginx container alongside.


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.

    container_name: nginx
    image: nginxinc/nginx-unprivileged:alpine
    restart: unless-stopped
    mem_limit: 8m
    command: ["nginx", "daemon off;"]
      - "./blog/static/:/usr/share/nginx/html/blog:ro"
      - "./blog/nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro"
      - NGINX_BLOG_PORT=80
      - NGINX_BLOG_HOST=blog.example.com
      - "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.

This configures the hostname Traefik should be listening on for our nginx container.
This configures the router to use our service.
We configure the service port.
We configure the router to use our middleware.
We configure all the middleware chain.
We always redirect http to https.
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

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 !

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. blog.example.com


Replace 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.


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.