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/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
- ./landing/site:/srv: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 #
|
# ------------------------------------------------------------------- music #
|
||||||
lidarr:
|
lidarr:
|
||||||
image: lscr.io/linuxserver/lidarr:latest
|
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 |
|
| Service | URL |
|
||||||
|---------|-----|
|
|---------|-----|
|
||||||
| **Landing page** | http://192.168.8.123/ |
|
| **Landing page** | http://192.168.8.123/ |
|
||||||
|
| Cloudflare DDNS | (background) apex + subdomains → public IPv4 |
|
||||||
| Jellyfin | http://192.168.8.123:8096 |
|
| Jellyfin | http://192.168.8.123:8096 |
|
||||||
| qBittorrent | http://192.168.8.123:8080 |
|
| qBittorrent | http://192.168.8.123:8080 |
|
||||||
| Prowlarr | http://192.168.8.123:9696 |
|
| 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*).
|
**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)**
|
**Usenet (NZBgeek + Newshosting + Tweaknews)**
|
||||||
Credentials live in **`secrets/.env`** (not git). Example keys: see `secrets/.env.example`.
|
Credentials live in **`secrets/.env`** (not git). Example keys: see `secrets/.env.example`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,13 @@ fi
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Usenet bootstrap
|
# 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
|
if [[ "$CONFIGURE_USENET" == "true" || "$CONFIGURE_USENET" == "1" ]]; then
|
||||||
log "Waiting for SABnzbd…"
|
log "Waiting for SABnzbd…"
|
||||||
for i in $(seq 1 60); do
|
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=
|
TWEAKNEWSLOGIN=
|
||||||
TWEAKNEWSPASSWORD=
|
TWEAKNEWSPASSWORD=
|
||||||
TWEAKNEWSCONNECTIONS=40
|
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