Install a station with weavesignal.sh
Loopback only, unless you ask.
weavesignal.shinstalls the Compose bundle, which keeps every listener on loopback.--public station.examplealso puts a reverse proxy with a certificate in front of it and turns on TURN over TLS: Publish a station from the Compose bundle.
weavesignal.sh is the Compose route’s installer (ADR-0022).
It finds Podman or Docker, makes the station’s secrets on your machine, picks free ports (ADR-0075), writes the settings file, starts the bundle, and tells you where to sign in.
Running it again changes nothing that is already set.
Before you start
Section titled “Before you start”- Podman 5 with
docker-composeas its compose provider, or Docker Engine with the Compose plugin. On Fedora,dnf install docker-compose; on Ubuntu,apt install docker-compose-v2. The script never usessudo. - Not
podman-compose. It ignores the settings file the bundle gives each service (the top-levelconfigs), so the station would start without them. The script refuses it, says why, and names the fix: installdocker-compose, whichpodman composethen prefers, or point Podman at it withPODMAN_COMPOSE_PROVIDER=/path/to/docker-compose, or use Docker. - On Podman,
docker-composereaches Podman through your user’s API socket. Ifpodman composecannot connect, turn the socket on withsystemctl --user enable --now podman.socket. ss(iproute2),curlandbash, which most Linux systems have.- The repository, checked out where it will stay: the script and the bundle are in
deploy/compose/, and the bundle mounts playout’s scripts (playout/) and the default loop (assets/default-loop/) from it. - A server that is not overloaded. Live audio needs CPU on time: on a busy shared server the listener stream runs dry (Memory and CPU).
The server image
Section titled “The server image”The bundle runs the published server image, forgejo.weavesignal.top/weavesignal/weavesignal-server:1.0.0.
The script pulls it when it is not on the machine.
To run an image built from the repository instead, build it, then give its tag with --image:
podman build -t localhost/weavesignal-desk-spike:dev spike/desk-imagejust desk-image server-imagedeploy/compose/weavesignal.sh --image localhost/weavesignal-server:m1The first line builds the base image that just desk-image builds on.
just server-image tags localhost/weavesignal-server:m1; set WEAVESIGNAL_SERVER_IMAGE to build another tag.
The image is kept in the settings file (SERVER_IMAGE), so later runs use it without --image.
Install
Section titled “Install”From the repository:
deploy/compose/weavesignal.shThe script:
- Uses Podman if
podman composeworks throughdocker-compose, else Docker withdocker compose, and says which.--engine podmanor--engine dockerchooses. - Makes the station’s identifier and its secrets from the kernel’s random source: LiveKit’s API secret, the listener source password, PostgreSQL’s password, the station key (
CONTROL_ADMIN_TOKEN), and three keys: the chat’s email key, the staff email key and the API token key. - Gives each port its default, or the next suggested alternative if the default is taken, and says so (Ports).
- Writes
deploy/compose/.envwith mode 600. The secrets are written there and nowhere else, and are never printed. - Starts the bundle, waits up to 90 s for the control plane and its database, and prints the addresses.
It ends like this:
WeaveSignal is running (project 'weavesignal', podman).
Control plane http://127.0.0.1:7310/ (health: /health) Chat http://127.0.0.1:7311/ Listeners http://127.0.0.1:7312/programme.opus Live room ws://127.0.0.1:7880 (media on UDP 7882)
Sign in: the studio uses the control plane's address above with the admin token, which isCONTROL_ADMIN_TOKEN in deploy/compose/.env (mode 600). It is not shown here.Every listener is on loopback: nobody off this machine can join or listen. To publish thestation, run this again with --public and the name that reaches this machine.The SRT link (7313), the playout feed (7315), the desk’s feed (7317), and the ports above the playout feed and above the listener port are internal: the script checks they are free but never shows or publishes them.
Sign in to the studio app with the station key, then add yourself as staff: Add staff and sign in.
Open call-in
Section titled “Open call-in”Call-in stays closed until the station says how a caller can ask for their call to be taken down (ADR-0124).
- In
deploy/compose/.env, setCONSENT_CONTACTto an email address or a link, such asCONSENT_CONTACT=calls@station.example. - Run
deploy/compose/weavesignal.shagain.
The player page then offers call-in, and the consent words name that contact.
Choose ports yourself
Section titled “Choose ports yourself”Give any port in the environment:
CONTROL_PORT=17310 LIVEKIT_MEDIA_PORT=52000 deploy/compose/weavesignal.shIf a port you give is taken, the script stops, names it and suggests a free alternative.
The names are those in deploy/compose/.env.example: CONTROL_PORT, CHAT_PORT, LISTENERS_PORT, SRT_INGEST_PORT, PLAYOUT_FEED_PORT, DESK_FEED_PORT, LIVEKIT_HTTP_PORT and LIVEKIT_MEDIA_PORT.
Settings only
Section titled “Settings only”--no-start writes the settings file and stops there (and changes nothing about start at boot); the Quadlet route starts from it.
Publish it
Section titled “Publish it”--public station.example is the public route, in its own page; --public off takes it away, and --renew-turn restarts LiveKit after the proxy has renewed the certificate.
Update
Section titled “Update”- Give the new image:
--imagewith its name, orSERVER_IMAGEin the settings file (for an image you build, rebuild it under the same tag). - Run the script again.
It keeps the settings file’s secrets and ports, says Settings unchanged, and recreates only the containers whose image or settings changed.
Start again after a reboot
Section titled “Start again after a reboot”The bundle’s containers restart unless stopped, and with Docker they come back at boot because Docker starts with the machine. Podman restarts nothing at boot by itself, so on Podman the script turns two things on after the station is up (ADR-0138), and says which it changed:
- lingering for your user (
loginctl enable-linger), so your user’s services run without a login session; - your user’s
podman-restart.service(systemctl --user enable podman-restart.service), which starts the containers again after a reboot. It is enabled, not started: the station is already up, and starting it now would also start your other stopped containers that have a restart policy.
Start at boot (Podman, user kettle): turned on lingering (loginctl enable-linger): your services now run without a login session enabled podman-restart.service (systemctl --user enable): the containers start again after a reboot --no-boot-start skips both; --uninstall leaves them as they are.Something already on is left as it was and reported (lingering was already on).
If a step is refused (turning on lingering can need an administrator; the script never waits at a password prompt for it), the script says so, prints the command to run by hand, and finishes the install: the station is already up.
Without lingering, podman-restart.service runs only when you log in, and the script says the containers start again at your next login rather than at boot.
Docker is untouched.
--no-boot-start skips both steps:
deploy/compose/weavesignal.sh --no-boot-startNeither step is undone by --uninstall, because other containers of yours may rely on them; turn them off by hand (systemctl --user disable podman-restart.service, loginctl disable-linger) if you want.
Or run the station as Quadlets, which systemd starts.
See the station
Section titled “See the station”deploy/compose/weavesignal.sh --statusRemove it
Section titled “Remove it”deploy/compose/weavesignal.sh --uninstallThis stops and removes the containers and keeps the data volumes (the database, carts, library, recordings) and the settings file, so running the script again brings the same station back.
To delete everything, including the database and the secrets:
deploy/compose/weavesignal.sh --uninstall --purge --yesWithout --yes, a terminal asks you to type purge.
More than one station on a machine
Section titled “More than one station on a machine”Give each its own project name, settings file and ports:
deploy/compose/weavesignal.sh --project kettle --env-file ~/stations/kettle.envThe volumes are named after the project (kettle_postgres-data), so stations never share data.
How it is tested
Section titled “How it is tested”just test-install runs the script end to end on its own project and ports: a taken port refused with an alternative, a taken default moved to its alternative, the install answering, no secret in the output, the settings file mode 600, a second run unchanged, --uninstall keeping the database, and --purge leaving no container, volume, network or setting.
It passes --no-boot-start, and just boot-check tests start at boot with loginctl, systemctl and the engines stubbed, so neither changes anything on the machine it runs on.
Both are part of just ci, which runs on Podman 5.8 with docker-compose.
The public route has also run on a dedicated public server with rootless Podman and docker-compose (2026-11-03).
Docker Engine has not been tried.
Text and images CC BY-SA 4.0; code samples AGPL-3.0-only, as the code is. Licences.