# Stevedore DynDNS - Docker Compose for Stevedore Deployment # # Deploy with: stevedore repo add dyndns # stevedore deploy sync dyndns # stevedore deploy up dyndns # # Configure secrets with: # stevedore param set dyndns CLOUDFLARE_API_TOKEN "your-token" # stevedore param set dyndns CLOUDFLARE_ZONE_ID "your-zone-id" # stevedore param set dyndns DOMAIN "example.com" # stevedore param set dyndns ACME_EMAIL "admin@example.com" services: dyndns: build: . image: stevedore-dyndns:latest # tini as PID 1 so orphaned subprocesses are reaped. Required by stevedore. init: true restart: unless-stopped # Ports for HTTPS reverse proxy # Note: When using network_mode: host, ports are ignored ports: - "80:80" # HTTP (redirect to HTTPS) - "443:443" # HTTPS - "443:443/udp" # HTTP/3 (QUIC) - "8080:8080" # Health/status endpoint (internal) environment: # Stevedore-provided variables - STEVEDORE_DEPLOYMENT=${STEVEDORE_DEPLOYMENT:-dyndns} # Required - set via: stevedore param set dyndns # Note: Use pass-through syntax for stevedore compatibility - CLOUDFLARE_API_TOKEN - CLOUDFLARE_ZONE_ID - DOMAIN - ACME_EMAIL # Discovery mode - token from: stevedore token get dyndns - STEVEDORE_TOKEN - STEVEDORE_SOCKET=/var/run/stevedore/query.sock # Optional - Fritzbox configuration (works without auth on most routers) - FRITZBOX_HOST=${FRITZBOX_HOST:-192.168.178.1} - FRITZBOX_USER=${FRITZBOX_USER:-} - FRITZBOX_PASSWORD=${FRITZBOX_PASSWORD:-} # Optional - Manual IP override (disables auto-detection) - MANUAL_IPV4=${MANUAL_IPV4:-} - MANUAL_IPV6=${MANUAL_IPV6:-} # Optional - Tuning - IP_CHECK_INTERVAL=${IP_CHECK_INTERVAL:-5m} - LOG_LEVEL=${LOG_LEVEL:-info} # Optional - Cloudflare settings # DNS_TTL: TTL in seconds (default: same as IP_CHECK_INTERVAL, min 60) # CLOUDFLARE_PROXY: true to enable Cloudflare proxy (orange cloud) # SUBDOMAIN_PREFIX: true to use prefix mode (app-zone.parent.com instead of app.zone.parent.com) # Required when using Cloudflare proxy with multi-level subdomains (Universal SSL limitation) - DNS_TTL=${DNS_TTL:-} - CLOUDFLARE_PROXY=${CLOUDFLARE_PROXY:-false} - SUBDOMAIN_PREFIX=${SUBDOMAIN_PREFIX:-false} - CATCHALL_SUBDOMAIN=${CATCHALL_SUBDOMAIN:-} # DISABLE_IPV6: when "true", suppress all AAAA publishing and delete # any prior AAAA records dyndns managed. Useful when the router's # WAN IPv6 doesn't actually forward to this host. - DISABLE_IPV6=${DISABLE_IPV6:-false} # MTProto dispatcher (optional). When MTPROTO_DISPATCHER=true, dyndns # binds :443 and peeks SNI; FakeTLS goes to mtglib, browser traffic is # forwarded to Caddy on the loopback port. See CLAUDE.md for details. - MTPROTO_DISPATCHER=${MTPROTO_DISPATCHER:-false} - MTPROTO_SUBDOMAINS=${MTPROTO_SUBDOMAINS:-} - MTPROTO_DATA_DIR=${MTPROTO_DATA_DIR:-} - MTPROTO_DISPATCHER_BIND=${MTPROTO_DISPATCHER_BIND:-} - MTPROTO_CADDY_LOOPBACK=${MTPROTO_CADDY_LOOPBACK:-} - MTPROTO_MAX_CONNECTIONS=${MTPROTO_MAX_CONNECTIONS:-} # Telegram bot (optional). When TELEGRAM_BOT_TOKEN is set the bot runs # and broadcasts MTProto URL posts to TELEGRAM_BOT_CHAT_IDS. - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN:-} - TELEGRAM_BOT_CHAT_IDS=${TELEGRAM_BOT_CHAT_IDS:-} - TELEGRAM_BOT_ALLOWED_USERS=${TELEGRAM_BOT_ALLOWED_USERS:-} # Internal paths — must match the container mount points below, not # the host paths. DYNDNS_DATA was previously set to ${STEVEDORE_DATA}, # which is the HOST path; writes from inside the container to that # path landed on the ephemeral container layer, not on the persistent # volume. MTProto secrets regenerating across rebuilds surfaced this. - DYNDNS_DATA=/data - DYNDNS_LOGS=/var/log/dyndns volumes: # Persistent data - certificates, DNS state # Stevedore provides: /opt/stevedore/deployments/dyndns/data - ${STEVEDORE_DATA:-./data}:/data # Logs directory # Stevedore provides: /opt/stevedore/deployments/dyndns/logs - ${STEVEDORE_LOGS:-./logs}:/var/log/dyndns # Shared storage for cross-deployment communication # Other deployments can register themselves here # Stevedore provides: /opt/stevedore/shared - ${STEVEDORE_SHARED:-./shared}:/shared # Stevedore query socket for service discovery - /var/run/stevedore:/var/run/stevedore:ro healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 start_period: 60s # Security hardening security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - NET_BIND_SERVICE # Required for binding to ports 80/443 # Network mode options: # # Option 1: Host network (RECOMMENDED for Fritzbox auto-detection) # - Required for TR-064/UPnP IP detection from local router # - All ports directly exposed on host (ports: section ignored) # - Simple setup, works out of the box network_mode: host # Option 2: Bridge network (use with MANUAL_IPV4/MANUAL_IPV6) # Uncomment below and comment out network_mode: host # networks: # - default # labels for potential future service discovery labels: - "stevedore.dyndns.enabled=true" - "stevedore.dyndns.role=ingress" # Network for bridge mode (uncomment if not using host network) # networks: # default: # name: stevedore-dyndns