Setup services on your VPS with Nginx and Docker

4 minute read

This article is a detailed explanation on how to reproduce the configuration I’ve made on my VPS to easily deploy services (such as a blog, a ctf platform, webapps etc.).

The VPS Architecture

The VPS is hosted by OVH and shipped with a nice Ubuntu Server.

What we are going to realize is this architecture :

VPS ARCHITECTURE

N.B the services described here are examples of what I could deploy, ctf.pwnh4 and webapp.pwnh4 may not be up when you read this

We can see here the VPS Ubuntu Server on which a nginx server is running. On the server, multiple dockers are running and expose different ports. The nginx server installed on the VPS plays the role of a reverse proxy by redirecting the incoming traffic to the correct service (ex : pwnh4.com is redirected to the Docker on which my blog is hosted). The nginx server also handles all the HTTPS certificates for the services.

Before starting any configuration on the vps, I modify the domain name pwnh4.com dns zone “A” field to the VPS IP address.

Setting up a service with Docker

In this part, I am going to show all the creation process of my blog Docker service.

I use Hugo, a fast static website generator written in Go. I recommend it as it is really easy to configure and has great performances.

I use a multi-stage Dockerfile described in this article to host my hugo blog on a nginx+alpine server. I let you check this article out to see more about the Dockerfile configuration.

This docker is then built run with :

docker build -t hugo
docker run -p 1234:80 hugo &

You can see that here I host my blog on localhost:1234.

Now that the service is up, we have to configure nginx to redirect the traffic targeting pwnh4.com to this docker and add an HTTPS certificate.

Nginx Configuration

First of all, install nginx :

apt-get update
apt-get install nginx

To reverse proxy to our blog service, we need to setup a virtual host for pwnh4.com. To add a virtual host, we need to add a configuration file like this in /etc/nginx/sites-available :

server {
        listen          80;
        server_name     pwnh4.com;
        return          301 https://$server_name$request_uri;
}

server {
        listen          443 ssl;
        server_name     pwnh4.com;

        location / {
                proxy_pass              http://localhost:1234/;
                proxy_set_header        Host $host;
                proxy_set_header        X-Forwarder-For $proxy_add_x_forwarded_for;
                proxy_connect_timeout   10;
                proxy_send_timeout      10;
        }
}

In this configuration file, we simply tell nginx to redirect all requests targeting pwnh4.com to localhost:1234. We also add a redirection to force HTTPS (we will configure it in a moment).

Then we need to enable this new virtual host :

ln -s /etc/nginx/sites-available/pwnh4.com /etc/nginx/sites-enabled/pwnh4.com

We can now test this nginx setup and see if some errors show up :

nginx -t && nginx -s reload

Nginx doesn’t find any error, we can now setup the HTTPS.

Time to setup HTTPS with LetsEncrypt

LetsEncrypt is a free and automated Certificate Authority which makes it really convenient to use. This organization provides a script that will automatically setup the certificate for us. So let’s install it and run it for our pwnh4.com service :

add-apt-repository ppa:certbot/certbot
apt-get update
apt-get install python-certbot-nginx
certbot --nginx -d pwnh4.com -d www.pwnh4.com

N.B. certbot is not fully functionnal with nginx servers. If the creation step fails, you’ll have to create a default virtualhost with this configuration :

$ cat /etc/nginx/sites-available/default

server {
   listen 80 default_server;
   server_name _;
   root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
        location / {
                try_files $uri $uri/ =404;
        }
}

We can now see that the cerbot added some lines to our pwnh4.com virtualhost configuration :

server {
        listen          443 ssl;
        server_name     pwnh4.com;

        location / {
                proxy_pass              http://localhost:1234/;
                proxy_set_header        Host $host;
                proxy_set_header        X-Forwarder-For $proxy_add_x_forwarded_for;
                proxy_connect_timeout   10;
                proxy_send_timeout      10;
        }

    ssl_certificate /etc/letsencrypt/live/pwnh4.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/pwnh4.com/privkey.pem; # managed by Certbot
}

A nice trick to not have to handle the certificate renewal every 90 days is to add a crontab job which checks every day if the certificates you have need to be updated and update them if necessary :

0 12 * * * /usr/bin/certbot renew >> /var/log/letsencrypt/renewal.log

We can now successfully access our Hugo blog through HTTPS via pwnh4.com ! If we want to add another service, we just have to follow those steps. For example with the webapp.pwnh4.com described in the Architecture schema : * build and run the docker container and root its 80 port to 3456 * create a new nginx virtualhost for webapp.pwnh4.com and redirect https traffic to localhost:3456 * run certbot to deliver a certificate for webapp.pwnh4.com * enjoy !

After that ?

A nice way to improve this workflow would be to automate services deployment with Gitlab CI/CD for example. It would be amazing to just push a new Dockerfile and see it automatically built, run and HTTPS certified ! If you have any advice / useful resources on that, please contact me !