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 domainauth.example.com→ your Keycloak domainlivekit.example.com→ your LiveKit domain- Every
REPLACE_ME→ the corresponding secret from Step 2
DJANGO_SETTINGS_MODULE=meet.settingsis required - the backend will not start without it.
LIVEKIT_API_URLmust be the public HTTPS URL. It is returned to browsers as the WebSocket address. Do not use the Docker-internalhttp://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 httpsmust 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¶
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:
All migrations should complete with OK.
If this command fails with
invalid USER value, your Docker daemon has user namespace remapping enabled. Usedocker exec -u rootinstead (see Troubleshooting below).
Step 10: Verify all containers are running¶
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¶
- Open
https://meet.example.com - You are redirected to
https://auth.example.comfor login - Log in with
meet-admin/ChangeMe!(the user fromkeycloak-realm.json) - 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:
502 Bad Gateway on all routes The frontend nginx is not listening on port 8083. Check for errors:
Site keeps loading / infinite redirect loop The API is returning 301 redirects. Ensureproxy_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