▄▄▄▄    ▒█████  ▒██   ██▒  █████▒▒█████   ██▀███  ▄▄▄█████▓      █    ██  ██ ▄█▀
█████▄ ▒██▒  ██▒▒▒ █ █ ▒░▓██   ▒▒██▒  ██▒▓██ ▒ ██▒▓  ██▒ ▓▒      ██  ▓██▒ ██▄█▒ 
██▒ ▄██▒██░  ██▒░░  █   ░▒████ ░▒██░  ██▒▓██ ░▄█ ▒▒ ▓██░ ▒░     ▓██  ▒██░▓███▄░ 
██░█▀  ▒██   ██░ ░ █ █ ▒ ░▓█▒  ░▒██   ██░▒██▀▀█▄  ░ ▓██▓ ░      ▓▓█  ░██░▓██ █▄ 
▓█  ▀█▓░ ████▓▒░▒██▒ ▒██▒░▒█░   ░ ████▓▒░░██▓ ▒██▒  ▒██▒ ░  ██▓ ▒▒█████▓ ▒██▒ █▄
▒▓███▀▒░ ▒░▒░▒░ ▒▒ ░ ░▓ ░ ▒ ░   ░ ▒░▒░▒░ ░ ▒▓ ░▒▓░  ▒ ░░    ▒▓▒ ░▒▓▒ ▒ ▒ ▒ ▒▒ ▓▒
░▒   ░   ░ ▒ ▒░ ░░   ░▒ ░ ░       ░ ▒ ▒░   ░▒ ░ ▒░    ░     ░▒  ░░▒░ ░ ░ ░ ░▒ ▒░
░    ░ ░ ░ ░ ▒   ░    ░   ░ ░   ░ ░ ░ ▒    ░░   ░   ░       ░    ░░░ ░ ░ ░ ░░ ░ 
░          ░ ░   ░    ░             ░ ░     ░                ░     ░     ░  ░   
     ░                                                       ░                  
        

fuck ai!


        

        
 

> setting up my home server

> 2026-06-17 16:32:50 UTC

 

After fantasizing and umm-ing and ahh-ing for a while I decided to take the plunge and buy myself a wee home server to play around with and attempt to self host my own website (and video game servers).

 

I went on Ebay and found myself a used Dell Optiplex 7090 Micro for sale for £250 and snatched it up. A couple days later I had it in my hands and was quickly wiping the nasty Windows install from it and replacing it with Ubuntu Server 24.04. I've had plenty experience installing Linux distros so in no time at all it was alive.

 
                             ....              boxfort@boxfort-server
              .',:clooo:  .:looooo:.           ----------------------
           .;looooooooc  .oooooooooo'          OS: Ubuntu 24.04.4 LTS (Noble Numbat) x86_64
        .;looooool:,''.  :ooooooooooc          Host: OptiPlex 7090
       ;looool;.         'oooooooooo,          Kernel: Linux 6.8.0-111-generic
      ;clool'             .cooooooc.  ,,       Uptime: 33 days, 22 hours, 40 mins
         ...                ......  .:oo,      Packages: 551 (dpkg)
  .;clol:,.                        .loooo'     Shell: bash 5.2.21
 :ooooooooo,                        'ooool     Terminal: /dev/pts/1
'ooooooooooo.                        loooo.    CPU: Intel(R) Core(TM) i5-10500T (12) @ 3.80 GHz
'ooooooooool                         coooo.    GPU: Intel UHD Graphics 630 @ 1.15 GHz [Integrated]
 ,loooooooc.                        .loooo.    Memory: 3.35 GiB / 7.49 GiB (45%)
   .,;;;'.                          ;ooooc     Swap: 2.50 MiB / 4.00 GiB (0%)
       ...                         ,ooool.     Disk (/): 100.26 GiB / 231.17 GiB (43%) - ext4
    .cooooc.              ..',,'.  .cooo.      Local IP (wlp0s20f3): 192.168.0.60/24
      ;ooooo:.           ;oooooooc.  :l.       Locale: C.UTF-8
       .coooooc,..      coooooooooo.           
         .:ooooooolc:. .ooooooooooo'                                   
           .':loooooo;  ,oooooooooc                                    
               ..';::c'  .;loooo:'

boxfort-server fastfetch output

 

Once I had my new shiny OS installed, what any real server needs remote access! It was currently on my desk with a keyboard and monitor plugged into it which is very un-server-like, so I got to setting up SSH access as the first port of call.

 

Firstly, I made sure that we were not allowing password authentication for extra safety by going to /etc/ssh/sshd_config and setting PasswordAuthentication no. This means that we can only SSH into the server with an allowed SSH key but now we need to generate one. This is simple enough, on the machine you wish to access the server from you run:

 
	ssh-keygen -t ed25519 # generate the SSH keys
	ssh-add ~/.ssh/id_ed25519 # register the private key with ssh-agent
  

which after following the steps in the command will generate you a public/private key pair at ~/.ssh/id_ed25519.pub and ~/.ssh/id_ed25519 respectively. The private key should be kept on whichever machine you want to access the server from, and then the contents of the public key can be copied onto the server into ~/.ssh/authorized_keys and voila you can SSH into your server.

 

Finally, we set up a firewall on the machine (which is entirely unnecessary as I won't be exposing this machine to the internet through traditional means) but good practice anyhow. We do this by installing ufw which will by default disallow traffic on all ports, and then allow incoming traffing on port 22 (SSH):

	sudo apt install ufw # install firewall
	sudo ufw allow ssh # allow ssh traffic
    

This gives us a pretty secure method of accessing our little server, albeit only from the same network. I did float the idea of exposing SSH to the internet through the use of something like port knocking but reading about all of the horror stories and bot traffic I decided it was better to just leave it alone.

 

That was the easy part. I now had remote access to my home server and could SSH in from my main rig. Now comes the hard part, self hosting a website. This comes in several parts:

 

First thing, I purchased the boxfort.uk domain from NameCheap for 2 years for appoximately $15.00 (I can't attest to the quality of this service, I don't really know enough. It was just the cheapest and most convenient for me).

 

Then I signed up for an account with Cloudflare to make sure of thier free DNS service, and followed this handy guide to connect up the NameCheap domain with Cloudflare.

 

Once that was set up I then set up my Cloudflare tunnel and NGINX server. The cloudflare tunnel portion I managed by following this guide here . On the flipside, on the server I began setting up the user and docker config to run the tunnel and server.

 

To install docker I followed the official guide which culminates in installing the required packages with sudo apt install ...

 

To make sure that if something did go horribly wrong with this server setup, I created a new user to run the NGINX server and cloudflare tunnel with as few permissions as possible so that in the event something does go wrong the blast radius should be reduced.

 
	sudo useradd nginx -m # create the new user
	sudo usermod -aG docker nginx # allow the user to use docker
	sudo su -l nginx # switch to the 'nginx' user
 

We can now set up our ~/docker-compose.yml which will allow us to configure which containers we want to run and how:

 
	services:
	  cloudflared:
	    image: cloudflare/cloudflared:latest
	    restart: unless-stopped
	    command: "tunnel --no-autoupdate run --token <SECRET_TOKEN_HERE>"
	    extra_hosts:
	      - "hostmachine:172.19.0.1"
	    networks:
	      - internal_network
	  nginx:
	    image: nginx:latest
	    ports:
	      - 80:80
	    restart: unless-stopped
	    env_file: "./.env"
	    networks:
	      - internal_network
	    volumes:
	      - ./nginx/logs/:/var/log/
	      - ./nginx/conf/:/etc/nginx/conf.d/
	      - ./site/www/html/:/var/www/html/:ro
	networks:
	internal_network:
	  driver: bridge
  

docker-compose.yml

 

And before we spin up these containers we need to do two more things, configure NGINX and make a nice website landing page. We create a file at ~/nginx/conf/default.conf with the following contents:

 
	server {
	    listen 80;
	    listen [::]:80;
	    server_name boxfort.uk www.boxfort.uk;
	    server_tokens off;
	    disable_symlinks on;
	    client_max_body_size 20M;
	
	    location / {
	        try_files $uri $uri/ =404;
	        proxy_set_header Host $host;
	        proxy_set_header X-Forwarded-Host $server_name;
	        proxy_set_header X-Real-IP $remote_addr;
	        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	        proxy_read_timeout 90;
	        proxy_set_header X-Forwarded-Proto $scheme;
	    }
	}
 

The worst NGINX config you've ever seen.

 

We don't need to configure HTTPS routes as this is just intra-docker network traffic and all of the traffic coming through/from Cloudflare will be secure. Then at ~/site/www/html/index.html we have the following:

 
	<!DOCTYPE HTML>
	<html>
	  <head>
	    <title>hello world</title>
	  </head>
	  <body>
	    Hello world!
	  </body>
	</html>
  

The best HTML you've ever seen.

 

and at last spin up your containers with:

 
	docker-compose up
 

AND WE HAVE LIFTOFF! (given you've followed the linked guide and setup cloudflare correctly). This was a bit of a religious experience for me at this point, I had never hosted anything myself before and the amount of satisfaction it gave me was incredible. Something that always seemed a bit arcane and unreachable to me was finally in my grasp and I can do WHATEVER I want with it! The power!!

 

After spending a while feeling smug and riding the self hosting high, I then checked the NGINX logs. Almost immediately after bringing up the server and having it publicly reachable it was being hit with bot traffic. This was a bit terrifying to me, I knew that the internet is crawling with bot traffic but I didn't fully realise the enormity of it. In order to ease my terror, I went about putting in some precautions to prevent myself getting owned.

 

The first thing I did was set up Fail2Ban which is a bit of software designed to prevent brute-force attacks, and configured it to monitor the logs coming out of my NGINX server and IP ban any suspicous traffic through CloudFlare using this guide. The config I ended up with in the end is as follows:

 
	[nginx]
	backend = auto
	enabled = true
	port = 80,443
	protocol = tcp
	filter = nginx
	maxretry = 5
	bantime = 48h
	findtime = 2m
	logpath = /home/nginx/nginx/logs/nginx/access.log
	action = cloudflare
	iptables-allports[name=nginx, chain=DOCKER-USER]
	mail-whois-lines[name=nginx, dest=boxfort]
  

/etc/fail2ban/jail.d/nginx.local

 
	[Definition]
	failregex = ^.*\[.*\] "[^"]*" (401|403|404) .* [^"]* [^"]*" "<ADDR>"
  

/etc/fail2ban/filter.d/nginx.conf

 

What this config does is it looks through the NGINX logs for any IPs which have made a dodgy request 5 times in the last 2 minutes, and IP bans them for 48 hours in CloudFlare.

 

And the final thing I done was set up automatic updates on the server to ensure that I get any vulnerability fixes as soon as possible, and that I don't have to do it myself. I just set this up with some simple cron jobs like so:

 
	0 4 * * * (apt update && apt upgrade -y) 2>&1 
	15 4 * * * (docker compose -f "/home/nginx/docker-compose.yml" pull && 
	            "/home/nginx/docker-compose.yml" up -d && 
	            docker image prune -a -f) 2>&1 
 

And thats it! I'm sure there's loads I've done wrong or that I can do better with the setup of the web server but for now it works and I'm happy with it, and I've not had any issues so far (its hosting the website you're reading this on right now!). The actual creation of this site was done using Eleventy which is a static site generator, but I won't go through it as its not that interesting. I've also set up a few other bits and bobs on my home server like a Jellyfin server and a Vintage Story server but those are not too interesting either.

 

I hope that someone reading this decides to give self hosting a try and can maybe benefit from some of the rambling and links I've provided here!

 

Happy hosting!

      backrooms