Nojoin Deployment & Configuration Guide

This guide is for operators deploying and running Nojoin.

If you just want the fastest path to a working instance, start with GETTING_STARTED.md and return here when you need deeper hosting, networking, or upgrade guidance.

Core Requirements

Compose Files

The repository does not ship a separate Docker Compose development override.

Quick Deployment

  1. Clone the repository.
  2. Create your local deployment files:

     cp docker-compose.example.yml docker-compose.yml
     cp .env.example .env
    
  3. Set FIRST_RUN_PASSWORD in .env.
  4. Set DATA_ENCRYPTION_KEY in .env before first production use.
  5. Adjust WEB_APP_URL if the deployment is not local-only.
  6. Review docker-compose.yml and apply any private or machine-specific changes.
  7. Start the stack:

    docker compose up -d
    
  8. Open https://localhost:14443.
  9. Use Chrome on Windows, Linux, or macOS for shared-audio live recording, another Chromium-family browser on Windows or Linux, or Chrome on Android/iOS for microphone-only live recording. Other Chromium-family browsers on macOS are best-effort. Other browsers can still review and administer Nojoin.

Nojoin refuses first initialisation if FIRST_RUN_PASSWORD is missing. If you add or change it, redeploy the stack before using the setup wizard. If FIRST_RUN_PASSWORD, DATA_ENCRYPTION_KEY, REDIS_PASSWORD, or the tracked PostgreSQL password placeholder are left at their example values, Nojoin now emits startup log warnings and an authenticated frontend warning toast. Those warnings are advisory only; operators are still responsible for replacing the placeholder secrets in .env.

The compose template is already configured for GPU inference.

The compose files now health-gate the web stack so frontend waits for a healthy api, and nginx waits for healthy api plus frontend before it is considered ready.

When doing targeted starts from a fully stopped stack, remember that Docker Compose does not auto-start an omitted dependent service. If you want the proxy back as part of a partial startup, include nginx explicitly:

docker compose up -d api frontend nginx

DATA_ENCRYPTION_KEY is strongly recommended for every non-ephemeral deployment. Earlier releases relied on the auto-generated data/.data_encryption_key fallback alone, which meant encrypted calendar secrets and tokens could become unreadable if the app data directory was replaced while the database volume was preserved. Setting a stable DATA_ENCRYPTION_KEY avoids that class of failure.

If you are developing from local source instead of operating a deployment, read DEVELOPMENT.md.

GPU Support

Linux

  1. Install the proprietary NVIDIA drivers.
  2. Verify GPU visibility with:

    nvidia-smi
    
  3. Install the NVIDIA Container Toolkit.
  4. Configure Docker for NVIDIA runtime support:

    sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
    

The default .env.example enables NVIDIA_VISIBLE_DEVICES=all and NVIDIA_DRIVER_CAPABILITIES=compute,utility.

Windows

Worker Container Startup

The worker container starts Celery without preloading inference models. Nojoin keeps GPU memory idle at startup, then queues worker-side model preparation for the configured Whisper model, Pyannote diarisation, and voice embeddings. The worker validates those assets on CPU where possible, caches them on disk, and releases model objects and CUDA memory before returning to idle.

If an administrator switches transcription to Parakeet or Canary, Nojoin queues preparation for the selected ONNX ASR model after the setting is saved. Live and final processing still load inference models only for active work. After each worker task, Nojoin releases model caches and clears CUDA memory when keep_models_loaded is unset or false. Set keep_models_loaded=true only if you deliberately prefer warmer repeated processing over idle VRAM.

GPU Acceleration

The worker image installs Triton in its virtual environment so Whisper word-level timestamps use GPU-accelerated kernels. Without Triton, whisper/timing.py falls back to slower CPU-based implementations for word alignment.

Text embedding (used during AI-generated meeting intelligence) uses the ONNX Runtime CUDA execution provider when available, with an automatic CPU fallback.

The Parakeet and Canary ASR engines also use ONNX Runtime CUDA. Some ONNX graph operations are inherently CPU-pinned; the resulting memcpy overhead is expected and does not indicate a configuration problem.

CPU-Only Deployment

If you do not have a compatible NVIDIA GPU:

  1. Open docker-compose.yml.
  2. Remove the deploy section under the worker service.
  3. Start the stack normally with docker compose up -d.

Processing will be slower, but the application remains usable.

Configure .env

Create .env from .env.example and treat it as the canonical operator configuration file. The compose stack derives internal service URLs for PostgreSQL, Redis, and Celery automatically, so those values are intentionally not part of .env.example. Keep any secrets, private mounts, or machine-specific overrides in your local docker-compose.yml, not in the tracked template. Nojoin auto-generates and persists its JWT signing keyring under data/.secret_keys.json in the default deployment, migrating any legacy data/.secret_key file on startup, so no .env setting is required for that. Nojoin can also auto-generate data/.data_encryption_key, but operators should treat that as a fallback rather than the primary persistence strategy.

Always Set

Change for Remote or Reverse-Proxy Deployments

Common Optional Values

DATA_ENCRYPTION_KEY Guidance

Custom Frontend Build Value

For calendar-specific registration detail, read CALENDAR.md.

Configuration Model

Nojoin splits configuration between:

The first-run setup wizard can pre-fill many values from environment variables to speed up deployment. On uninitialised systems, that prefill flow is itself locked behind FIRST_RUN_PASSWORD.

Remote Access and Trusted Public Origin

If you expose Nojoin beyond localhost:

For publicly reachable deployments, use a VPN or a secure reverse proxy rather than exposing the service casually. For internet-exposed deployments, treat FIRST_RUN_PASSWORD as a deployment secret and avoid logging request headers that could capture it during the setup flow.

Reverse Proxy Requirements

When fronting Nojoin with Nginx, Caddy, Traefik, or another reverse proxy:

Loopback Port Binding (DEP-001)

By default, the bundled Nginx proxy publishes ports 14141 and 14443 bound to the loopback interface (127.0.0.1) rather than all host interfaces (0.0.0.0). This ensures that if you place Nojoin behind an edge reverse proxy (such as Caddy, Traefik, or a tunnel) on the same host, the bundled proxy is not exposed directly to the public internet, preventing bypass of the edge proxy’s authentication, rate limiting, or filtering.

  1. Proxy to the HTTPS endpoint, not the plain HTTP port.
  2. By default that means the host-facing port 14443.
  3. Disable upstream certificate verification because Nojoin uses a self-signed internal certificate by default.
  4. Keep WEB_APP_URL aligned with the public origin.
  5. Keep the public HTTPS origin stable so browser capture, session cookies, invitation links, and OAuth callbacks all target the same Nojoin site.

Caddy Example

nojoin.yourdomain.com {
    reverse_proxy localhost:14443 {
        transport http {
            tls_insecure_skip_verify
        }
    }
}

Nginx Example

location / {
    proxy_pass https://localhost:14443;
    proxy_ssl_verify off;
    proxy_set_header Host $host;
}

Upgrading and Migration

Live Pipeline Readiness Notes

Canonical Cutover Notes

Database Migrations

Useful Alembic commands:

alembic upgrade head
alembic revision --autogenerate -m "message"

Updating a Deployment

Pull-First Installations

docker compose down
docker compose pull
docker compose up -d

Local Custom Builds

docker compose down
docker compose build
docker compose up -d

Use this only if your local docker-compose.yml includes custom build directives.

Nojoin also exposes installed and latest published version information in Settings > Updates. The installed version is read from build metadata embedded into the API image, with local source builds falling back to docs/VERSION.

Release Model

Nojoin uses a unified lock-step release model: