F118CfL
Hosting my own Netflix on an ancient Mac Mini 2011 3

(This is not really a guide or a tutorial, you may get some tips on how to setup a reverse proxy to access your server from your own domain, but if you’re here looking for a full blown media server setup guide, you’re in the wrong place)

I’m sure I’m not the first to have setup a little media server at home serving all kinds of, um, totally legal content that you obtained from the Internet. But accessing it outside of your home has always been something that I never got around to do.

Until this Tet holiday that is.

Since I had to spend like 4-5 days at my inlaws during these times, I knew Netflix alone ain’t gonna cut it, especially since I’ve pretty much watched everything that worth watching on it. I need to be able to access my quality contents that were sitting under my TV in my bedroom, a hundred kilometer away.

phimhub jelly
Jellyfin running perfectly on my inlaws’ smart TV

Anywho, enough rambling. You want to know how I set it up. Read on.

It’s simple really, I bought a used Mac Mini 2011 for around $40 back in 2018. It has a what I believe a 3rd gen core i5 and 6GB of DDR3 SODIMM RAM. I know it is a potato, but it consumes about 85W at full load so I can leave it running 24/7 instead of my Ryzen rig. Originally it only serves as a media player for my wife to watch movies on FPT Play, HayHayTV, PubVN, MotPhim, etc. on our cheapo Asanzo TV (some of these services only run on a browser so Kodi doesn’t cut it, need to be a fully functional OS). She only watches for around 2 hours each night, yet the thing is running 24/7, it would a complete waste of electricity if I don’t somehow find a way to make it useful for the rest of the day.

And that’s where Jellyfin came in. A few simple setups and you can now watch movies store on this little potato.

Basically there’s only 4 steps involved:

  1. Download, install and run Jellyfin server
  2. Populate your media library with whatever movies/TV shows you have
  3. Install Caddy and run the command below
  4. Point your domain DNS to your home’s public IP

I got the reverse proxy up, bind it to my domain WITH a valid SSL with a single command:

caddy reverse-proxy --from example.com --to 127.0.0.1:8096

(Check out Jellyfin documentation on Caddy if you need further information)

That’s it. You’re done!

Of course, since I’m running the server from my bedroom, there’s still the issue with dynamic IP address. Viettel is not exactly the most reliable ISP so I get random modem reboot every couple of days. Everytime it happens I’d need to log into CloudFlare and manually change the DNS to my new public IP address. That is a pain indeed.

Luckily there is a way to automate this process.

Running a cronjob to update your domain’s DNS every hour.

You’ll need a machine running 24/7 for this task. The most efficient way is to run it on your media server since it’s already doing this.

But efficient is not what I do 😛

I have an old Raspberry Pi (the original one) that is just begging to be useful. So that’s what I’ll use instead.

Basically, throw Raspbian on it, and setup a cronjob to run this shell script every few hours to update your DNS.

(The full guide can be found over at LetsWP, check them out, I only copy the script here for your convenience)

#!/bin/bash
# Cloudflare as Dynamic DNS
# From: https://letswp.io/cloudflare-as-dynamic-dns-raspberry-pi/
# Based on: https://gist.github.com/benkulbertis/fff10759c2391b6618dd/
# Original non-RPi article: https://phillymesh.net/2016/02/23/setting-up-dynamic-dns-for-your-registered-domain-through-cloudflare/

# Update these with real values
auth_email="[email protected]"
auth_key="global_api_key_goes_here" 
zone_name="example.com"
record_name="home.example.com"

# Don't touch these
ip=$(curl -s http://ipv4.icanhazip.com)
ip_file="ip.txt"
id_file="cloudflare.ids"
log_file="cloudflare.log"

# Keep files in the same folder when run from cron
current="$(pwd)"
cd "$(dirname "$(readlink -f "$0")")"

log() {
    if [ "$1" ]; then
        echo -e "[$(date)] - $1" >> $log_file
    fi
}

log "Check Initiated"

if [ -f $ip_file ]; then
    old_ip=$(cat $ip_file)
    if [ $ip == $old_ip ]; then
        log "IP has not changed."
        exit 0
    fi
fi

if [ -f $id_file ] && [ $(wc -l $id_file | cut -d " " -f 1) == 2 ]; then
    zone_identifier=$(head -1 $id_file)
    record_identifier=$(tail -1 $id_file)
else
    zone_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" | grep -Po '(?<="id":")[^"]*' | head -1 )
    record_identifier=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json"  | grep -Po '(?<="id":")[^"]*')
    echo "$zone_identifier" > $id_file
    echo "$record_identifier" >> $id_file
fi

update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"name\":\"$record_name\",\"content\":\"$ip\"}")

if [[ $update == *"\"success\":false"* ]]; then
    message="API UPDATE FAILED. DUMPING RESULTS:\n$update"
    log "$message"
    echo -e "$message"
    exit 1 
else
    message="IP changed to: $ip"
    echo "$ip" > $ip_file
    log "$message"
    echo "$message"
fi

FAQ