Add Cloudflare DDNS for devopshomelab.com (IPv4 A records)
timothyjmiller/cloudflare-ddns with host networking; render env from secrets (apex + subdomains mc/minecraft/vpn/www/forgejo/jellyfin).
This commit is contained in:
parent
60f8a89c87
commit
c130080c52
5 changed files with 129 additions and 0 deletions
|
|
@ -200,6 +200,33 @@ services:
|
|||
- ./landing/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- ./landing/site:/srv:ro
|
||||
|
||||
# ---------------------------------------------- Cloudflare dynamic DNS (A) #
|
||||
# Updates apex + subdomains to the host's public IPv4. Secrets from secrets/.env
|
||||
# Render env first: ./scripts/render-cloudflare-ddns-env.sh
|
||||
cloudflare-ddns:
|
||||
image: timothyjmiller/cloudflare-ddns:latest
|
||||
container_name: cloudflare-ddns
|
||||
restart: unless-stopped
|
||||
# Host networking so we see the real WAN IPv4 (not a Docker NAT address)
|
||||
network_mode: host
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
env_file:
|
||||
- path: ${CONFIG_DIR}/cloudflare-ddns/ddns.env
|
||||
required: false
|
||||
environment:
|
||||
TZ: ${TZ}
|
||||
# Fallbacks if ddns.env not rendered yet (prefer secrets via render script)
|
||||
CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN:-${CLOUDFLAREAPIKEY:-}}
|
||||
IP4_DOMAINS: ${CLOUDFLARE_IP4_DOMAINS:-}
|
||||
IP4_PROVIDER: ${CLOUDFLARE_IP4_PROVIDER:-cloudflare.trace}
|
||||
IP6_PROVIDER: none
|
||||
PROXIED: ${CLOUDFLARE_PROXIED:-false}
|
||||
UPDATE_CRON: ${CLOUDFLARE_UPDATE_CRON:-@every 5m}
|
||||
UPDATE_ON_START: "true"
|
||||
TTL: "1"
|
||||
RECORD_COMMENT: arr-stack cloudflare-ddns
|
||||
|
||||
# ------------------------------------------------------------------- music #
|
||||
lidarr:
|
||||
image: lscr.io/linuxserver/lidarr:latest
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ LAN URLs (UFW allows **`192.168.8.0/24`**). Replace host if needed.
|
|||
| Service | URL |
|
||||
|---------|-----|
|
||||
| **Landing page** | http://192.168.8.123/ |
|
||||
| Cloudflare DDNS | (background) apex + subdomains → public IPv4 |
|
||||
| Jellyfin | http://192.168.8.123:8096 |
|
||||
| qBittorrent | http://192.168.8.123:8080 |
|
||||
| Prowlarr | http://192.168.8.123:9696 |
|
||||
|
|
@ -147,6 +148,17 @@ Root folders: `/data/media/tv`, `/data/media/movies`, `/data/media/music` (hardl
|
|||
|
||||
**Prowlarr** — Add indexers. Apps: Sonarr `http://sonarr:8989`, Radarr `http://radarr:7878`, Lidarr `http://lidarr:8686` + each API key (*Settings → General*).
|
||||
|
||||
**Cloudflare DDNS** (`timothyjmiller/cloudflare-ddns`) — updates **devopshomelab.com** and subdomains to your **public IPv4** every 5 minutes.
|
||||
|
||||
```bash
|
||||
# secrets/.env must include CLOUDFLAREAPIKEY, CLOUDFLAREDOMAIN, CLOUDFLARESUBDOMAINS
|
||||
./scripts/render-cloudflare-ddns-env.sh
|
||||
docker compose up -d cloudflare-ddns
|
||||
docker logs cloudflare-ddns --tail 30
|
||||
```
|
||||
|
||||
Token needs **Zone → DNS → Edit**. `PROXIED=false` by default (DNS-only; better for VPN). Set `CLOUDFLARE_PROXIED=true` in secrets if you want orange-cloud for all names.
|
||||
|
||||
**Usenet (NZBgeek + Newshosting + Tweaknews)**
|
||||
Credentials live in **`secrets/.env`** (not git). Example keys: see `secrets/.env.example`.
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,13 @@ fi
|
|||
# ---------------------------------------------------------------------------
|
||||
# Usenet bootstrap
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cloudflare DDNS (optional — only if secrets present)
|
||||
if [[ -f "$STACK_DIR/secrets/.env" ]] && grep -qE '^CLOUDFLAREAPIKEY=.+' "$STACK_DIR/secrets/.env" 2>/dev/null; then
|
||||
log "Rendering Cloudflare DDNS env + starting container"
|
||||
"$SCRIPT_DIR/render-cloudflare-ddns-env.sh" || log "cloudflare-ddns render failed"
|
||||
docker compose up -d cloudflare-ddns || log "cloudflare-ddns start failed"
|
||||
fi
|
||||
|
||||
if [[ "$CONFIGURE_USENET" == "true" || "$CONFIGURE_USENET" == "1" ]]; then
|
||||
log "Waiting for SABnzbd…"
|
||||
for i in $(seq 1 60); do
|
||||
|
|
|
|||
69
scripts/render-cloudflare-ddns-env.sh
Executable file
69
scripts/render-cloudflare-ddns-env.sh
Executable file
|
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build config/cloudflare-ddns.env for timothyjmiller/cloudflare-ddns from secrets/.env
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
STACK_DIR="${STACK_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
||||
SECRETS="${SECRETS_FILE:-$STACK_DIR/secrets/.env}"
|
||||
OUT_DIR="${CONFIG_DIR:-$STACK_DIR/config}/cloudflare-ddns"
|
||||
OUT="$OUT_DIR/ddns.env"
|
||||
|
||||
[[ -f "$SECRETS" ]] || { echo "missing $SECRETS"; exit 1; }
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$SECRETS"
|
||||
set +a
|
||||
|
||||
TOKEN="${CLOUDFLARE_API_TOKEN:-${CLOUDFLAREAPIKEY:-}}"
|
||||
ZONE="${CLOUDFLARE_ZONE_ID:-${CLOUDFLAREZONEID:-}}"
|
||||
DOMAIN="${CLOUDFLARE_DOMAIN:-${CLOUDFLAREDOMAIN:-devopshomelab.com}}"
|
||||
SUBS="${CLOUDFLARE_SUBDOMAINS:-${CLOUDFLARESUBDOMAINS:-}}"
|
||||
PROXIED="${CLOUDFLARE_PROXIED:-${CLOUDFLAREPROXIED:-false}}"
|
||||
CRON="${CLOUDFLARE_UPDATE_CRON:-@every 5m}"
|
||||
|
||||
[[ -n "$TOKEN" ]] || { echo "CLOUDFLAREAPIKEY / CLOUDFLARE_API_TOKEN missing"; exit 1; }
|
||||
[[ -n "$DOMAIN" ]] || { echo "CLOUDFLAREDOMAIN missing"; exit 1; }
|
||||
|
||||
# apex + each subdomain → full FQDNs
|
||||
DOMAINS=("$DOMAIN")
|
||||
IFS=',' read -ra parts <<<"$SUBS"
|
||||
for s in "${parts[@]}"; do
|
||||
s="$(echo "$s" | tr -d '[:space:]')"
|
||||
[[ -z "$s" ]] && continue
|
||||
if [[ "$s" == "$DOMAIN" || "$s" == "@" || "$s" == "." ]]; then
|
||||
continue
|
||||
elif [[ "$s" == *.* ]]; then
|
||||
DOMAINS+=("$s")
|
||||
else
|
||||
DOMAINS+=("${s}.${DOMAIN}")
|
||||
fi
|
||||
done
|
||||
|
||||
# unique join
|
||||
IP4_LIST="$(printf '%s\n' "${DOMAINS[@]}" | awk 'NF && !seen[$0]++' | paste -sd, -)"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
umask 077
|
||||
cat >"$OUT" <<EOF
|
||||
# Generated by scripts/render-cloudflare-ddns-env.sh — do not commit
|
||||
CLOUDFLARE_API_TOKEN=${TOKEN}
|
||||
IP4_DOMAINS=${IP4_LIST}
|
||||
IP4_PROVIDER=cloudflare.trace
|
||||
IP6_PROVIDER=none
|
||||
PROXIED=${PROXIED}
|
||||
UPDATE_CRON=${CRON}
|
||||
UPDATE_ON_START=true
|
||||
TTL=1
|
||||
RECORD_COMMENT=arr-stack cloudflare-ddns
|
||||
TZ=${TZ:-UTC}
|
||||
EOF
|
||||
|
||||
# optional zone id is not required by current image (token + domains)
|
||||
if [[ -n "$ZONE" ]]; then
|
||||
echo "# CLOUDFLARE_ZONE_ID=${ZONE} (informational; env-mode uses token scopes)" >>"$OUT"
|
||||
fi
|
||||
|
||||
chmod 600 "$OUT"
|
||||
echo "Wrote $OUT"
|
||||
echo "IP4_DOMAINS=${IP4_LIST}"
|
||||
|
|
@ -24,3 +24,17 @@ TWEAKNEWSSSLPORT=563
|
|||
TWEAKNEWSLOGIN=
|
||||
TWEAKNEWSPASSWORD=
|
||||
TWEAKNEWSCONNECTIONS=40
|
||||
|
||||
# --- Cloudflare DDNS (timothyjmiller/cloudflare-ddns) ---
|
||||
# API token needs Zone.DNS Edit on the zone (not the global API key)
|
||||
CLOUDFLAREAPIKEY=
|
||||
# optional aliases
|
||||
CLOUDFLARE_API_TOKEN=
|
||||
CLOUDFLAREZONEID=
|
||||
CLOUDFLAREACCOUNTID=
|
||||
CLOUDFLAREDOMAIN=devopshomelab.com
|
||||
# Comma-separated labels (or FQDNs). Apex is always included.
|
||||
CLOUDFLARESUBDOMAINS=mc,minecraft,vpn,www,forgejo,jellyfin
|
||||
# false = DNS-only (needed for VPN); true = orange-cloud proxy
|
||||
CLOUDFLARE_PROXIED=false
|
||||
CLOUDFLARE_UPDATE_CRON=@every 5m
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue