HOWTO
VIDEOCHAT
INSTALL JITSI SERVER WITH DOCKER COMPOSE

Published: 20221023

Tested on:

-

  1. Preparations

    These are just some quick notes on how I got it working

    Required:

    In this example I will use these values:

    A (or CNAME) record jitsi5.example.com pointing to public IPv4 address: 192.0.2.5

    OK, let's get on with the actual installation!

  2. Install the docker environment and docker-compose

    ~$ sudo apt -y install docker-compose
    
  3. Download jitsi

    On the jitsi account at github there's a repo for installing jitsi with docker-compose. Under the releases section you can download the latest "Source code tarball". Under the latest release, under Assets, download the file called "Source code (tar.gz)"

    ~$ wget https://github.com/jitsi/docker-jitsi-meet/archive/refs/tags/stable-7830.tar.gz
    

    unpack it:

    ~$ tar zxvf stable-7830.tar.gz
    ~$ cd docker-jitsi-meet-stable-7830/
    
  4. Configuring the server

    As mentioned in the repo page: "The installation manual is available here"

    I followed the manual and it went like this:

    ~/docker-jitsi-meet-stable-7830$ cp env.example .env
    ~/docker-jitsi-meet-stable-7830$ ./gen-passwords.sh
    ~/docker-jitsi-meet-stable-7830$ sudo mkdir -p /root/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}
    ~/docker-jitsi-meet-stable-7830$ vi .env
    

    Inside this .env file I made sure these values were set:

    CONFIG=/root/.jitsi-meet-cfg
    HTTP_PORT=80
    HTTPS_PORT=443
    JVB_PORT=10000
    PUBLIC_URL=https://jitsi5.example.com
    JVB_ADVERTISE_IPS=192.0.2.5
    ENABLE_LETSENCRYPT=1
    LETSENCRYPT_DOMAIN=jitsi5.example.com
    LETSENCRYPT_EMAIL=webmaster@example.com
    LETSENCRYPT_USE_STAGING=1
    ENABLE_HTTP_REDIRECT=1
    ENABLE_HSTS=1
    
  5. Configuring the server: JVB_ADVERTISE_IPS

    This setting was previously known as: DOCKER_HOST_ADDRESS

    The value of JVB_ADVERTISE_IPS depends on how your network is setup. There are basically four network scenarios when you setup jitsi on IPv4.

    Scenario A - Public IPv4 on the VM

    This scenario is typical if you install jitsi on a VM in a cloud. Make sure the firewall ports are open.

    jitsi_ipv4_scenario_a You can click on this SVG picture if you want to view it closer

    In this case you don't really need to set JVB_ADVERTISE_IPS:

    #JVB_ADVERTISE_IPS=
    

    If you want to have it set anyway, you can set it to the public IPv4 address of your VM:

    JVB_ADVERTISE_IPS=192.0.2.5
    

    Scenario B - Public IPv4 network and public IPv4 address on the VM

    If you do have an IPv4 subnet (of public addresses), you might want other machines on your network be able to reach your jitsi server.

    jitsi_ipv4_scenario_b You can click on this SVG picture if you want to view it closer

    Settings:

    JVB_ADVERTISE_IPS=192.0.2.5
    

    Scenario C - Public IPv4 on the router, jitsi server on a private RFC 1918 network

    Therefore my IPv4 router needs to have these port forwards set.
    I can verify that running port 80 and port 443 (by using the proxy protocol, aka SNI routing) through HAProxy works.

    192.0.2.5 TCP port    80 --port-forward--> 10.0.0.2 TCP    80
    192.0.2.5 TCP port   443 --port-forward--> 10.0.0.2 TCP   443
    192.0.2.5 UDP port 10000 --port-forward--> 10.0.0.2 UDP 10000
    

    jitsi_ipv4_scenario_c You can click on this SVG picture if you want to view it closer

    Settings:

    JVB_ADVERTISE_IPS=10.0.0.2
    

    Scenario D - Public IPv4 on the router, jitsi server on a private RFC 1918 network with a Split-horizon DNS

    jitsi_ipv4_scenario_d You can click on this SVG picture if you want to view it closer

    I don't really know what to say.

    These kind of setups are really the deep end of IPv4 NAT.

    On top of that, the official documentation isn't very clear.

    Nor are the discussions around this, that you can read about here or here.

    I think the idea is JVB_ADVERTISE_IPS (which now can be several IP-addresses) means that you can and should advertise both addresses:

    JVB_ADVERTISE_IPS=10.0.0.2,192.0.2.5
    

    My recommendation is that while NAT is stupid enough, try to stay away from NAT with Split-horizon DNS, because you will have a lot of problems.

    Anyway... Let's continue.

  6. Configuring the server: LETSENCRYPT_USE_STAGING=1

    Let's launch the service with the settings we already have:

    ~/docker-jitsi-meet-stable-7830$ sudo docker-compose up -d
    Creating network "docker-jitsi-meet-stable-7830_meet.jitsi" with the default driver
    Pulling web (jitsi/web:stable-7830)...
    stable-7830: Pulling from jitsi/web
    31b3f1ad4ce1: Pull complete
    6020084f933f: Pull complete
    ee1eaad5280a: Pull complete
    4f4fb700ef54: Pull complete
    3819e3283aee: Pull complete
    820d6d377523: Pull complete
    c3b22928936c: Pull complete
    Digest: sha256:5209d42b08c1330472a6f1af5674a44901228022a9bb95c472043db76fdc3567
    Status: Downloaded newer image for jitsi/web:stable-7830
    Pulling prosody (jitsi/prosody:stable-7830)...
    stable-7830: Pulling from jitsi/prosody
    31b3f1ad4ce1: Already exists
    6020084f933f: Already exists
    ee1eaad5280a: Already exists
    4f4fb700ef54: Already exists
    7a2424ecf957: Pull complete
    0c37992c8287: Pull complete
    8ec5fdfe8d5f: Pull complete
    a802456bd2fb: Pull complete
    Digest: sha256:5e3050d5c5e47cbb542f03cd9b8f4fd33a9b9888ffd5cec4219497a6115f2552
    Status: Downloaded newer image for jitsi/prosody:stable-7830
    Pulling jicofo (jitsi/jicofo:stable-7830)...
    stable-7830: Pulling from jitsi/jicofo
    31b3f1ad4ce1: Already exists
    6020084f933f: Already exists
    ee1eaad5280a: Already exists
    4f4fb700ef54: Already exists
    bc9d3bc2c78e: Pull complete
    1a207e87fa86: Pull complete
    3f43ac9e7c10: Pull complete
    Digest: sha256:f108b9b23d40743e02193d880d6cb6568d09405aeac7dbac3f61f5046aa13c93
    Status: Downloaded newer image for jitsi/jicofo:stable-7830
    Pulling jvb (jitsi/jvb:stable-7830)...
    stable-7830: Pulling from jitsi/jvb
    31b3f1ad4ce1: Already exists
    6020084f933f: Already exists
    ee1eaad5280a: Already exists
    4f4fb700ef54: Already exists
    bc9d3bc2c78e: Already exists
    8e8f21dd7dc4: Pull complete
    2b9d1507078a: Pull complete
    Digest: sha256:d30f7675eb8b7b834a48c86262816aac3c18c1c202ac26ebeb8705774feaa74d
    Status: Downloaded newer image for jitsi/jvb:stable-7830
    Creating docker-jitsi-meet-stable-7830_prosody_1 ... done
    Creating docker-jitsi-meet-stable-7830_web_1     ... done
    Creating docker-jitsi-meet-stable-7830_jvb_1     ... done
    Creating docker-jitsi-meet-stable-7830_jicofo_1  ... done
    ~/docker-jitsi-meet-stable-7830$
    

    Check that the containers are running:

    $ sudo docker ps
    CONTAINER ID   IMAGE                       COMMAND   CREATED              STATUS              PORTS                                                                      NAMES
    08c2368c2a22   jitsi/jvb:stable-7830       "/init"   About a minute ago   Up About a minute   127.0.0.1:8080->8080/tcp, 0.0.0.0:10000->10000/udp, :::10000->10000/udp    docker-jitsi-meet-stable-7830_jvb_1
    b57cea77b093   jitsi/jicofo:stable-7830    "/init"   About a minute ago   Up About a minute                                                                              docker-jitsi-meet-stable-7830_jicofo_1
    c724c67b3bf8   jitsi/web:stable-7830       "/init"   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   docker-jitsi-meet-stable-7830_web_1
    fd83ef2522b2   jitsi/prosody:stable-7830   "/init"   About a minute ago   Up About a minute   5222/tcp, 5280/tcp, 5347/tcp                                               docker-jitsi-meet-stable-7830_prosody_1
    $
    

    Now the service should be up and it should have a "red certificate" (meaning you have an invalid certificate).

    If you look at the certificate (via a web browser) it has these values:

    Issued By:
    CN = (STAGING) Artificial Apricot R3
    O = (STAGING) Let's Encrypt

    You can also verify these values in the terminal:

    $ sudo openssl x509 -in /root/.jitsi-meet-cfg/web/acme.sh/jitsi5.example.com/jitsi5.example.com.cer -text -noout
    

    This is because we launched the service with:

    LETSENCRYPT_USE_STAGING=1
    

    When you're using Let's Encrypt, it's always good practise to start with the Staging Environment.

    There are many things that could go wrong (you could even be unlucky and run a bad release of Jitsi) and you don't want to get rate limited for trying too many times, so you'd have to wait until you could deploy again.

    However, now when things are running smoothly, you can just disable the staging environment and redeploy.

    First, undeploy the containers:

    ~/docker-jitsi-meet-stable-7830$ sudo docker-compose down
    Stopping docker-jitsi-meet-stable-7830_jvb_1     ... done
    Stopping docker-jitsi-meet-stable-7830_jicofo_1  ... done
    Stopping docker-jitsi-meet-stable-7830_web_1     ... done
    Stopping docker-jitsi-meet-stable-7830_prosody_1 ... done
    Removing docker-jitsi-meet-stable-7830_jvb_1     ... done
    Removing docker-jitsi-meet-stable-7830_jicofo_1  ... done
    Removing docker-jitsi-meet-stable-7830_web_1     ... done
    Removing docker-jitsi-meet-stable-7830_prosody_1 ... done
    Removing network docker-jitsi-meet-stable-7830_meet.jitsi
    ~/docker-jitsi-meet-stable-7830$
    

    Secondly, edit ~/docker-jitsi-meet-stable-7830/.env and make sure this row is commented:

    #LETSENCRYPT_USE_STAGING=1
    

    Thirdly, delete the old (staging environment) certificate files or the next time you deploy the containers, they will prevent the containers to ask for new ones (since they're not outdated yet).

    $ sudo rm -rf /root/.jitsi-meet-cfg
    

    Lastly, don't forget to recreate the required directories:

    $ sudo mkdir -p /root/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}
    

    Now, redeploy:

    ~/docker-jitsi-meet-stable-7830$ sudo docker-compose up -d
    Creating network "docker-jitsi-meet-stable-7830_meet.jitsi" with the default driver
    Creating docker-jitsi-meet-stable-7830_web_1     ... done
    Creating docker-jitsi-meet-stable-7830_prosody_1 ... done
    Creating docker-jitsi-meet-stable-7830_jicofo_1  ... done
    Creating docker-jitsi-meet-stable-7830_jvb_1     ... done
    ~/docker-jitsi-meet-stable-7830$
    

    And if all goes well, that's it. Jitsi is installed!

  7. If you run into troubles...

    There are many things that can go wrong, but the two most commons problems I run into are either: rate limiting or jitsi is starting to get buggy.

    Rate limiting - jitsi wont start

    I'm often tempted to set:

    RESTART_POLICY=unless-stopped
    

    This setting isn't wrong but it can cause a lot of problems.

    The idea is that if the containers are having some kind of trouble starting, they will just restart themselves, about once a minute.

    If the "web container" is the one restarting and is having troubles with the certificate signing request, it can within minutes hit the Let's Encrypt rate limits and then there's nothing you can do but shutdown the container and wait.

    So the good idea is to disable this setting:

    #RESTART_POLICY=unless-stopped
    

    at least until things are stable and everything is setup against the staging environment. Even when I've done this a couple of times and feel that I know how jitsi works and could skip the staging evinronment, that is usually when I miss something.

    It happened when I wrote this article. But it wasn't obvious at first.

    The web container was restarting, so I started tracking the docker logs:

    Find out the docker container id:

    $ sudo docker ps
    CONTAINER ID   IMAGE                       COMMAND   CREATED          STATUS          PORTS                                                                      NAMES
    ac52a8511e05   jitsi/web:stable-7882       "/init"   22 seconds ago   Up 21 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   docker-jitsi-meet-stable-7882_web_1
    8f8de7c103ff   jitsi/jicofo:stable-7882    "/init"   15 minutes ago   Up 15 minutes                                                                              docker-jitsi-meet-stable-7882_jicofo_1
    d75ee6f262f1   jitsi/jvb:stable-7882       "/init"   15 minutes ago   Up 15 minutes   127.0.0.1:8080->8080/tcp, 0.0.0.0:10000->10000/udp, :::10000->10000/udp    docker-jitsi-meet-stable-7882_jvb_1
    8cd8f2d2cfba   jitsi/prosody:stable-7882   "/init"   15 minutes ago   Up 15 minutes   5222/tcp, 5280/tcp, 5347/tcp                                               docker-jitsi-meet-stable-7882_prosody_1
    $
    

    Read the logs of the container that has troubles: ac52a8511e05

    $ sudo docker logs ac52a8511e05 -f
    [s6-init] making user provided files available at /var/run/s6/etc...exited 0.
    [s6-init] ensuring user provided files have correct perms...exited 0.
    [fix-attrs.d] applying ownership & permissions fixes...
    [fix-attrs.d] done.
    [cont-init.d] executing container initialization scripts...
    [cont-init.d] 01-set-timezone: executing...
    [cont-init.d] 01-set-timezone: exited 0.
    [cont-init.d] 10-config: executing...
    /opt /
    [Sat Oct 22 22:22:47 UTC 2022] Installing to /config/acme.sh
    [Sat Oct 22 22:22:47 UTC 2022] Installed to /config/acme.sh/acme.sh
    [Sat Oct 22 22:22:47 UTC 2022] Installing alias to '/root/.profile'
    [Sat Oct 22 22:22:47 UTC 2022] OK, Close and reopen your terminal to start using acme.sh
    [Sat Oct 22 22:22:47 UTC 2022] Installing cron job
    no crontab for root
    no crontab for root
    [Sat Oct 22 22:22:47 UTC 2022] Good, bash is found, so change the shebang to use bash as preferred.
    [Sat Oct 22 22:22:47 UTC 2022] OK
    /
    [Sat Oct 22 22:22:49 UTC 2022] Using CA: https://acme-v02.api.letsencrypt.org/directory
    [Sat Oct 22 22:22:49 UTC 2022] Run pre hook:'if [[ -d /var/run/s6/services/nginx ]]; then s6-svc -d /var/run/s6/services/nginx; fi'
    [Sat Oct 22 22:22:50 UTC 2022] Standalone mode.
    [Sat Oct 22 22:22:50 UTC 2022] Create account key ok.
    [Sat Oct 22 22:22:50 UTC 2022] Registering account: https://acme-v02.api.letsencrypt.org/directory
    [Sat Oct 22 22:22:52 UTC 2022] Registered
    [Sat Oct 22 22:22:52 UTC 2022] ACCOUNT_THUMBPRINT='PqWbqYUoy4QRiQZM5lI1tp7ToZmvcFORhR3VZZAEuPQ'
    [Sat Oct 22 22:22:52 UTC 2022] Creating domain key
    [Sat Oct 22 22:22:53 UTC 2022] The domain key is here: /config/acme.sh/jitsi5.example.com/jitsi5.example.com.key
    [Sat Oct 22 22:22:53 UTC 2022] Single domain='jitsi5.example.com'
    [Sat Oct 22 22:22:53 UTC 2022] Getting domain auth token for each domain
    [Sat Oct 22 22:22:56 UTC 2022] Getting webroot for domain='jitsi5.example.com'
    [Sat Oct 22 22:22:56 UTC 2022] Verifying: jitsi5.example.com
    [Sat Oct 22 22:22:56 UTC 2022] Standalone mode server
    [Sat Oct 22 22:23:01 UTC 2022] jitsi5.example.com:Verify error:During secondary validation: 192.0.2.5: Invalid response from http://jitsi5.example.com/.well-known/acme-challenge/KXixVGSQ04LgCTNa9CqB9t8gNdXAq456kbI76ezG7ug: 503
    [Sat Oct 22 22:23:01 UTC 2022] Please add '--debug' or '--log' to check more details.
    [Sat Oct 22 22:23:01 UTC 2022] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
    [Sat Oct 22 22:23:01 UTC 2022] Run post hook:'if [[ -d /var/run/s6/services/nginx ]]; then s6-svc -u /var/run/s6/services/nginx; fi'
    Failed to obtain a certificate from the Let's Encrypt CA.
    Exiting.
    [cont-init.d] 10-config: exited 1.
    [cont-finish.d] executing container finish scripts...
    [cont-finish.d] done.
    [s6-finish] waiting for services.
    [s6-finish] sending all processes the TERM signal.
    [s6-finish] sending all processes the KILL signal and exiting.
    $
    

    So it looks like there's some kind of trouble with the nginx web server, but in reality it worked much better after a couple of hours.

    I'm posting this as an example how often rate limiting is the real issue.

    When the jitsi experience starts to get buggy

    If you don't update your jitsi server regularly you will eventually find that it works less and less well. This is because all the web browsers have auto update, meaning that their way of parsing web code changes over the time.

    When you notice video working (or not working) in weird ways...
    When you notice audio is not working properly...
    When you can join meetings but there are glitches...
    When it works for some but not for others...
    When the app works but the browser doesn't work...

    Then ask yourself: When was the last time you updated the server?

    So, if you use jitsi for a longer period of time, upgrading the jitsi server will become a regular task.

  8. Updating the jitsi server

    Upgrading is actually quite simple.

    First, download the new release:

    ~$ wget https://github.com/jitsi/docker-jitsi-meet/archive/refs/tags/stable-7882.tar.gz
    

    Secondly, take down the current containers:

    ~$ cd docker-jitsi-meet-stable-7830
    ~/docker-jitsi-meet-stable-7830$ sudo docker-compose down
    Stopping docker-jitsi-meet-stable-7830_jvb_1     ... done
    Stopping docker-jitsi-meet-stable-7830_jicofo_1  ... done
    Stopping docker-jitsi-meet-stable-7830_prosody_1 ... done
    Stopping docker-jitsi-meet-stable-7830_web_1     ... done
    Removing docker-jitsi-meet-stable-7830_jvb_1     ... done
    Removing docker-jitsi-meet-stable-7830_jicofo_1  ... done
    Removing docker-jitsi-meet-stable-7830_prosody_1 ... done
    Removing docker-jitsi-meet-stable-7830_web_1     ... done
    Removing network docker-jitsi-meet-stable-7830_meet.jitsi
    ~/docker-jitsi-meet-stable-7830$
    

    Copy the configuration to the new release:

    ~/docker-jitsi-meet-stable-7830$ cd
    ~$ tar zxvf stable-7882.tar.gz
    ~$ cp docker-jitsi-meet-stable-7830/.env docker-jitsi-meet-stable-7882/
    ~$ cd docker-jitsi-meet-stable-7882
    

    Deploy the new release:

    ~/docker-jitsi-meet-stable-7882$ sudo docker-compose up -d
    Creating network "docker-jitsi-meet-stable-7882_meet.jitsi" with the default driver
    Pulling web (jitsi/web:stable-7882)...
    stable-7882: Pulling from jitsi/web
    bd159e379b3b: Pull complete
    41b5f31c8065: Pull complete
    8582d050995d: Pull complete
    4f4fb700ef54: Pull complete
    1130e8313dbf: Pull complete
    63dd5eda0e26: Pull complete
    bd645286c2d9: Pull complete
    Digest: sha256:7f755b130d95d48f8ea1568ba9848e9c9574ae89b73170b869b73697b5f70b48
    Status: Downloaded newer image for jitsi/web:stable-7882
    Pulling prosody (jitsi/prosody:stable-7882)...
    stable-7882: Pulling from jitsi/prosody
    bd159e379b3b: Already exists
    41b5f31c8065: Already exists
    8582d050995d: Already exists
    4f4fb700ef54: Already exists
    1a800766f078: Pull complete
    a8dba1d9ad1e: Pull complete
    156ba1c18fd5: Pull complete
    b4c7f5a50d7d: Pull complete
    Digest: sha256:df473674f47c45b97f1e3063a613e9a8ca1e143b5456f7268594aadae6066689
    Status: Downloaded newer image for jitsi/prosody:stable-7882
    Pulling jicofo (jitsi/jicofo:stable-7882)...
    stable-7882: Pulling from jitsi/jicofo
    bd159e379b3b: Already exists
    41b5f31c8065: Already exists
    8582d050995d: Already exists
    4f4fb700ef54: Already exists
    909201c50280: Pull complete
    28d3f309146c: Pull complete
    9129d06cfa66: Pull complete
    Digest: sha256:b9324e367c601b58dfe90dfdeba77fbd065ae36a583d351aae8d1668fa650e0a
    Status: Downloaded newer image for jitsi/jicofo:stable-7882
    Pulling jvb (jitsi/jvb:stable-7882)...
    stable-7882: Pulling from jitsi/jvb
    bd159e379b3b: Already exists
    41b5f31c8065: Already exists
    8582d050995d: Already exists
    4f4fb700ef54: Already exists
    909201c50280: Already exists
    1d42c90453ff: Pull complete
    890370b0ccb8: Pull complete
    Digest: sha256:97582ef359bd82b40c8d4d9eb71f5eed8ed7926a35fd19c3420213531a260ecb
    Status: Downloaded newer image for jitsi/jvb:stable-7882
    Creating docker-jitsi-meet-stable-7882_prosody_1 ... done
    Creating docker-jitsi-meet-stable-7882_web_1     ... done
    Creating docker-jitsi-meet-stable-7882_jicofo_1  ... done
    Creating docker-jitsi-meet-stable-7882_jvb_1     ... done
    ~/docker-jitsi-meet-stable-7830$
    

    That's it!

    However...

    If you find yourself with a non-working jitsi service, it could very well be that the latest release has bugs. It happens every now and then.

    You can usually find information about this on the github issues page.

    The solution then is usually to just run the second latest release.

  9. Jitsi, IPv6 problems and the woes of IPv4 NAT

    Yeah, I need to tell you something about JVB_ADVERTISE_IPS and IPv4 NAT...

    I do understand that writing a HOWTO in 2022 which not only implies an IPv4-only install but also includes "port forwarding" is quite disturbing. Let me explain myself. Now and then the jitsi and/or ubuntu distribution of the docker environment has had problems with IPv6, breaking jitsi for dual stack users. It was a while ago I had these problems, so the dual stack and/or IPv6-only setups might be more stable now, I don't know, I haven't tried in a while. The reason for sticking to IPv4 is not because IPv4 is a good idea, but because the jitsi developers have historically kept the IPv4 support much more stable than the IPv6 support. If you set this up dual stack just be prepared that a regular "apt update/upgrade" or an update of the docker-compose can break this and the only solution is to remove the AAAA record and wait for the settings to take globally. I'm not making this up, it happened to me. So by writing a tutorial aimed for IPv4, I at least know it'll work for us who still have access to the IPv4 Internet...

    More IPv6 Penguin Comics here