Documentation
Media Server Setup
Link the Plex library you already run so your server can hold watch parties — Set up, Status and Remove, from the admin console.
Watch parties play from a machine you control: the Plex library you already run, on your own hardware. Asbern connects to it. We supply no media, host nothing, and never hold your Plex sign-in.
Everything happens in /menu › Other › Admin › Media, which has five rows: Set up, Status, Remove, Voice status and Music.
Before You Start
- A computer that stays on, with the library on it.
- About 5 Mbps of upload per viewer.
- A hardware video encoder, or roughly 4–6 CPU cores per 1080p stream.
- Usually nothing to set up on your router for the watch party: a box that may open router ports (it asks when you install it) opens what it needs while a screening runs, and every screening gets its own address (The watch party on your box). Where it cannot, Turn on says why.
Set Up
Watch plays from your own Plex, on a machine that stays on — and that machine is Your Asbern Box. No box yet? /menu › Other › Admin › Media › Set up says so and sends you to /menu › Other › Admin › Box first: pick where it runs and paste the one line it gives you there.
With your box online, Set up has three steps, and it remembers where you got to, so you can stop and come back:
- Point it at Plex — press Link Plex. Your box asks Plex for a sign-in link: press Open Plex to approve, sign in to Plex (on a phone too), and the card finishes by itself. Your box finds your server and keeps the sign-in. There is no token to copy and no password typed in Discord, and the sign-in never reaches us. The same Link Plex sits on your box's card.
- Open the screening room — on a box, press Turn on: your box orders its own certificate and starts the rooms, and Rooms sets one or two. Nothing to type, and usually nothing to set up on your router — see The watch party on your box.
- Prove it works — a check that every piece answers, end to end. Members see Watch disabled until you press Turn on beside Verify here.
The Watch Party on Your Box
On an Asbern box the watch party runs beside your companion, and the box looks after all of it.
- Turn on (the Open the screening room step of Set up) — your box orders its own certificate, starts the rooms and is ready. The first time takes a minute or two; Check again shows where it is.
- Rooms — one or two watch parties at once. A playing room costs your machine about 4–6 CPU cores, and every viewer about 5 Mbps of your upload.
- Every screening has its own address. When a screening starts, the links members are handed point at a name made for that screening under Asbern's names domain. When it ends the name is taken down, so a link from an earlier screening opens nothing.
- One door, only while it is needed. Your box opens one port on your router for the watch party while something is playing, and closes it again when the room is idle. It never takes the web's standard ports, so whatever else you run at home keeps them. If your box may not open router ports, your router will not let it, or your connection has no public IPv4 address, Turn on and the Room step say so and what to do.
- The certificate stays on your box. Its keys are made there and never leave it. Asbern publishes only the one-time value that proves the name is yours, and takes it down straight after.
Viewers' browsers connect to your home directly, so a viewer can see your home's internet address — the same as any game your friends join.
Watching from home? If a link will not open on your home network, open it from outside it (your phone's mobile data, for example): some home connections cannot reach their own outside address.
No Names Zone on This Deployment?
On a deployment with no names zone (NAMES_ZONE blank), the watch party is reached at an address of your
own.
- On a box, Set address on the Room step takes your domain, and the box serves the watch party on
127.0.0.1:8080for your own reverse proxy (which adds HTTPS). No reverse proxy? Rundocker exec -it asbern-agent asbern watch expose standardon the box (highfor 8080/8443), forward ports 80 and 443 to it, and press Turn on — it then gets its own certificate, and anyone who opens your address can see your home's internet address. Room 2 isroom2.<your address>. - A companion linked before your box needs a free web address (duckdns.org works) and ports 80 and 443 forwarded to your machine, and the watch-party file below; a new setup never does.
The Watch-Party File
Only for a companion linked before your box: the Open the screening room step's Download the watch
party button gives you the projector as a file, the same way the companion comes:
docker load -i asbern-cinema-engine-VERSION.tar.gz. Owncast
(the stream) and Caddy (the web address and its certificate) are their projects' own public images,
which this file fetches.
Save it as compose.yml in an empty folder, put the .env the step prints beside it — your address,
the stream key, the rooms and the projector's name — and run docker compose up -d there. It needs
Docker Compose 2.23.1 or newer. It joins the asbern network the companion made, so the companion
finds the projector by name; nothing else on your network can reach it.
To change the number of rooms, press Rooms in the same step and put the two lines it gives you in
.env, then docker compose up -d again. To update, download and load the new file, change the
version on the ASBERN_WATCH_IMAGE line, and docker compose up -d.
Linked a companion before 0.8.0 that runs outside Docker? Its page shows an ENGINE_SECRET=… line under Projector. Add it to
the same .env and run docker compose up -d again: the projector then answers only your companion, and
the file addresses it is handed travel sealed and never come back in its answers.
# The Asbern watch party — the part of Watch that runs on your own machine.
#
# PARKED (0.8.0): an Asbern box makes its watch party itself — no file, no .env and
# nothing to set up on your router. This file is for a deployment with no names zone
# and for an install made before the box (the docs' "No names zone" section).
#
# Save this file as compose.yml in an empty folder, put the .env from Discord's
# "Open the screening room" step beside it, and run: docker compose up -d
#
# Before that, once: download the watch party from the same step and load it with
# docker load -i <the file> — the projector image is not on any registry, which is
# why pull_policy is never. Owncast and Caddy are their projects' public images.
#
# Ports 80 and 443 must be forwarded to this machine: 80 is how the free HTTPS
# certificate is issued, 443 is what your members' browsers reach. Nothing else
# is exposed: the projector and Owncast publish on 127.0.0.1 only.
#
# One room is the default. Rooms are profiles, so the number is two lines in the
# .env (SCREENING_ROOMS and COMPOSE_PROFILES) — Discord's Rooms button gives both.
# Room 1 is your address; room 2 is room2.<your address>.
#
# Running the Windows app? Copy the ENGINE_SECRET line from Asbern Companion's
# page into the .env too, so the projector answers only your companion.
name: asbern-watch
x-logs: &logs
driver: json-file
options: { max-size: "10m", max-file: "3" }
x-engine: &engine
image: ${ASBERN_WATCH_IMAGE:?set ASBERN_WATCH_IMAGE in .env - the Room step in Discord gives you the line}
labels: { com.centurylinklabs.watchtower.enable: "false" }
pull_policy: never
restart: unless-stopped
init: true
environment: &engine-env
ENCODER: ${ENCODER:-libx264}
WIDTH: ${VIDEO_WIDTH:-1920}
HEIGHT: ${VIDEO_HEIGHT:-1080}
FPS: ${VIDEO_FPS:-60}
VIDEO_BITRATE: ${VIDEO_BITRATE:-8M}
VIDEO_BUFSIZE: ${VIDEO_BUFSIZE:-16M}
IDLE_STOP_MINUTES: ${IDLE_STOP_MINUTES:-10}
networks: [watch, asbern]
logging: *logs
x-owncast: &owncast
image: owncast/owncast:latest
restart: unless-stopped
networks: [watch, asbern]
# The stream key is a flag: Owncast ignores it as an environment variable.
command: ["-streamkey", "${OWNCAST_STREAM_KEY:?set OWNCAST_STREAM_KEY in .env - the Room step in Discord gives you the line}"]
logging: *logs
services:
# The only thing the outside world reaches. Its Caddyfile is written at start
# for exactly the rooms that are running (the entrypoint below).
caddy:
image: caddy:2
container_name: asbern-watch-caddy
restart: unless-stopped
logging: *logs
ports:
- "80:80"
- "443:443"
environment:
CINEMA_DOMAIN: ${CINEMA_DOMAIN:?set CINEMA_DOMAIN in .env - the Room step in Discord gives you the line}
SCREENING_ROOMS: ${SCREENING_ROOMS:-1}
configs:
- source: caddy-entrypoint
target: /usr/local/bin/caddy-entrypoint.sh
volumes:
- "${CADDY_DATA_ROOT:-./data/caddy}:/data"
- "${CADDY_CONFIG_ROOT:-./data/caddy-config}:/config"
entrypoint: ["/bin/sh", "/usr/local/bin/caddy-entrypoint.sh"]
networks: [watch]
cinema-1:
<<: *engine
profiles: ["room1"]
container_name: asbern-watch-cinema-1
ports: ["127.0.0.1:${CINEMA_PORT_1:-8099}:8099"]
environment:
<<: *engine-env
OWNCAST_RTMP: rtmp://owncast-1:1935/live/${OWNCAST_STREAM_KEY:?set OWNCAST_STREAM_KEY in .env}
ENGINE_SECRET: ${ENGINE_SECRET:-}
owncast-1:
<<: *owncast
profiles: ["room1"]
container_name: asbern-watch-owncast-1
ports: ["127.0.0.1:${OWNCAST_PORT_1:-8090}:8080"]
volumes: ["${OWNCAST_DATA_ROOT:-./data/owncast}/1:/app/data"]
cinema-2:
<<: *engine
profiles: ["room2"]
container_name: asbern-watch-cinema-2
ports: ["127.0.0.1:${CINEMA_PORT_2:-8100}:8099"]
environment:
<<: *engine-env
OWNCAST_RTMP: rtmp://owncast-2:1935/live/${OWNCAST_STREAM_KEY:?set OWNCAST_STREAM_KEY in .env}
ENGINE_SECRET: ${ENGINE_SECRET:-}
owncast-2:
<<: *owncast
profiles: ["room2"]
container_name: asbern-watch-owncast-2
ports: ["127.0.0.1:${OWNCAST_PORT_2:-8091}:8080"]
volumes: ["${OWNCAST_DATA_ROOT:-./data/owncast}/2:/app/data"]
networks:
watch:
driver: bridge
# The network the companion joined on its first run (docker network create asbern).
# The companion finds the projector and Owncast on it by name: cinema-1, owncast-1.
asbern:
external: true
name: asbern
configs:
# Writes a Caddyfile for exactly the rooms that exist, then runs Caddy.
# $$ is how compose writes a literal $ — the script receives a single one.
caddy-entrypoint:
content: |
#!/bin/sh
set -eu
DOMAIN="$${CINEMA_DOMAIN:?CINEMA_DOMAIN is not set}"
ROOMS="$${SCREENING_ROOMS:-1}"
case "$$ROOMS" in
1|2|3|4) : ;;
*) echo "caddy-entrypoint: SCREENING_ROOMS must be 1-4, got '$$ROOMS'" >&2; exit 1 ;;
esac
OUT=/etc/caddy/Caddyfile
: > "$$OUT"
n=1
while [ "$$n" -le "$$ROOMS" ]; do
if [ "$$n" -eq 1 ]; then host="$$DOMAIN"; else host="room$${n}.$${DOMAIN}"; fi
cat >> "$$OUT" <<EOF
$${host} {
@admin path /admin* /api/admin* /api/integrations*
handle @admin {
respond 403
}
forward_auth cinema-$${n}:8099 {
uri /gate
header_up X-Forwarded-Uri {uri}
copy_headers Set-Cookie
}
handle /watch* {
redir * /?{query} 302
}
handle {
reverse_proxy owncast-$${n}:8080
}
}
EOF
n=$$((n + 1))
done
caddy fmt --overwrite "$$OUT" 2>/dev/null || true
echo "caddy-entrypoint: $${ROOMS} room(s) on $${DOMAIN}"
exec caddy run --config "$$OUT" --adapter caddyfile
Status
/menu › Other › Admin › Media › Status shows everything Watch needs in one place — the switch, your machine, Plex and the watch address — and what is left to do. It only reads; it changes nothing.
Remove
/menu › Other › Admin › Media › Remove turns Watch off for everyone, unlinks your machine and forgets Plex, after showing you what it will do.
- Remove unlinks your whole box, game servers and music included.
- Your address, rooms and watch history are kept. Linking a machine again starts from /menu › Other › Admin › Box, with a fresh line.
- It does not touch plex.tv. To remove the device there too, remove it from your Plex account.
Voice Status
/menu › Other › Admin › Media › Voice status is a switch, and it is on unless you turn it off.
While something plays, the voice channel it plays in shows what it is under the channel's name —
🎬 Now watching:
- Asbern needs Set Voice Channel Status and Manage Channels on that voice channel. Without them nothing is shown, and the Voice status row names what is missing.
- Turning it off takes down the line Asbern put up, and leaves every voice channel alone.
What Members Get
Once Watch is set up, members open it from /menu › Community › Watch — the first door there:
- /menu › Community › Watch › Resume — every paused film and episode. A show rolls into the next episode on its own.
- /menu › Community › Watch › Browse — your library with posters, filtered by films, TV shows or ⭐ Saved — the titles you saved with ⭐ Save on a title's card.
- /menu › Community › Watch › Music — your Plex music, played by your box into the voice channel you are in. Members queue songs and vote to skip; admins skip, pause, stop and change the volume. Music in Media switches it off. Music has the rest.
- /menu › Community › Watch › Add New — search your library and the catalogue together, and request what nobody has yet. Your own Radarr and Sonarr fetch it.
- /menu › Community › Watch › Live TV — a guide of what is on, where your Plex has live TV.
A screening belongs to the voice channel it started from: only people in that channel, or an admin, can pause, switch or stop it. Time watched counts towards pay and your resume points.
Jellyfin
Run Jellyfin instead of Plex, or beside it? Your box can link the Jellyfin you already run, with one press of Link Jellyfin on your box's card and a code you type into Jellyfin. On a server with no Plex, members then browse and search your Jellyfin library under /menu › Community › Watch; playing together, Resume and Live TV still need Plex. Link Jellyfin has the steps.
Troubleshooting
Open Plex to approve stopped working. A sign-in link does not last for ever. Press New link on the same card for a fresh one.
Linked, but not answering. Check the machine is awake and Docker is running (on Windows and a Mac, Docker Desktop). Status tells "never linked" apart from "linked and silent" — the second means the pairing is fine and the box is not running.
Plex works on the machine but not for your box. Check, under /menu › Other › Admin › Box, has a Plex row that says your box cannot reach Plex, or names the Unraid setting to turn on. There is no address to type.
Playback starts and then drops. Almost always upload bandwidth. Encoding happens on your machine, and so does the ceiling.
What it Costs
Nothing. Asbern is free: every feature on this page is included in every server, and no card is needed.
