Skip to main content

Command Palette

Search for a command to run...

Cloudflare automatic DNS update for homelab

Updated
Cloudflare automatic DNS update for homelab
N

HI there 馃憢

I'm Nir Adler, and I'm a Developer, Hacker and a Maker, you can start with me a conversation on any technical subject out there, you will find me interesting.

First of all, I love Cloudflare, I think they have a great product, I use Cloudflare to manage all my DNS, anyone that has a homelab setup knows you need to figure out a way to keep Cloudflare IP update if your home IP is changed, there are many great solutions out there but I love to create my own tools, so I created cloudflaresync.

My home lab setup is a set of docker-compose files, everything is dockerized so this was the obvious choice, I'm trying to learn Golang so written in Go, and the code is very simple, but taylormade to my need.

# docker-compose
version: '3.4'

services:
  cloudflaresync:
    image: niradler/cloudflaresync
    env_file:
      - .env

The code is very straightforward, we get the configuration from a set of env vars, and configure a cron task to run every x time to update DNS records with new IP, I'm also using it to create new records when a new service is created.

// main.go
package main

import (
    ...

    "github.com/cloudflare/cloudflare-go"
    "github.com/joho/godotenv"
    "github.com/robfig/cron/v3"
)

....

func main() {
    err := godotenv.Load()
    if err != nil {
        log.Println("Error loading .env file")
    }
    updateRecords()
    log.Println("Start cron", time.Now())
    cronExpression, exist := os.LookupEnv("CRON")
    if !exist {
        cronExpression = "0 * * * *"
    }
    log.Println("cron:", cronExpression)
    c := cron.New()
    id, err := c.AddFunc(cronExpression, updateRecords)
    if err != nil {
        log.Fatal("cron AddFunc error:", err)
    }
    log.Println("cron id:", id)
    c.Start()
    log.Println("Cron Info: ", c.Entries())

    go forever()

    quitChannel := make(chan os.Signal, 1)
    signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
    <-quitChannel

    fmt.Println("done")
}

Supported env vars:

CLOUDFLARE_API_TOKEN=<key>
CLOUDFLARE_DOMAIN=example.com
CLOUDFLARE_SUB_DOMAINS=test,home
CRON=*/2 * * * *
PROXIED=true

Running example for raspberry pi.

docker run --name cloudflaresync --env CLOUDFLARE_API_TOKEN=<key> --env CLOUDFLARE_SUB_DOMAINS=<app,home> --env CLOUDFLARE_DOMAIN=<example.com> --restart unless-stopped niradler/cloudflaresync:armv7

Updated to automatically update record from container label, check out the repo for more details https://github.com/niradler/cloudflaresync

version: "3.5"

services:
  cloudflaresync:
    image: niradler/cloudflaresync:latest
    restart: unless-stopped
    environment:
      CLOUDFLARE_API_TOKEN: "${CLOUDFLARE_API_TOKEN}"
      CLOUDFLARE_DOMAIN: "${DOMAIN}"
      CLOUDFLARE_SUB_DOMAINS: "${SUB_DOMAINS}"
      DOCKER_DAEMON: 'true'
    labels:
      - homepage.show=true
      - homepage.description=cloudflaresync
      - homepage.title=cloudflaresync
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
version: "3.5"

services:
  adminer:
    image: adminer:latest
    labels:
      - cloudflaresync.name=adminer

will automatically create a record adminer.domain

More from this blog

P

Piece by Piece

46 posts

Hi 馃憢, let me share a story with you. I don't want to overwhelm you, so I'm serving this story piece by piece.