# 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](https://app.meshcast.io/settings/selfhost). 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: ```sh 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](https://app.meshcast.io/settings/selfhost) 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. ```sh 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: ```sh 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. ```sh 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](https://app.meshcast.io/docs/account-api). Create an expiring scoped token in [API access](https://app.meshcast.io/settings/api-tokens). 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. ```sh 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](https://app.meshcast.io/openapi.json) and [API reference](https://app.meshcast.io/api/docs) 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.