name: podsteadr services: podsteadr: build: . # Container/DNS-alias name deliberately NOT "podsteadr" — on hosts whose own hostname is # "podsteadr" (e.g. a VPS named after the app), the host's own /etc/hosts self-hostname # entry (127.0.1.1 podsteadr, added by cloud-init) shadows the container network's DNS # alias for other containers looking up "podsteadr", so mediamtx's auth-webhook callback # resolved to the host's loopback instead of this container and every RTMP publish got # rejected with "connection refused" (observed on podsteadr.atobitcoin.io, 2026-07-30). container_name: podsteadr-app restart: unless-stopped ports: - "8095:8095" environment: NODE_ENV: production PORT: "8095" DATA_DIR: /data RECORDINGS_DIR: /recordings PUBLIC_URL: ${PUBLIC_URL:-http://localhost:8095} MEDIAMTX_API_URL: http://mediamtx:9997 MEDIAMTX_RTMP_PUBLIC: ${MEDIAMTX_RTMP_PUBLIC:-rtmp://localhost:1935} MEDIAMTX_WHIP_PUBLIC: ${MEDIAMTX_WHIP_PUBLIC:-http://localhost:8889} MEDIAMTX_HLS_PUBLIC: ${MEDIAMTX_HLS_PUBLIC:-http://localhost:8890} BLOSSOM_URL_DEFAULT: ${BLOSSOM_URL_DEFAULT:-http://localhost:8098} BLOSSOM_URL_INTERNAL: http://blossom:3000 NOSTR_RELAYS: ${NOSTR_RELAYS:-wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band} CASHU_MINT_URL_DEFAULT: ${CASHU_MINT_URL_DEFAULT:-https://mint.minibits.cash/Bitcoin} volumes: - podsteadr-data:/data - mediamtx-recordings:/recordings:ro depends_on: - mediamtx - blossom mediamtx: image: docker.io/bluenviron/mediamtx:1.20.0 container_name: podsteadr-mediamtx restart: unless-stopped ports: - "1935:1935" # RTMP ingest - "8889:8889" # WebRTC / WHIP - "8189:8189/udp" # WebRTC ICE - "8890:8888" # HLS (host 8890; 8888 kept free for other apps) environment: # Browsers need a reachable ICE host candidate for the actual UDP media # path (browser-publish "stream from this browser" / WHIP). This must # be the raw origin IP, NOT PUBLIC_HOST — Cloudflare's proxy only # forwards HTTP(S), never raw UDP, regardless of port (same reason # MEDIAMTX_RTMP_PUBLIC above uses the raw IP instead of the # Cloudflare-proxied domain). Using PUBLIC_HOST here means the browser # resolves the ICE candidate to Cloudflare's edge and the WHIP HTTP # handshake succeeds while media silently never arrives — set # MEDIAMTX_WEBRTC_HOST in .env. MTX_WEBRTCADDITIONALHOSTS: ${MEDIAMTX_WEBRTC_HOST:-localhost} volumes: - ./mediamtx/mediamtx.yml:/mediamtx.yml:ro - mediamtx-recordings:/recordings blossom: image: ghcr.io/hzrd149/blossom-server:4 container_name: podsteadr-blossom restart: unless-stopped ports: - "8098:3000" volumes: - ./blossom/config.yml:/app/config.yml:ro - blossom-data:/app/data volumes: podsteadr-data: mediamtx-recordings: blossom-data: