self hosting without port forwarding using cloudflare zero trust tunnels

Contents:

  1. Pros and Cons to using Cloudflare
  2. Costs
  3. Setting up the web server
  4. Setting up Cloudflare
  5. Restricting access

First and foremost, I want to preface this post by listing out the pros and cons as there are some trade offs that should be taken into consideration when using a MITM service like Cloudflare.

Perks:

Trade Offs:

COSTS

In a nutshell: ~10 USD per year

For the purpose of the demonstration, i’m going to use a Raspberry Pi 5. It was a one time purchase and costed around 90 USD however, depending on traffic, you could get a much cheaper pi (such as the pi zero 2 W which costs 15 USD).

When testing the Pi5, it consumed between 2.5-6W, depending on the things it was running and the traffic of the webserver.

Calculating the power usage: 5 W / 1000 = 0.005 kW 0.005 kW x 24 h = 0.12 kWh / day 0.005 kW x 730 h = 3.65 kWh / month 0.005 kW x 8760 h = 43.8 kWh / year

Calculating the cost: (My power rates are around 0.15 USD per kWh) 0.12 kWh x 0.15 $ = 0.018 USD per day 3.65 kWh x 0.15 $ = 0.54 USD per month 43.8 kWh x 0.15 $ = 6.57 USD per year (I also get ‘free’ power between certain times so the cost is probably much less.)

You’ll also need a domain. The costs can vary but my current one costs around 5 USD per year through PorkBun.

Overall, there was a base purchase of ~15 to 100 USD and a yearly cost of around 10 USD (Domain + Power costs). A yearly subscription for an average VPS would also cost around 10 USD (and sometimes they have really good deals so be sure to look out for that) so that is something to consider.

Some pros to using a VPS are that they typically are extremely reliable, with 99.9% uptime, as opposed to self hosting

SETTING UP THE WEBSERVER

In a nutshell:

Note: I will be using the command line to do all of this. Please let me know in the comments if you’re having trouble with anything.

  1. Choosing an OS The first thing to do is pick the machine you will be using. As stated above, I shall be using the Pi 5, running RaspbianOS. These steps should be relatively similar for other debian based systems.
  2. Installing Docker Install docker using one of the below commands. Make sure your system is compatible with Docker Raspbian, Ubuntu, Debian - sudo apt install docker, sudo apt upgrade && update to upgrade and update everything.
  3. Create index First, create a folder called “webserver”. I will be cloning my github repo into a folder called “site” (you can too, if you’ve already got a website created). The most important file to have is some sort of index (index.html, index.php etc), as that’s what we’ll see when we open, so create an index.php file inside the ‘site’ folder and add the below into it:
<?php
phpinfo();
  1. Within the ‘webserver’ folder, create another folder called “docker”. Add a file called “docker-compose.yml”, with the following contents:
version: "3.9"
services:
  nginx:
    image: nginx
# if youd like to change the port, change 8080 to something else
    ports:
      - '8080:80'
    restart: unless-stopped
    volumes:
      - ../site:/var/www/html
      - ./default.conf:/etc/nginx/conf.d/
    depends_on:
      - php
  php:
    image: php:7.3-fpm-alpine
    restart: unless-stopped
    volumes:
     - ../site:/var/www/html
  cloudflared:
    image: cloudflare/cloudflared 
    container_name: cloudflare-tunnel 
    restart: unless-stopped 
    command: tunnel run 
# You put your tunnel token here. We setup cloudflare in the next section.
    environment: 
      - TUNNEL_TOKEN=TUNNELTOKEN

The restart: unless-stopped` line tells docker to always restart the container if it wasn’t manually stopped. This means that it will restart upon system boot.

  1. Create a file called “nginx.config” within the docker folder that has the following contents:
server {
    index index.php index.html;
    server_name php-docker.local;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/html;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}
  1. Run the docker compose file using docker compose up -d. The -d flag makes sure it runs in the background. You should get confirmation that both nginx and php are running, however the cloudflare container will return an error as we haven’t set it up yet.
  2. Obtain the IP address of the machine you’re using. Enter that into the browser, followed by the port you set in the docker compose file (in this case, 8080, so i would visit 192.168.1.80:8080)

This is what your file setup should look like:

SETTING UP CLOUDFLARE

In a nutshell:

  1. You first need to add a domain to cloudflare. This varies from provider to provider, but you’ll need to add the nameservers that cloudflare provides for you. If you dont know how to do that, search for the name of the site you purchased your domain from + changing nameservers.
  2. Create Tunnel Create a tunnel by returning to the dashboard. Click “Zero Trust”, and then “Networks”. Create a tunnel, and select “Cloudflared” as your type. Name your tunnel anything you wish, and press save. You’ll be prompted to choose your environment, to which you should choose “Docker” and copy the command they give you. This was my one: docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoiZ...
  3. Update docker-compose.yml file Isolate the token (everything to the right of --token) and paste it into your docker-compose.yml file.
environment: 
      - TUNNEL_TOKEN= eyJhIjoiZDgyNDc3OTg1OTBkZjIzZjgwNzg5ZThjM2JmM2JjMTkiLCJ0IjoiYzFiMmVkOTAtYmJjNi00ODRmLWFiNWYtN2NjMTIxYTczZjkyIiwicyI6Ik5qTm1Oamd4WkRNdE5EVmpOUzAwTkRJeUxUZzBNVEV0TURZeU56UXhOV1pqTVdVdyJ9 
  1. Save your compose file. Run docker compose down to shut down your containers and re-run docker compose up -d. If everything worked, you should see success in all containers.
  2. Add a public hostname to the cloudflare tunnel and save.