Files

5.8 KiB
Raw Permalink Blame History

Caption Stack

Self‑hosted live captioning stack using Whisper STT and a private caption relay.

Repository

Table of Contents

Prerequisites

  • git (≥2.30)
  • docker & docker compose (v2.x)
  • Internet connection for submodule download

Quick start

git clone --recurse-submodules https://git.coopcloud.tech/captions.coop/captions-deploy.git
cd captions-deploy

Submodule handling

If the repository was cloned without --recurse-submodules:

git submodule update --init --recursive

The submodules provide the Whisper service (caption-local) and the relay/web UI (captionninja).

Configuration

The relay expects a private JSON file with room tokens. Create it from the example:

cd captionninja/relay
cp .example.private.json .caption-ninja-relay.private.json
# edit the file and add your secrets

.gitignore already excludes any *.private.json files from version control.

Docker Compose overview

docker-compose.yml defines three services:

Service Build context Port(s) Purpose
caption-local ./caption-local 8765:8765 Whisper speech‑to‑text service
captionninja-relay ./captionninja/relay 127.0.0.1:8787:8787 WebSocket relay, reads the private config
captionninja-web nginx:alpine 8080:80 Serves the static UI and proxies /ws to the relay

Running the stack

docker compose up --build -d

Monitor logs with docker compose logs -f.

Access URLs

Troubleshooting

  1. Missing submodules – run git submodule update --init --recursive.
  2. Port conflict – edit the corresponding ports in docker-compose.yml.
  3. Secret file not found – ensure .caption-ninja-relay.private.json exists; it is ignored by git.

Contributing

  1. Fork the repository.
  2. Create a feature branch.
  3. Make changes and test with docker compose up.
  4. Open a pull request.

Production deployment with Traefik

The stack is intended to be run behind Traefik which provides automatic TLS certificates via Let’s Encrypt and optional HTTP authentication.

Prerequisites for production

  • A domain name that resolves to the host where the stack will run.
  • An email address for Let’s Encrypt notifications (set in .env.production).
  • Docker Compose v2 or later.

Environment file

Create a file named .env.production in the repository root (it is ignored by Git):

LETSENCRYPT_EMAIL=you@example.com

The email will be used by Traefik to obtain certificates.

Secrets

  • The relay requires a private JSON file with room tokens (.caption‑ninja‑relay.private.json). It is already ignored by .gitignore.
  • (Optional) To protect the whole UI with a username/password, generate an htpasswd file:
mkdir -p secrets
htpasswd -nbB admin mySecretPassword > secrets/traefik.htpasswd

Then create a Docker secret: docker secret create traefik-htpasswd ./secrets/traefik.htpasswd.

Running the production stack

# Initialise submodules if you haven’t yet
git submodule update --init --recursive

# Build and start everything (Traefik will obtain TLS certs automatically)

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Traefik will expose the services on the hostnames you configured in docker-compose.prod.yml (default placeholders are caption.local.yourdomain.com, relay.yourdomain.com, and ui.yourdomain.com). Adjust them to match your actual DNS records.

Access URLs (when TLS is active)

  • UI: https://ui.yourdomain.com
  • Relay WebSocket: wss://relay.yourdomain.com/ws
  • Whisper API: https://caption.local.yourdomain.com

Optional HTTP Basic Auth

If you created the traefik-htpasswd secret, the UI, Relay and Whisper routers are already configured with a basicAuth middleware. Browsers will display a login prompt. To disable it, simply remove the traefik.http.routers.<name>.middlewares=... labels from docker-compose.prod.yml and redeploy.

Health‑check verification

Each service now defines a Docker health‑check (see docker-compose.prod.yml). Verify they are healthy with:

docker compose ps

The STATUS column should show healthy for caption-local, captionninja-relay, captionninja-web, and traefik.

Troubleshooting

  • Let’s Encrypt rate‑limit – If you repeatedly hit the ACME rate limit, delete the letsencrypt volume (docker volume rm captions-deploy_letsencrypt) and retry after an hour.
  • Missing submodules – Run git submodule update --init --recursive.
  • Port conflict – Ensure ports 80 and 443 are free on the host, or change the mappings in docker-compose.prod.yml.
  • Auth failures – Verify the secret exists (docker secret ls) and that the usersFile path in the middleware label points to /run/secrets/traefik-htpasswd.

Contributing

  1. Fork the repository.
  2. Create a feature branch.
  3. Make changes and test with the production compose file:
    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
    
  4. Open a pull request.

License

This project is licensed under the GNU Affero General Public License v3.0 – see the LICENSE file. This project is licensed under the GNU Affero General Public License v3.0 – see the LICENSE file.