Skip to content

Custom Cloudflare DNS Update

  • Workflow runs every 30 min via self-hosted github runner, octorunner
  • GitHub secrets passed through the workflow to the script via environment vars
  • environment vars configured in github
  • secrets held in 1Password and used in GitHub Actions
  • API Token NOT API Key is used to initialize the client in the script
  • Token permissions: All zones - DNS:Read, DNS:Edit
Workflow
name: Monitor IP and Update Cloudflare

on:
  schedule:
    # Run the workflow every 30 minutes
    - cron: "*/30 * * * *"
  workflow_dispatch:

jobs:
  monitor-ip:
    runs-on: self-hosted

    steps:
      # Checkout the repository
      - name: Checkout repository
        uses: actions/checkout@v4

      # Set up Python
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.x"

      # Install dependencies
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install python-dotenv requests cloudflare

      # Run the script
      - name: Run monitor script
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_API_EMAIL: ${{ secrets.CLOUDFLARE_API_EMAIL }}
          CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
          CLOUDFLARE_DNS_RECORD_ID: ${{ secrets.CLOUDFLARE_DNS_RECORD_ID }}
        run: |
          python automation/ddns/cloudflare-update.py
Script
import sys
import requests
from cloudflare import Cloudflare
import os
from dotenv import load_dotenv



def get_public_ip():
    """Fetch the public IP address."""
    try:
        response = requests.get("https://api.ipify.org", timeout=10)
        response.raise_for_status()
        return response.text
    except requests.RequestException as e:
        print(f"Error fetching public IP: {e}")
        sys.exit(1)

def update_cloudflare_record(ip, api_token, zone_id, dns_record_id):
    """Monitor IP changes and edit Cloudflare."""
    client = Cloudflare(
        api_token=api_token
    )
    """Edit the Cloudflare DNS record."""
    try:
        # Edit the DNS record
        response = client.dns.records.edit(
            dns_record_id=dns_record_id,
            zone_id=zone_id,
            content=ip
        )
        print(f"Successfully updated DNS record to IP")
    except Exception as e:
        print(f"Error editing Cloudflare DNS record: {e}")
        sys.exit(1)

def main():
    # Check for a flag to determine how to load environment variables
    if len(sys.argv) > 1 and sys.argv[1] == "--use-env-file":
        print("Loading environment variables from .env file...")
        load_dotenv()

    # Configuration from environment variables
    CLOUDFLARE_API_TOKEN = os.getenv("CLOUDFLARE_API_TOKEN")
    CLOUDFLARE_API_EMAIL = os.getenv("CLOUDFLARE_API_EMAIL")
    CLOUDFLARE_ZONE_ID = os.getenv("CLOUDFLARE_ZONE_ID")
    CLOUDFLARE_DNS_RECORD_ID = os.getenv("CLOUDFLARE_DNS_RECORD_ID")

    if not CLOUDFLARE_API_TOKEN or not CLOUDFLARE_ZONE_ID or not CLOUDFLARE_DNS_RECORD_ID:
        print("Missing required environment variables.")
        sys.exit(1)

    # Fetch the current public IP address
    current_ip = get_public_ip()

    if current_ip:
        print(f"Detected IP")
        # Update the Cloudflare DNS record
        update_cloudflare_record(
            current_ip,
            CLOUDFLARE_API_TOKEN,
            CLOUDFLARE_ZONE_ID,
            CLOUDFLARE_DNS_RECORD_ID
        )

if __name__ == "__main__":
    main()