Archipelago — open-source initial import
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
app:
|
||||
id: podsteadr-mediamtx
|
||||
name: podsteadr MediaMTX
|
||||
version: "1.20.0"
|
||||
description: MediaMTX ingest/output backend for podsteadr — RTMP + WebRTC/WHIP ingest, HLS playback, stream recording.
|
||||
category: media
|
||||
|
||||
# Hyphenated name matches the podsteadr repo's docker-compose container_name
|
||||
# (podsteadr-mediamtx); alias `mediamtx` is the short hostname podsteadr's
|
||||
# server reaches it by (MEDIAMTX_API_URL=http://mediamtx:9997) and the one
|
||||
# baked into mediamtx.yml's authHTTPAddress callback below.
|
||||
container_name: podsteadr-mediamtx
|
||||
|
||||
container:
|
||||
image: docker.io/bluenviron/mediamtx:1.20.0
|
||||
pull_policy: if-not-present
|
||||
network: podsteadr-net
|
||||
network_aliases: [mediamtx]
|
||||
derived_env:
|
||||
# Browsers need a reachable ICE host candidate for WebRTC/WHIP; without
|
||||
# this, the offer only advertises container-internal addresses and
|
||||
# publish/playback negotiation fails for anyone off-host.
|
||||
- key: MTX_WEBRTCADDITIONALHOSTS
|
||||
template: "{{HOST_MDNS}}"
|
||||
|
||||
dependencies:
|
||||
- storage: 10Gi
|
||||
|
||||
resources:
|
||||
cpu_limit: 1
|
||||
memory_limit: 512Mi
|
||||
disk_limit: 10Gi
|
||||
|
||||
security:
|
||||
# Stock mediamtx image runs as container-root (no USER directive) but
|
||||
# only ever writes to the bind-mounted /recordings — CHOWN/DAC_OVERRIDE
|
||||
# cover the fresh-bind-dir-ownership gap the same way apps/botfights and
|
||||
# apps/immich document (root uid inside the container does not
|
||||
# automatically bypass DAC checks once cap-drop ALL applies). Unverified
|
||||
# against a real install; check first-boot logs on initial deploy.
|
||||
capabilities: [CHOWN, DAC_OVERRIDE]
|
||||
readonly_root: true
|
||||
no_new_privileges: true
|
||||
network_policy: isolated
|
||||
|
||||
ports:
|
||||
- host: 1935
|
||||
container: 1935
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
RTMP ingest (OBS). Not HTTP, so the node's session gate has no login
|
||||
page to serve here; publish auth is delegated to podsteadr's own
|
||||
HTTP auth webhook (authHTTPAddress below), which checks a per-stream
|
||||
secret key never exposed in this port mapping.
|
||||
- host: 8889
|
||||
container: 8889
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
WebRTC/WHIP ingest — browsers publish directly with a per-stream
|
||||
bearer secret checked by podsteadr's auth webhook, the same
|
||||
protocol-level auth as the RTMP port above.
|
||||
- host: 8189
|
||||
container: 8189
|
||||
protocol: udp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
WebRTC ICE/UDP media transport. Raw UDP has no HTTP session concept
|
||||
for the gate to enforce.
|
||||
- host: 8890
|
||||
container: 8888
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
Public HLS playback URL, handed out to viewers and podcast/livestream
|
||||
clients outside the node (zap.stream, third-party players). A login
|
||||
page here would break every external viewer; playback is read-only.
|
||||
|
||||
volumes:
|
||||
- type: bind
|
||||
# Shared with apps/podsteadr (mounted read-only there) so the app can
|
||||
# list and remux finished recordings for one-click episode publishing.
|
||||
source: /var/lib/archipelago/podsteadr/recordings
|
||||
target: /recordings
|
||||
options: [rw]
|
||||
- type: bind
|
||||
source: /var/lib/archipelago/podsteadr-mediamtx/config/mediamtx.yml
|
||||
target: /mediamtx.yml
|
||||
options: [ro]
|
||||
|
||||
environment: []
|
||||
|
||||
files:
|
||||
- path: /var/lib/archipelago/podsteadr-mediamtx/config/mediamtx.yml
|
||||
overwrite: true
|
||||
content: |
|
||||
# MediaMTX configuration for podsteadr.
|
||||
# Ingest: RTMP (OBS) + WebRTC/WHIP (browser). Output: HLS. Publish auth is
|
||||
# delegated to podsteadr via HTTP; stream status is polled from the API.
|
||||
|
||||
logLevel: info
|
||||
|
||||
api: yes
|
||||
apiAddress: :9997
|
||||
|
||||
# ---- authentication ------------------------------------------------------
|
||||
authMethod: http
|
||||
authHTTPAddress: http://podsteadr-app:8095/api/mediamtx/auth
|
||||
authHTTPExclude:
|
||||
- action: api
|
||||
- action: metrics
|
||||
- action: pprof
|
||||
|
||||
# ---- protocols -----------------------------------------------------------
|
||||
rtsp: no
|
||||
srt: no
|
||||
moq: no
|
||||
|
||||
rtmp: yes
|
||||
rtmpAddress: :1935
|
||||
|
||||
hls: yes
|
||||
hlsAddress: :8888
|
||||
# Standard HLS, not lowLatency: LL-HLS's small per-part buffering window has very little
|
||||
# tolerance for B-frame reordering (common in most OBS encoder presets), and a real test
|
||||
# stream crashed the muxer twice in ~2 minutes with "too many reordered frames" / "unable to
|
||||
# extract DTS" once frame timing got even slightly irregular. Standard HLS buffers a full
|
||||
# segment before finalizing, which absorbs that jitter — a few extra seconds of latency
|
||||
# instead of intermittent muxer crashes / viewer buffering.
|
||||
hlsVariant: mpegts
|
||||
hlsAlwaysRemux: yes
|
||||
hlsAllowOrigins: ["*"]
|
||||
|
||||
webrtc: yes
|
||||
webrtcAddress: :8889
|
||||
webrtcLocalUDPAddress: :8189
|
||||
webrtcAllowOrigins: ["*"]
|
||||
|
||||
# ---- recording -----------------------------------------------------------
|
||||
pathDefaults:
|
||||
record: yes
|
||||
recordPath: /recordings/%path/%Y-%m-%d_%H-%M-%S-%f
|
||||
recordFormat: fmp4
|
||||
recordPartDuration: 1s
|
||||
recordSegmentDuration: 1h
|
||||
recordDeleteAfter: 168h
|
||||
|
||||
paths:
|
||||
# Streams live at live/<streamId>; publish requires the stream secret,
|
||||
# which podsteadr checks in the auth webhook.
|
||||
"~^live/[A-Za-z0-9]+$": {}
|
||||
|
||||
health_check:
|
||||
# Stock mediamtx image has no shell, so an in-container HTTP probe of the
|
||||
# API isn't meaningfully cheaper than TCP; RTMP liveness is enough (same
|
||||
# polling-not-hooks rationale as podsteadr's own status poller, which
|
||||
# exists precisely because runOn*-style shell hooks aren't available on
|
||||
# this image).
|
||||
type: tcp
|
||||
endpoint: localhost:1935
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
Reference in New Issue
Block a user