Self-hosting with Meshcast

Run your own private MediaMTX server with your Meshcast account. These instructions cover MediaMTX 1.16.1 on Linux amd64/arm64 using Docker Compose, the native installer, or a manual setup. Only your account can publish to your node; viewers use your public viewing links.

Start by registering your server. You need a Meshcast account and your own server with a public address and trusted TLS certificate. Docker publishing, browser playback, reconnect and monitoring have been validated; native installation limitations are described below.

Get the public package

Create a private working directory and fetch the public files from the same Meshcast deployment where your account is registered:

umask 077
mkdir meshcast-personal
cd meshcast-personal
for file in README.md SKILL.md compose.yml reporter.py install.py; do
  curl --fail --silent --show-error --output "$file" \
    "https://app.meshcast.io/static/downloads/personal-node/$file" || exit 1
done

If a package file or API route returns 404, stop and check the deployment; do not substitute an old installer. Review README.md and the chosen installer/configuration before running them. Download the private ZIP separately through your account; it is not part of these public files. Work inside this directory for the following commands.

Prerequisites

Use your own publicly reachable IPv4 address or hostname. Meshcast must reach the node's HTTPS signaling port; STUN does not provide HTTP ingress through CGNAT. The default signaling port is TCP 8889; media uses UDP 8189, RTMP TCP 1935 and SRT UDP 8890. Permit outbound HTTPS to app.meshcast.io and STUN UDP to stun.l.google.com:19302. Use your registered signaling port if different.

Register the node in Self-hosted servers using HTTPS. Download the private setup ZIP. Pending is normal before installation. Extract mediamtx.yml and reporter.json into a private directory. Provide a trusted certificate with full chain for your registered hostname/IP as server.crt and server.key. Automated certificate enrollment is not available yet. Never disable certificate verification. Keep configurations and private keys out of chat, source control and shared logs.

If another installation exists, inspect services, ports, versions and storage first. Back up configuration and recordings. Configuration changes require a stopped-service maintenance window; never reload running MediaMTX. The old v1 installer is unrelated to this account-linked bundle.

Native installation

Full native installation and reboot acceptance are pending. Only prepare-only has been tested; use a disposable machine for native installation validation.

Keep install.py and reporter.py from this directory together. Python 3, systemd, CA certificates and useradd are prerequisites. Review the installer before use.

chmod 600 mediamtx.yml reporter.json server.key
sudo python3 install.py --config mediamtx.yml --reporter-config reporter.json \
  --cert server.crt --key server.key

The helper verifies the release archive's upstream SHA-256 digest, creates a dedicated service user and installs two stopped services. It refuses an existing destination, service unit or service user. It does not alter DNS or firewall rules. After checking ports and certificates on an approved clean machine:

sudo systemctl enable --now meshcast-personal.service meshcast-personal-reporter.service
sudo systemctl status meshcast-personal.service meshcast-personal-reporter.service

Use --prepare-only with a new --destination to prepare files without changing users or systemd. Prepared units are examples; service hardening may require moving files out of a user's home directory before activation.

Docker Compose

The Linux host must have an up-to-date CA certificate package and /etc/ssl/certs. The Compose file mounts this trust store read-only because the minimal MediaMTX image does not include one. This is required for verified HTTPS authentication callbacks; the node's own server certificate is not a replacement for a CA trust store.

Place compose.yml and reporter.py beside the private configuration and TLS files. Create a recordings directory. Both containers default to UID/GID 1000; set MESHCAST_UID/MESHCAST_GID to the owner of those files. That user needs read access to private files and write access to recordings. Set WEBRTC_PORT to the registered port in mediamtx.yml.

Docker Engine and its Compose plugin must already be installed and available to your user. Check docker version and docker compose version first. The package does not install Docker or change access to its daemon. Check that TCP 1935, your signaling port, UDP 8890 and UDP 8189 are free before starting; do not stop another application to free them without authorization.

mkdir -p recordings
chmod 600 mediamtx.yml reporter.json server.key
export MESHCAST_UID=$(id -u) MESHCAST_GID=$(id -g) WEBRTC_PORT=8889
docker compose -f compose.yml config --quiet
docker compose -f compose.yml up -d

The reporter shares MediaMTX's network namespace to read its loopback API. Never publish port 9997. Recreate the reporter when replacing MediaMTX so it uses the new network namespace. Do not hot-reload configuration. To opt out of monitoring, omit the reporter and use the basic config with its API disabled.

Manual or AI-assisted deployment

Use the same version, private configuration and reporting protocol with your own service manager or container platform. MediaMTX must read its TLS/config files; reporter.py requires Python 3 and access to http://127.0.0.1:9997 on the node. That management API is unauthenticated because it is confined to the trusted host/network namespace. Never expose it to other machines. Do not add managed fleet recording, restream or billing hooks.

The reporter sends outbound HTTPS snapshots every 30 seconds with a separate node credential. It does not run remote commands or stop media on reporting failure. Streaming works without it, but monitoring is unavailable. Usage is observed MediaMTX byte differences, not a provider invoice. First samples, restarts and missing final samples create coverage gaps.

Verify and diagnose

  1. Check connectivity from the node's settings row. This checks TCP only.
  2. Publish an owned test Stream ID and watch audio/video from another network through the Meshcast gateway links pinned to your node.
  3. Open Monitor. Check live streams, viewer connections and last report time, then stop the test publisher and verify the next report. Stale reporting does not prove the stream stopped.
  4. For Docker, run the one-shot diagnostic inside the reporter container: docker compose -f compose.yml exec -T reporter python -B /reporter.py --config /reporter.json --once. For native installs, run python3 reporter.py --config reporter.json --once as the service user from its working directory. Success exits with code zero; failure prints a redacted error. Inspect logs locally; redact credentials, auth URLs and session data before sharing.
  5. Test restart only during approved maintenance. Verify media, persistent local recordings and reporting afterward.

RTMP publish: rtmp://NODE:1935/st_ID?user=meshcast&pass=pk_KEY

SRT publish: srt://NODE:8890?streamid=publish:st_ID:meshcast:pk_KEY&pkt_size=1316

For OBS's separate RTMP key field use st_ID?user=meshcast&pass=pk_KEY. For WHIP, use https://NODE:8889/st_ID/whip with pk_KEY as the Bearer Token. WHEP playback uses https://NODE:8889/st_ID/whep without a publishing credential. Replace NODE with your registered hostname and 8889 with your signaling port. Use the public Stream ID as every media path; the Publish Key is a credential. WebRTC audio needs compatible codecs, typically Opus. RTMP/SRT AAC tests do not prove browser audio compatibility; no automatic transcoder is included. HLS, RTSP, managed restreaming and cloud recordings are not part of this bundle. Optional local recording requires record: yes, disk management and approved stopped-service configuration changes. It does not populate the Meshcast library. In pathDefaults, enable record: yes, set recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f and recordFormat: fmp4. The Compose recordings directory is the persistent location for those files. Stop both containers before editing an existing installation's configuration, then start both again. Verify new files with an audio/video player after stopping the test publisher; do not delete the directory during container recreation.

Account API

For scripts or an AI assistant, see the account automation guide. Create an expiring scoped token in API access. Store it in a private curl configuration file with permissions 600 containing a header setting: header = "Authorization: Bearer YOUR_ACCOUNT_TOKEN". Never commit that real file or paste its contents into chat.

curl --config account.curl https://app.meshcast.io/api/v1/capabilities
curl --config account.curl https://app.meshcast.io/api/v1/streams
curl --config account.curl https://app.meshcast.io/api/v1/nodes

Use the OpenAPI contract and API reference for supported operations. Read-only stream access excludes keys. Downloading a node setup bundle requires nodes:write and credentials:read without a stream restriction. Account tokens, publishing keys and node/reporting credentials serve separate purposes.

Account API requests allow 120 calls per account per minute and 16 KiB bodies. Honor HTTP 429 Retry-After (seconds). Errors use {error: {code, message}}; save the X-Request-ID when reporting a problem. Stream creation requires a fresh Idempotency-Key for each intended creation; retries retain their original key and label. Keys are retained for seven days, with at most 1000 creations per account during that period. After expiry, reusing a key is a new request. Credential rotation requires the current GET response's ETag as If-Match; after a conflict, read current state before deciding whether to rotate again.

The reporter normally sends twice per minute and allows up to 12 authenticated requests per node per minute, including malformed reports. Audit events retain the token/account, operation, owned resource and request ID, without credentials or request bodies. Retention is seven days and at most 1000 audit rows per account, with cleanup during API activity. Observed usage is retained for 90 days; recent node authorization history for seven days (at most 1000 per node).

Enabling/disabling publishing changes new admission, not existing connections. The account API cannot start OBS or disconnect this node's live publisher. Stop your encoder or MediaMTX to disconnect existing sessions. Removing the node rejects new authorization/reporting; established sessions may continue. Deleting a node also removes its dashboard monitoring history and clears its manual server selection. Export any history you need before deleting it.

Upgrade and rollback

Keep the previous binary/image, private config/certificates and recordings backup. Stop MediaMTX and its reporter during approved maintenance, replace the reviewed files, start and verify. If checks fail, stop the replacement and restore the saved version/config. Do not delete recordings or reload live configuration. Do not automatically upgrade to an untested version. App signing-key rotation requires downloading new node credentials.

Copy Markdown

Clipboard access is unavailable. Copy the selected instructions below.