Skip to content

Deployment Guide

Overview

Step What you do
1 Create the project directory and download configuration files
2 Generate secrets
3 Edit .env with your values
4 Edit livekit-server.yaml with your LiveKit secret
5 Download nginx-routing.conf
6 Edit keycloak-realm.json with your values
7 Edit compose.yml with your values
8 Pull images and start
9 Run database migrations
10 Verify containers
11 First login

Prerequisites

Complete the Prerequisites checklist first: Docker installed, DNS configured, firewall ports open, OIDC provider ready.

This guide uses nginx-proxy + acme-companion as the reverse proxy. Follow the nginx-proxy setup guide to install it before continuing. If you prefer Traefik, follow the Traefik guide instead and replace VIRTUAL_HOST environment variables with Traefik labels in the compose file.


Step 1: Create the project directory and download configuration files

mkdir -p ~/docker/meet
cd ~/docker/meet
docker network create proxy   # required for the compose file to start

# Download all configuration files
curl -L -o .env https://meet-doc.bebopo.eu/examples/docker.env
curl -L -o livekit-server.yaml https://meet-doc.bebopo.eu/examples/livekit-server.yaml
curl -L -o nginx-routing.conf https://meet-doc.bebopo.eu/examples/routing.conf
curl -L -o keycloak-realm.json https://meet-doc.bebopo.eu/examples/keycloak-realm.json
curl -L -o compose.yml https://meet-doc.bebopo.eu/examples/compose.yml

All files contain REPLACE_ME placeholders or example domains. The next steps walk through customizing each one.

Step 2: Generate secrets

DJANGO_SECRET=$(openssl rand -hex 32)
LIVEKIT_SECRET=$(openssl rand -hex 32)
DB_PASSWORD=$(openssl rand -hex 16)
KC_DB_PASSWORD=$(openssl rand -hex 16)
KC_CLIENT_SECRET=$(openssl rand -hex 16)
KC_ADMIN_PASSWORD=$(openssl rand -hex 16)

echo "DJANGO_SECRET=$DJANGO_SECRET"
echo "LIVEKIT_SECRET=$LIVEKIT_SECRET"
echo "DB_PASSWORD=$DB_PASSWORD"
echo "KC_DB_PASSWORD=$KC_DB_PASSWORD"
echo "KC_CLIENT_SECRET=$KC_CLIENT_SECRET"
echo "KC_ADMIN_PASSWORD=\$KC_ADMIN_PASSWORD"

Copy the output - you will paste these values into the files below.

Step 3: Edit the environment file

The .env file was downloaded from meet-doc.bebopo.eu/examples/docker.env. Replace the hostnames and every REPLACE_ME with the values generated in Step 2:

  • meet.example.com → your Meet domain
  • auth.example.com → your Keycloak domain
  • livekit.example.com → your LiveKit domain
  • Every REPLACE_ME → the corresponding secret from Step 2

DJANGO_SETTINGS_MODULE=meet.settings is required - the backend will not start without it.

LIVEKIT_API_URL must be the public HTTPS URL. It is returned to browsers as the WebSocket address. Do not use the Docker-internal http://livekit:7880.

For documentation on the variables in this .env file, see the environment variables reference.

Step 4: Edit the LiveKit configuration

Replace REPLACE_ME in livekit-server.yaml with the LIVEKIT_SECRET generated in Step 2. This must match LIVEKIT_API_SECRET in .env.

Step 5: Nginx routing config

Already downloaded in Step 1. The file is mounted into the frontend container in Step 7. The upstream names (backend, frontend) match the Docker Compose service names and resolve via Docker's internal DNS.

X-Forwarded-Proto https must be hardcoded (not $scheme) to prevent SSL redirect loops.

Step 6: Edit the Keycloak realm

In keycloak-realm.json, replace: - REPLACE_ME → the KC_CLIENT_SECRET from Step 2 - meet.example.com → your Meet domain (in both redirectUris and webOrigins) - admin@example.com → your email address (optional) - ChangeMe! → the password you want for the meet-admin user

The redirect URI must end with /api/v1.0/callback/. Meet uses a versioned API path, not the standard /oidc/callback/.

Step 7: Edit the compose file

In compose.yml, replace: - REPLACE_ME → the KC_ADMIN_PASSWORD from Step 2 - meet.example.com / auth.example.com / livekit.example.com → your domains - you@example.com → your email (for Let's Encrypt notifications)

Database passwords (${DB_PASSWORD}, ${KC_DB_PASSWORD}) are read automatically from .env by Docker Compose.

Step 8: Pull images and start

docker compose pull
docker compose up -d

The reverse proxy will detect the new containers and request TLS certificates automatically. This typically takes under a minute once DNS has propagated.

Step 9: Run database migrations

The backend's healthcheck (python manage.py check) confirms it is ready. Once the backend container shows healthy in docker compose ps, run:

docker compose exec backend python manage.py migrate

All migrations should complete with OK.

If this command fails with invalid USER value, your Docker daemon has user namespace remapping enabled. Use docker exec -u root instead (see Troubleshooting below).

Step 10: Verify all containers are running

docker compose ps

All services should show running:

NAME                 SERVICE       STATUS
meet-backend-1       backend       running
meet-frontend-1      frontend      running
meet-celery-1        celery        running
meet-postgresql-1    postgresql    running
meet-redis-1         redis         running
meet-livekit-1       livekit       running
meet-keycloak-1      keycloak      running
meet-kc-postgresql-1 kc-postgresql running

Step 11: First login

  1. Open https://meet.example.com
  2. You are redirected to https://auth.example.com for login
  3. Log in with meet-admin / ChangeMe! (the user from keycloak-realm.json)
  4. You are redirected back to Meet and can create your first room

The Keycloak admin console is at https://auth.example.com. Log in with admin and the password you set in KC_BOOTSTRAP_ADMIN_PASSWORD.


Troubleshooting

App returns 404 on all routes DJANGO_SETTINGS_MODULE is missing from .env. Add DJANGO_SETTINGS_MODULE=meet.settings and restart the backend:

docker compose restart backend

502 Bad Gateway on all routes The frontend nginx is not listening on port 8083. Check for errors:

docker compose logs frontend
Site keeps loading / infinite redirect loop The API is returning 301 redirects. Ensure proxy_set_header X-Forwarded-Proto https is hardcoded (not $scheme) in the routing config.

"Invalid parameter: redirect_uri" from Keycloak The Keycloak client's redirect URI must be https://meet.example.com/api/v1.0/callback/, not /oidc/callback/. Fix via the Keycloak admin UI: 1. Go to https://auth.example.com → Clients → meet → Settings 2. Set Valid redirect URIs to https://meet.example.com/api/v1.0/callback/ 3. Set Web origins to https://meet.example.com 4. Save

Disconnected from meeting after a few seconds LiveKit WebSocket is not reachable. Verify: - LIVEKIT_API_URL=https://livekit.example.com (public URL, not Docker-internal) - The livekit.example.com DNS record exists and points to your server - The reverse proxy has issued a TLS cert: curl -o /dev/null -w "%{http_code}" https://livekit.example.com/ should return 200

No audio or video after joining Firewall is blocking LiveKit media ports. Verify that 7881/TCP and 7882/UDP are open in your firewall or cloud security group.

docker compose exec fails with "invalid USER value" Docker user namespace remapping is active on this system. Use docker exec directly with an explicit user:

docker exec -u root <container-name> <command>
# Example:
docker exec -u root meet-backend-1 python manage.py migrate


Next steps