For this project I started working with a MacMini M1 I had lying around. Once the computer has a user logged in, you can login with that user using macOS's Screen Sharing app on another computer for a "headless" no monitor setup. This isn't ideal for production, but if you just want to play around, it's fine. Now, you need to connect that computer to a domain.
- Get a domain name
- Follow the steps on Cloudflare to add a domain (I prefer Cloudflare over services like no-ip). Create a Cloudflare account and set the nameservers on your domain to the ones provided by Cloudflare which also copies your existing DNS settings.
- Find your local machine's local IP address, login to your router and forward 443 and 80 to that local machine. You may also want to forward 22 if you plan to interact with the server over ssh in the future.
- Set up DDNS so when your local IP changes it will be synced with Cloudflare.
Here are the basic steps to set up DDNS on a Mac (you may run into errors in your own situation, and there's
another method I know of that uses Rails itself to automate the IP updates):
If you don't have a favorite default terminal text editor, don't do the next step, but I usually do this on a fresh OSX install because I like nano. If you don't do this, but you like nano like me, you'll run into a problem at the crontab step which uses the system default editor.
export EDITOR=nano
create the file and edit it
nano ~/script_name.sh
enter this script and save
#!/bin/bash
#
# script_name.sh - Updates the single A record for moab.jp in Cloudflare to match the current public IP.
# ========= USER CONFIGURATION =========
CF_TOKEN="YOUR_CLOUDFLARE_API_TOKEN_HERE"
ZONE_ID="YOUR_ZONE_ID_HERE" # The zone ID for moab.jp
RECORD_NAME="your.domain" # Or a subdomain like "home.your.domain
# =====================================
# 1) Get the current public IP
CURRENT_IP=$(curl -s ifconfig.me)
# 2) Find the record ID for the single A record named RECORD_NAME in this zone
LOOKUP_RESPONSE=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?type=A&name=${RECORD_NAME}" \
-H "Authorization: Bearer ${CF_TOKEN}" \
-H "Content-Type: application/json")
# Extract the record ID from the JSON response (assuming exactly one result)
RECORD_ID=$(echo "$LOOKUP_RESPONSE" | /usr/bin/env python3 -c "
import sys, json
data = json.load(sys.stdin)
results = data.get('result', [])
print(results[0]['id'] if results else '')")
if [ -z "$RECORD_ID" ]; then
echo "Error: Could not find an A record for ${RECORD_NAME} in zone ${ZONE_ID}."
echo "Response was: $LOOKUP_RESPONSE"
exit 1
fi
# 3) Update that DNS record with the new IP
UPDATE_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
-H "Authorization: Bearer ${CF_TOKEN}" \
-H "Content-Type: application/json" \
--data "{
\"type\": \"A\",
\"name\": \"${RECORD_NAME}\",
\"content\": \"${CURRENT_IP}\",
\"ttl\": 120,
\"proxied\": true
}")
# 4) Print the Cloudflare update response
echo "Cloudflare update response: $UPDATE_RESPONSE"
chmod this file, with
chmod +x script_name.sh
test it
./script_name.sh
then modify your crontab `crontab -e` and add this line
*/10 * * * * /Users/yourname/path/to/script_name.sh >> /Users/yourname/path/to/script_name.log 2>&1
At this point you should be able to run a server in production and it will show up at the domain.
# create the blog
rails new blog
# run the server in production with bindings
sudo RAILS_ENV=production rails server -b your_local_ip -p 80