How do I upgrade docker-jitsi-meet from stable-11031 to the rootless releases?

Short answer

Since stable-11146 every docker-jitsi-meet container runs as uid/gid 1000 on a read-only filesystem, listens on 8000 and 8443 inside the container, and is published on ghcr.io/jitsi instead of Docker Hub. To upgrade, back up .env, the compose files and your CONFIG folder, create the new storage and tmp folders writable by uid 1000, download the compose files from the new tag, remove any old JITSI_IMAGE_VERSION pin, then pull and recreate. Go straight to stable-11248, because 11146 and 11146-1 break certificate renewal and 11146-2 can break every conference join.

Who this is for

You run Jitsi Meet with docker-jitsi-meet on stable-11031 or older and want to move to stable-11146 or newer. You want the same rooms, accounts, branding, certificate and recordings afterwards, plus a quick way back if something breaks. The latest release on 2026-10-05 is stable-11248 (2026-09-14).

How it works

stable-11146 was a large hardening release. A maintainer described it as one of the largest changes in the project’s lifetime. The parts that matter for an upgrade:

  • Unprivileged user. Every service runs as user s6 with uid/gid 1000 and no Linux capabilities. Any host folder a container writes to must be writable by uid 1000.
  • Read-only root filesystem. Only /run and /tmp are writable, as tmpfs mounts. /config is now input only. Each service renders its real config into /run/<service>/config at every start, so generated files such as config.js no longer appear in ${CONFIG}/web.
  • New storage and tmp folders. Persistent state moves to ${CONFIG}/storage/{jibri,prosody,transcripts,web}, and regenerable files to ${CONFIG}/tmp. Prosody data and web TLS files are copied over on first start. Old folders are left untouched.
  • Ports 8000 and 8443 inside the web container. HTTP_PORT and HTTPS_PORT still set the host ports. The container side is fixed at 8000 and 8443.
  • Debian Trixie base. Images moved from Bookworm to Trixie, and Java images from OpenJDK 17 to 21.
  • GHCR registry. Images are published only at ghcr.io/jitsi. Docker Hub jitsi/* stopped at stable-11031.
  • Colibri websockets removed. The /colibri-ws/ proxy, the JVB websocket server on port 9090 and their variables are gone. SCTP data channels are always on.

Containers check that each storage folder is writable and refuse to start with a FATAL ERROR message if it is not.

Before you start

  • Shell access to the Docker host, with Docker Compose v2 (docker compose).
  • The value of CONFIG in your .env. The handbook default is ~/.jitsi-meet-cfg. The commands below use that path, so change it if yours is different.
  • A maintenance window. All containers are recreated.
  • Free disk space for a backup of CONFIG (recordings can be large).
  • Host ports do not change: whatever HTTP_PORT and HTTPS_PORT say (defaults 8000 and 8443), plus 10000/udp for media.
  • If you use Let’s Encrypt, DNS for meet.example.com must still point to 203.0.113.10, so a new certificate can be issued if needed.

Steps

Docker (docker-jitsi-meet)

Applies when moving from stable-11031 or older to stable-11146 or newer. The commands use the stable-11248 files.

  1. Record what runs now. From your docker-jitsi-meet folder:

    Terminal
    docker compose images
    grep -E '^(CONFIG|JITSI_IMAGE_VERSION|JITSI_IMAGE_REPO|HTTP_PORT|HTTPS_PORT)=' .env
  2. Stop and back up. Old containers ran as root, so some files are root-owned. Use sudo:

    Terminal
    docker compose down
    sudo tar czf ~/jitsi-files-stable-11031.tgz .env *.yml
    sudo tar czf ~/jitsi-config-stable-11031.tgz -C ~ .jitsi-meet-cfg

    On a cloud server, a disk snapshot is better still. Add -f jibri.yml and your other overlays to every docker compose command if you use them.

  3. Download the new release files. Compose files and image tags must come from the same release:

    Terminal
    NEW=stable-11248
    BASE=https://raw.githubusercontent.com/jitsi/docker-jitsi-meet/$NEW
    curl -fsSLO $BASE/docker-compose.yml
    curl -fsSL -o env.example.new $BASE/env.example
    # only the overlays you use:
    curl -fsSLO $BASE/jibri.yml
    curl -fsSLO $BASE/jigasi.yml
    curl -fsSLO $BASE/transcriber.yml

    The handbook alternative is to download the latest release archive and unzip it over your folder. If you changed docker-compose.yml yourself, copy those changes into the new file by hand.

  4. Create the new folders and make them writable by uid 1000. These are the handbook commands for upgrades from releases older than stable-11146:

    Terminal
    mkdir -p ~/.jitsi-meet-cfg/storage/{jibri,prosody,transcripts,web}
    mkdir -p ~/.jitsi-meet-cfg/tmp/{web-crontabs,web-load-test}
    chmod 777 ~/.jitsi-meet-cfg/storage/{jibri,prosody,transcripts,web}
    chmod 777 ~/.jitsi-meet-cfg/tmp/{web-crontabs,web-load-test}

    A tighter option is ownership instead of mode 777:

    Terminal
    sudo chown -R 1000:1000 ~/.jitsi-meet-cfg/storage ~/.jitsi-meet-cfg/tmp

    When you can skip it: the handbook says the chmod step is not needed on Docker Desktop for Windows. The start check does a real write test, so folders already owned by uid 1000 also pass. Create the folders yourself either way: if Docker creates a missing bind mount source, it is owned by root and the container cannot write to it.

    tmp/web-crontabs is no longer mounted from stable-11146-2 on, because cron was replaced by an acme-renewal service. Creating it does no harm.

  5. Update .env. Compare the variable names:

    Terminal
    diff <(grep -oE '^#?[A-Z_]+=' env.example.new | tr -d '#' | sort -u) \
         <(grep -oE '^[A-Z_]+=' .env | sort -u)

    Then:

    • Remove or comment out JITSI_IMAGE_VERSION so the compose file picks the tag. A maintainer gave this advice in issue #2336. A leftover JITSI_IMAGE_VERSION=stable-11031 makes the new compose file look for ghcr.io/jitsi/web:stable-11031, which does not exist (manifest unknown).
    • Leave JITSI_IMAGE_REPO unset. It defaults to ghcr.io/jitsi.
    • Delete the removed colibri and SCTP variables (see the table below). They are ignored now.
  6. Fix your reverse proxy, if you have one. Point it at the host port (HTTP_PORT, default 8000), or at container port 8000, never container port 80. Remove any /colibri-ws/ and /colibri-relay-ws/ locations and any proxy to JVB port 9090. Keep the /xmpp-websocket proxy.

  7. Pull and start:

    Terminal
    docker compose pull
    docker compose up -d
    docker compose ps
  8. Move optional data yourself. Recordings, Jibri logs and transcripts are not migrated automatically:

    Terminal
    sudo cp -a ~/.jitsi-meet-cfg/jibri/recordings ~/.jitsi-meet-cfg/storage/jibri/
    sudo cp -a ~/.jitsi-meet-cfg/transcripts/. ~/.jitsi-meet-cfg/storage/transcripts/
    sudo chown -R 1000:1000 ~/.jitsi-meet-cfg/storage
  9. Update your admin habits. Create users with the explicit config path:

    Terminal
    docker compose exec prosody prosodyctl --config /run/prosody/config/prosody.cfg.lua register alice meet.jitsi CHANGE_ME

    Branding stays in ${CONFIG}/web/custom-config.js and custom-interface_config.js. Editing a generated config.js on the host has no effect.

Rollback (Docker)

stable-11031 images are still on Docker Hub as jitsi/<service>:stable-11031, and the old compose file pulls from there.

Terminal
docker compose down
sudo tar xzf ~/jitsi-files-stable-11031.tgz
sudo tar xzf ~/jitsi-config-stable-11031.tgz -C ~
docker compose up -d

Run it from your docker-jitsi-meet folder, so .env and the old compose files are restored there. The new storage and tmp folders stay behind and the old images ignore them. Old Prosody data in prosody/config/data was only copied, not moved, so accounts made before the upgrade are still there. Accounts created after the upgrade are lost.

Debian/Ubuntu packages

The rootless change, port change, Trixie base and GHCR move apply only to Docker images. Package installs follow the jitsi-meet releases (2.0.11248 is the counterpart of stable-11248). Supported systems are Debian 12 or newer and Ubuntu 24.04 or newer. Snapshot the server, then:

Terminal
sudo apt update
sudo apt upgrade

In the package nginx example, the colibri websocket proxy is commented out by default. Remove it only if you turned it on yourself.

Configuration reference

Name Where Default What it does
CONFIG .env ~/.jitsi-meet-cfg (handbook) Host folder for config, storage and tmp
JITSI_IMAGE_REPO .env (new) ghcr.io/jitsi Registry and namespace for images
JITSI_IMAGE_VERSION .env tag set in compose file Image tag. Leave unset after upgrading
HTTP_PORT / HTTPS_PORT .env 8000 / 8443 Host ports only. The container uses 8000/8443
JIBRI_RECORDING_DIR .env now /storage/recordings Recordings now under ${CONFIG}/storage/jibri
ENABLE_COLIBRI_WEBSOCKET, ENABLE_COLIBRI_WEBSOCKET_UNSAFE_REGEX, COLIBRI_WEBSOCKET_PORT, COLIBRI_WEBSOCKET_REGEX, COLIBRI_WEBSOCKET_JVB_LOOKUP_NAME, DISABLE_COLIBRI_WEBSOCKET_JVB_LOOKUP, ENABLE_OCTO .env removed in 11146 No longer honored
ENABLE_SCTP, ENABLE_OCTO_SCTP .env removed in 11146 SCTP is always on
JVB_WS_DOMAIN, JVB_WS_SERVER_ID, JVB_WS_TLS .env removed in 11146 JVB websocket server removed
ENABLE_TRACING, TRACING_ENDPOINT, TRACING_PROTOCOL, TRACING_HTTP_ENDPOINT .env (new, 11248) unset OpenTelemetry tracing for Jicofo, JVB, Prosody
ENABLE_ICE_RESTART, ENABLE_ICE_RESTART_ON_NETWORK_CHANGE .env (new, 11248) unset ICE restart behaviour
ENABLE_AUDIO_TRANSLATION, AUDIO_TRANSLATION_DUCKED_VOLUME, JICOFO_TRANSLATION_URL_TEMPLATE, JICOFO_TRANSLATION_CF_ACCESS_CLIENT_ID, JICOFO_TRANSLATION_CF_ACCESS_CLIENT_SECRET .env (new) unset Live audio translation
ENABLE_ADVANCED_AUDIO_SETTINGS, ENABLE_THIRD_PARTY_REQUESTS, DISABLE_AV1_DECODE_FOR_FF, new VIDEOQUALITY_* keys .env (new, 11248) unset Web client options
JIBRI_CHROMEDRIVER_DEBUG, JIBRI_CHROMEDRIVER_DEBUG_LOG .env (new) unset Jibri driver logging
read_only, tmpfs (/run, /tmp) docker-compose.yml /run 16M web and prosody, 32M jicofo, jvb, jigasi, 256M jibri (11248) Read-only root, writable tmpfs

No variable was renamed between stable-11031 and stable-11248. The changes are additions and removals.

Common mistakes

  • FATAL ERROR: directory '/storage' is not writable by the container user (uid 1000). (or '/var/lib/prosody'). The folder was created by Docker as root, or never made writable. Run the chmod or chown from step 4.
  • manifest unknown on pull. .env still pins an old tag that GHCR never had. Remove JITSI_IMAGE_VERSION.
  • Your reverse proxy can no longer reach Jitsi. It targets container port 80. Use 8000. Setting HTTP_PORT=80 does not change the container side.
  • Prosody was unable to find the configuration file: /etc/prosody//config/prosody.cfg.lua. Add --config /run/prosody/config/prosody.cfg.lua to prosodyctl.
  • Branding looks ignored. Check the served file with curl https://meet.example.com/interface_config.js. In issue #2291 the reporter fixed it by rebuilding the custom file from the current template.
  • Certificates never renew on 11146 or 11146-1. The web log repeats seteuid: Operation not permitted. Upgrade to 11146-2 or newer.
  • Renewal fails with a 404 on /.well-known/acme-challenge/. acme.sh state copied from the old image still uses the old port and hooks. A community workaround: stop web, move storage/web/acme.sh, storage/web/acme-certs, web/acme.sh and web/acme-certs aside, start web so it issues a fresh certificate. Not fixed upstream as of 2026-10-05.
  • Nobody can join on 11146-2: There are no operational bridges. JVB logs UnsatisfiedLinkError for dcsctp4j, because /run was too small. Fixed in stable-11248 (PR #2325). Keep the 32M /run line if you maintain your own compose file.
  • LDAP: could not open pid lock file: /var/run/saslauthd/saslauthd.pid.lock. Fixed in stable-11248 (PR #2312).

Verify

Terminal
docker compose images

Every Jitsi service shows ghcr.io/jitsi/<service> with tag stable-11248.

Terminal
docker compose exec web id -u
docker compose logs web | grep 'read-only root'

The first prints 1000. The second contains info: read-only root.

Terminal
docker compose logs | grep -E 'FATAL ERROR|no operational bridges|No stream features'

No output means none of the known failure lines appeared.

If you use Let’s Encrypt:

Terminal
docker compose exec web /storage/acme.sh/acme.sh --cron --home /storage/acme.sh
sudo grep Le_HTTPPort ~/.jitsi-meet-cfg/storage/web/acme.sh/meet.example.com*/meet.example.com.conf

A healthy cron run ends with Skip, Next renewal time is: for your domain, or with a successful renewal. Le_HTTPPort='8000' matches the port the new image uses.

Then do the post-upgrade checklist:

  1. Three people in one room with cameras on. Two people use peer-to-peer, so only three or more test the videobridge.
  2. Moderator login, if you use authentication or JWT.
  3. A short recording, and check it appears in ${CONFIG}/storage/jibri/recordings.
  4. Certificate renewal, with the cron command above.
  5. Branding is still shown after a hard refresh.

If it still fails

  • docker compose logs -t -f prosody, then jicofo and jvb. Look for FATAL ERROR lines, which name the folder at fault.
  • jvb and jicofo log No stream features to proceed with: this is issue #2336, open as of 2026-10-05. Maintainers asked for three things: the compose file from the same release, the new folders, and no JITSI_IMAGE_VERSION=stable pin.
  • Check docker compose exec web ls /run/web/config to see the rendered config, and confirm your custom files were appended.
  • If you run Podman or Kubernetes, check the volume ownership rules in PR #2304.

What we have not confirmed yet

We checked everything above against the handbook, release notes, pull requests, maintainer comments and the release source on 2026-10-05. We have not yet run this upgrade end to end on a test server. These points are still open:

  • The handbook still lists tmp/web-crontabs, but stable-11146-2 and later no longer mount it. Harmless, but unconfirmed whether the handbook will drop it.
  • Docker Desktop for macOS: the handbook only says the chmod step is not needed on Docker Desktop for Windows. Not confirmed for macOS.
  • Whether the container uid can be changed from 1000 was asked in issue #2291 and not answered.
  • ENABLE_VIRTUAL_BACKGROUND_V2 and ENABLE_MESSAGE_MODERATION appear in the 11146 and 11248 notes and are read by the templates (both default true), but neither is listed in the environment: section of the stable-11248 compose files. A value set only in .env may not reach the container. Needs a test.
  • The exact docker compose pull error text for a missing GHCR tag was not captured. The registry itself returns manifest unknown.
  • Issue #2336 (No stream features to proceed with) has no confirmed cause yet.
  • The package quickstart still says “OpenJDK 17 must be used”, while Debian 13 Trixie dropped openjdk-17 and the Docker images moved to OpenJDK 21. Package installs on Debian 13 need checking.
  • Whether the old-acme-state renewal problem in #2321 affects every Let’s Encrypt upgrade, or only some, is not known.

Sources

Need a hand?

If you would rather not do this on a production server, our support plans include upgrades done and verified for you. You can also contact our engineers with the output of docker compose logs --tail 200 prosody jicofo jvb.

Frequently asked questions

Can I go straight from stable-11031 to stable-11248?

Yes. Migrations run on the first start of any release from 11146 on, and the handbook steps are the same. Skipping 11146 to 11146-2 also avoids their renewal and bridge bugs.

Do I have to use chmod 777?

No. The folders only need to be writable by uid 1000, so `chown -R 1000:1000` works too.

Why are there no new images on Docker Hub?

The project moved to GHCR because of Docker Hub rate limits. stable-11031 is the last Docker Hub release.

Is there a `stable` tag I can follow?

GHCR has a `stable` tag. For the web image it pointed to stable-11248 on 2026-10-05. Maintainers advise against pinning it, because you cannot tell which release you are running.

Does this affect apt installs?

No. Rootless containers, the port change and GHCR apply only to the Docker images.

Stuck, or would rather not do this by hand?

Deploy it in one click

A private Jitsi server in your own AWS account with SSL, your domain and optional recording, transcription and JWT. Free 15 minute trial.

Start free trial

Talk to a Jitsi engineer

Setup, fixes, branding, recording, scaling. Tell us what is happening and we reply with a plan and a quote.

Get expert help

Related

Recently updated