Overview

This site is self-hosted on a Raspberry Pi 5 sitting on a desk at home. Traffic routes through a Cloudflare Tunnel — no ports are forwarded, the public IP is never exposed, and HTTPS is handled automatically. Total cost: ~$10/year for the domain. The Pi is hardwired to the switch (no Wi-Fi) and runs off a UPS, so a brief power blip or brownout won’t drop the site or risk an SD-card corruption mid-write.

ComponentDetail
HardwareRaspberry Pi 5
NetworkWired Ethernet — USW port 13 (static 10.0.0.20)
PowerUPS-backed (ride-through on outages)
OSRaspberry Pi OS (Debian/arm64)
Web ServerNginx
TunnelCloudflare Tunnel (cloudflared)
Domainthe-arcanum.com (Cloudflare Registrar)
SSL/TLSCloudflare Edge (automatic)
Site TypeStatic HTML/CSS/JS — no frameworks
Port ForwardingNone required

Step 1 — Install Nginx

# On the Pi
sudo apt install nginx -y
sudo mkdir -p /var/www/yoursite
sudo chown $USER:$USER /var/www/yoursite

Edit the Nginx config to point at your site folder:

sudo nano /etc/nginx/sites-available/default
# Change the root line to:
root /var/www/yoursite;
sudo systemctl restart nginx

Step 2 — Domain & Cloudflare

  1. Create a free account at dash.cloudflare.com
  2. Buy a domain through Cloudflare Registrar (~$10/year for a .com)
  3. DNS is automatically configured when you buy through Cloudflare

Step 3 — Install cloudflared

# Download for ARM64 (Pi 5)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb

# Verify
cloudflared --version

Step 4 — Authenticate & Create Tunnel

# Opens a browser URL to authorize
cloudflared tunnel login

# Create the tunnel (note the UUID it returns)
cloudflared tunnel create my-tunnel

Step 5 — Route DNS

# Points your domain at the tunnel
cloudflared tunnel route dns my-tunnel yourdomain.com

Step 6 — Configuration

nano ~/.cloudflared/config.yml
tunnel: YOUR-TUNNEL-UUID
credentials-file: /home/youruser/.cloudflared/YOUR-TUNNEL-UUID.json

ingress:
  - hostname: yourdomain.com
    service: http://localhost:80
  - service: http_status:404

Step 7 — Test

cloudflared tunnel run my-tunnel

Visit https://yourdomain.com — if the site loads, proceed to make it permanent.

Step 8 — Install as Service

# Copy config to system path
sudo mkdir -p /etc/cloudflared
sudo cp ~/.cloudflared/config.yml /etc/cloudflared/config.yml
sudo cp ~/.cloudflared/*.json /etc/cloudflared/

# Install and enable
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared

The tunnel now runs in the background and survives reboots.

Step 9 — Deploy

From your local machine:

scp your-files/* [email protected]:/var/www/yoursite/

Optional — SSH Keys (No Password)

# Generate a key (give it a unique name)
ssh-keygen -t ed25519 -f ~/.ssh/id_mysite

# Copy to the Pi
ssh-copy-id -i ~/.ssh/id_mysite [email protected]

Add to ~/.ssh/config:

Host yourpi.local
    IdentityFile ~/.ssh/id_mysite
    User youruser

Now scp and ssh never ask for a password.

Result

← back to the lab