Files
Media-Server-Setup/HOSTNAME-CHANGE.md
Brandyn / Techy 6f77764fec Propagate this build's lessons into the scripts and every runbook
addCommunityMediaServer.sh asks for the diag hostname, DNS-checks it and passes it to --hosts, so a rebuild
sets it up like every other host instead of leaving it to be discovered missing.

HOSTNAME-CHANGE.md gains §D, adding a new host, which is where the hour went: --apply is stateless and reads
hostnames back out of the Caddyfile, so it silently omits a role that is not already there and the name ends
up with no certificate. README and the Phase 13 checklist now point at --full or --hosts instead of --apply,
and the plan's risks section carries the same warning plus the path-prefix cascade.

LOGGING.md collects the four traps this produced so none of them is rediscovered. FRESH-INSTALL gains an
ordered §7b and appendix M for the diag host, including that running without a webhook is correct rather than
half-configured; the old Afterwards section is relettered N.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-03 16:32:09 -06:00

12 KiB

Changing hostnames safely

Two cases: renaming one subdomain (e.g. watch.BASE → video.BASE), and moving the whole stack to a new base domain. Both are mostly configure.py --full plus a known list of places outside the repo that hold the name. Do it between streams: the main host is MediaMTX's WebRTC advertised host, so a main-host change needs a MediaMTX restart.

Roles: main (gateway page at /), files (copyparty), clips, sync, live (portal + /dock/, /dock/clock/, /dump, /me, /time), auth, watch, git, bot. Ours: media., files., clips., sync., live., … under dumpstervigilantes.rip.

Where hostnames live

Generated on the server by configure.py (regenerated by --full, nothing to edit by hand)

Place Holds
Caddyfile every site block — the source of truth configure.py reads back (# vanguard:<role> markers)
.env MAIN_HOST, FILES_HOST, CLIPS_HOST, SYNC_HOST, DIAG_HOST, WATCH_HOST, GIT_HOST, BOT_HOST, LIVE_HOST, APEX_HOST, APEX_REDIRECT
mediamtx.yml webrtcAdditionalHosts: [MAIN_HOST] (needs a MediaMTX restart to take effect)
docker-compose.yml → container env Jellyfin PublishedServerUrl (WATCH), Gitea DOMAIN/ROOT_URL/SSH_DOMAIN (GIT), bot WEB_PUBLIC_URL (BOT), MediaMTX LIVE_HOST (text of the "is live" post), act_runner GITEA_INSTANCE_URL (GIT). compose up -d in the apply recreates the affected containers (seconds each)
Caddy TLS certificates for new names are fetched automatically; old ones linger harmlessly

Pages in the repo that derive sibling hosts from the browser's location (label names hard-coded)

File Assumes
about/index.html files., clips., sync., live., auth., watch., git., bot. + the parent of the current host
dock/index.html auth. (sign-out), ingest host prefilled from the current host (live.; any name of the box works for RTMP/SRT)
switchToClone.sh, deployLive.sh defaults for git.dumpstervigilantes.rip / media.dumpstervigilantes.rip (override by env/arg, or edit)
README.md, FRESH-INSTALL.md examples only
Renaming a label (e.g. watch → video) means editing the first two; changing only the domain does not.

Outside the repo — must be changed by hand

System Setting Which host
DNS A record + CNAMEs all
Discord developer portal OAuth2 redirect https://AUTH/source/oauth/callback/discord/ auth
Authentik Embedded Outpost authentik_host auth
Authentik Vanguard sites proxy provider: authentication URL (auth) and cookie domain (base) auth, base
Authentik Jellyfin provider redirect URI https://WATCH/sso/OID/redirect/authentik watch
Authentik Gitea provider redirect URI https://GIT/user/oauth2/authentik/callback git
Jellyfin SSO-Auth plugin: OID endpoint https://AUTH/application/o/jellyfin/ auth
Jellyfin Branding login disclaimer (the form action contains the watch host) watch
Jellyfin Vanguard Metadata plugin: Public URL, Authentik URL watch, auth
Gitea Authentication source authentik: discovery URL https://AUTH/application/o/gitea/.well-known/openid-configuration auth
Gitea runner registered with the instance URL → ./registerGiteaRunner.sh again git
Server clone git remote set-url origin ssh://git@GIT:2222/techy/Media-Server-Setup.git; ~/.ssh/config.vanguard Host line git
Dev box git remote set-url origin … git
Gitea, the bot repo nothing for a rename; VANGUARD_DEPLOY_HOST only if it was set to an address —
Streamers' OBS dock URL (Custom Browser Docks), dock Settings → ingest host (re-save), clock browser source URL (dock → Timecode band → "Add band + clock source" again), any manual RTMP/SRT configs (copy from the dock) live
Jellyfin apps on phones/TVs server address watch
Bookmarks, Discord pinned links main (gateway), clips, files, watch, live all
Not affected: Syncthing pairings (device IDs), MariaDB, stream tokens, the Gitea webhook (gateway IP), Authentik users/groups/policies.

A. Renaming one subdomain (example: watch.BASE → video.BASE)

  1. DNS: add CNAME video.BASE → HOST. Keep the old record until step 7.
  2. Server, between streams: ./configure.py --full → Enter keeps every value; type the new name at the "Jellyfin hostname" prompt; y. Caddy reloads (new block, old gone), Jellyfin is recreated with the new published URL. curl -sI https://video.BASE/ | head -1 → 200/302 once the certificate is issued. (Only the main host touches mediamtx.yml; other hosts need no MediaMTX restart.)
  3. Authentik: Applications → Providers → Jellyfin → Edit → redirect URI → the new host → Update.
  4. Jellyfin: Dashboard → General → Branding → disclaimer form action → new host → Save. Dashboard → Plugins → Vanguard Metadata → Public URL → new host → Save.
  5. If the label changed (watch→video), edit about/index.html (sub('watch') → sub('video')) — commit, push, sudo vanguard-deploy pull. (.env APEX_REDIRECT too if the bare domain pointed at it: ./configure.py --full.)
  6. Private window: https://video.BASE/ → Discord → in. The gateway https://HOST/ links to the new name.
  7. Remove the old DNS record. Tell members with Jellyfin apps to update the server address.

Per-host extras when it is a different host:

  • auth: Discord portal redirect; Authentik outpost authentik_host and the proxy provider's authentication URL; Jellyfin SSO plugin OID endpoint; Vanguard plugin Authentik URL; Gitea auth source discovery URL; dock/index.html + about/index.html if the label changed. Everyone re-logs in.
  • git: Authentik Gitea provider redirect; ./registerGiteaRunner.sh; server clone remote + ~/.ssh/config.vanguard; dev remote; switchToClone.sh default.
  • live, bot: only the compose env (automatic) and about/index.html if the label changed.
  • main (HOST): mediamtx.yml changes → docker compose restart mediamtx between streams; every streamer re-adds the dock URL, re-saves Settings (ingest host), and re-runs "Add band + clock source"; manual RTMP/SRT configs are re-copied from the dock; deployLive.sh default host.

B. Changing the base domain (everything at once)

Plan a maintenance window: Caddy, Jellyfin, Gitea, the bot and MediaMTX all restart, and every member logs in again (the Authentik session cookie is bound to the base domain).

  1. DNS on the new domain: A HOST2 → IP, CNAMEs files, clips, sync, live, auth, watch, git, bot → HOST2 (optional bare domain A). Verify they resolve before continuing. Leave the old domain's records in place for now.
  2. Discord developer portal → OAuth2 → add redirect https://auth.NEW/source/oauth/callback/discord/ (keep the old one until step 9).
  3. Server, between streams: ./configure.py --full → new main hostname; the eight sub-hostnames default to the new base (Enter); bare domain; y. Then docker compose restart mediamtx (webrtcAdditionalHosts changed). All sites answer on the new names within a minute (certificates).
  4. Authentik (log in at the NEW auth host with akadmin — Discord login is broken until step 5 is done):
    • Outposts → Embedded Outpost → authentik_host: https://auth.NEW
    • Providers → Vanguard sites → authentication URL https://auth.NEW, cookie domain NEW
    • Providers → Jellyfin → redirect https://watch.NEW/sso/OID/redirect/authentik
    • Providers → Gitea → redirect https://git.NEW/user/oauth2/authentik/callback
  5. Private window: https://auth.NEW/ → Discord login works (the redirect from step 2 is used).
  6. Jellyfin: SSO plugin OID endpoint https://auth.NEW/application/o/jellyfin/; Branding disclaimer form action https://watch.NEW/sso/OID/start/authentik; Vanguard plugin Public URL + Authentik URL. Test the SSO start URL.
  7. Gitea: auth source discovery URL https://auth.NEW/application/o/gitea/.well-known/openid-configuration; ./registerGiteaRunner.sh; server: cd ~/vanguard-stream && git remote set-url origin ssh://git@git.NEW:2222/techy/Media-Server-Setup.git and edit ~/.ssh/config.vanguard (Host line); dev box: git remote set-url origin …. Test sudo vanguard-deploy pull.
  8. Repo defaults: switchToClone.sh (REPO_SSH), deployLive.sh (HOST) — commit, push, pull.
  9. Streamers: re-add the dock at https://live.NEW/dock/, re-save Settings, "Add band + clock source" again. Members: https://HOST2/; Jellyfin apps → new server address. Remove the old Discord redirect and the old DNS.

C. Splitting the single main host into media / files / clips / sync (done 2026-11-01)

Before: one main host (ovh1.BASE) served copyparty at /, /about/, /clips/, /sync/, /dock/, /dump, /time. After: media.BASE = gateway at /; files.BASE = copyparty; clips.BASE; sync.BASE; live.BASE gains /dock/, /dock/clock/, /dump, /me, /time, /whoami. /time is only on live because the two pages that fetch it (the clock source and Go Live) are same-origin there. Authentik needs nothing: the domain-level provider covers every name under BASE, the outpost host (auth.) is unchanged.

  1. DNS: CNAMEs media, files, clips, sync → the box (keep ovh1 for ssh; it is no longer a web host).
  2. Dev box: pull/upload the repo (configure.py, about/, addCommunityMediaServer.sh, docs).
  3. Server, between streams: ./configure.py --full → main hostname media.BASE; Enter for the sub-hostnames (defaults files., clips., sync., …); bare domain redirect → https://media.BASE/; y. Then docker compose restart mediamtx (webrtcAdditionalHosts is now media.) and ./deployLive.sh (portal build picks up nothing new, but confirms /srv/live is served on live.).
  4. Streamers: OBS → Custom Browser Docks → replace the dock URL with https://live.BASE/dock/; dock Settings → save; Timecode band → "Add band + clock source" again (the clock browser source URL moved).
  5. Discord: re-pin https://media.BASE/ (members), https://clips.BASE/ (gamers), https://files.BASE/ (editors).
  6. Old ovh1 bookmarks stop working (no Caddy block, TLS fails) — point people at the gateway.

D. Adding a NEW host

./configure.py --apply cannot do this, and will not complain. It is stateless by design: it reads the hostnames back out of the Caddyfile (# vanguard:<role> markers), so a role that is not in there yet has no value to write and the site is simply left out. The containers come up, Caddy reloads, and the new name has no site block and therefore no certificate — which presents as an SSL handshake error, not as an error from the apply. This cost an hour on 2026-11-03 when diag. was added.

Introduce a new host with --full (asks for every hostname, defaults from the current config) or with --hosts, which is the non-interactive form and takes them positionally:

./configure.py --hosts AUTH WATCH GIT BOT LIVE FILES SYNC CLIPS DIAG     # "" leaves one unset

After that, --apply maintains it like any other host, because the marker is now in the file.

  1. DNS: CNAME (or A) for the new name → the box; check it resolves before continuing.
  2. Add the role to configure.py: a write_caddy block, a <ROLE>_REQUIRE_GROUP default in write_env, the prompt in full(), the positional in --hosts, and the line in show().
  3. ./configure.py --hosts … with every hostname including the new one.
  4. Verify the marker landed: grep -c "vanguard:<role>" Caddyfile → 1. Then curl -sI https://NEW/ → 302.
  5. Add it to addCommunityMediaServer.sh so a rebuild asks for it too.

Checklist after any change

dev$ for h in HOST files.BASE clips.BASE sync.BASE live.BASE diag.BASE auth.BASE watch.BASE git.BASE bot.BASE; do printf '%-26s ' $h; curl -s -o /dev/null -m 10 -w '%{http_code}\n' https://$h/; done
https://HOST/whoami            → your identity (gate works on the new name)
https://watch.BASE/sso/OID/start/authentik   → Jellyfin SSO round trip
https://git.BASE/user/login    → "Sign in with authentik" round trip
dock → Go Live → journalctl -u dumpd | grep mtx-auth   → allow publish
live.BASE → a window appears for the test stream, chip "webrtc"