MTX_WEBRTCADDITIONALHOSTS (the ICE host candidate MediaMTX advertises for WebRTC/WHIP) was wired to PUBLIC_HOST, the Cloudflare-proxied domain. The WHIP HTTP handshake (SDP offer/answer through nginx) still succeeds through Cloudflare, so 'stream from this browser' looks like it works — but the actual media is a raw UDP path (port 8189) that Cloudflare never forwards regardless of port, same as the earlier RTMP issue. The browser ends up trying to send video/audio to Cloudflare's edge, which drops it, so nothing ever actually arrives. Split into a dedicated MEDIAMTX_WEBRTC_HOST env var (raw origin IP in production) instead of reusing PUBLIC_HOST, mirroring how MEDIAMTX_RTMP_PUBLIC already does this for the same reason. Confirmed via podman inspect that the previously-deployed container really was resolving MTX_WEBRTCADDITIONALHOSTS to the Cloudflare-proxied hostname before this fix.
75 lines
3.0 KiB
YAML
75 lines
3.0 KiB
YAML
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:
|