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

> 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.

Source: https://jitsi.help/guides/upgrade-jitsi-docker-rootless/
Updated: October 5, 2026
Publisher: Jitsi Help (https://jitsi.help/)

## 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:

   ```bash
   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`:

   ```bash
   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:

   ```bash
   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:

   ```bash
   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:

   ```bash
   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:

   ```bash
   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:**

   ```bash
   docker compose pull
   docker compose up -d
   docker compose ps
   ```

8. **Move optional data yourself.** Recordings, Jibri logs and transcripts are not migrated automatically:

   ```bash
   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:

   ```bash
   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.

```bash
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:

```bash
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

```bash
docker compose images
```

Every Jitsi service shows `ghcr.io/jitsi/<service>` with tag `stable-11248`.

```bash
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`.

```bash
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:

```bash
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

- [docker-jitsi-meet release stable-11146](https://github.com/jitsi/docker-jitsi-meet/releases/tag/stable-11146), 2026-08-03, release note
- [PR #2258 feat: Rootless containers](https://github.com/jitsi/docker-jitsi-meet/pull/2258), merged 2026-06-19, maintainer PR (backwards-incompatible changes section)
- [PR #2261 refactor(web): use unprivileged ports 8000 and 8443](https://github.com/jitsi/docker-jitsi-meet/pull/2261), merged 2026-06-20, maintainer PR
- [PR #2271 Migrate image publishing from DockerHub to GHCR](https://github.com/jitsi/docker-jitsi-meet/pull/2271), merged 2026-06-25, maintainer PR
- [PR #2285 feat(web,jvb): remove deprecated colibri websocket support](https://github.com/jitsi/docker-jitsi-meet/pull/2285), merged 2026-07-29, maintainer PR
- [PR #2257 base: update to Debian Trixie](https://github.com/jitsi/docker-jitsi-meet/pull/2257), merged 2026-06-15, maintainer PR
- [docker-jitsi-meet release stable-11146-2](https://github.com/jitsi/docker-jitsi-meet/releases/tag/stable-11146-2), 2026-08-17, release note
- [PR #2305 fix(web) replace cron with a rootless acme.sh renewal service](https://github.com/jitsi/docker-jitsi-meet/pull/2305), merged 2026-08-17, maintainer PR (fixes issue #2301)
- [docker-jitsi-meet release stable-11248](https://github.com/jitsi/docker-jitsi-meet/releases/tag/stable-11248), 2026-09-14, release note
- [Jitsi handbook, Self-Hosting Guide: Docker (Quick start, Updating from a release older than stable-11146, Rootless and read-only containers, Authentication, Accessing server logs)](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker), source last changed 2026-10-02, official doc
- [Issue #2302 Prosody unable to find config file after updating to stable-11146-1](https://github.com/jitsi/docker-jitsi-meet/issues/2302), 2026-08-08 to 2026-08-22, maintainer comments (saghul, emrahcom)
- [Issue #2291 custom-*.js files seems to be ignored in 11146](https://github.com/jitsi/docker-jitsi-meet/issues/2291), 2026-08-05, maintainer comments and user logs
- [Issue #2336 jvb and jicofo crashing with XMPP error in stable-11248](https://github.com/jitsi/docker-jitsi-meet/issues/2336), opened 2026-10-01, open on 2026-10-05, maintainer comments
- [Issue #2323 jitsi-videobridge2 on stable-11146-2 missing native dcsctp4j library](https://github.com/jitsi/docker-jitsi-meet/issues/2323), 2026-09-11, maintainer comments; fix PR #2325 in stable-11248
- [Issue #2289 web container doesn't seem to respect $HTTP_PORT](https://github.com/jitsi/docker-jitsi-meet/issues/2289), 2026-08-05, maintainer comment (saghul)
- [Issue #2294 container images are not available on docker-hub](https://github.com/jitsi/docker-jitsi-meet/issues/2294), 2026-08-05, maintainer comments
- [Issue #2321 acme.sh is not succeeding in renewing cert](https://github.com/jitsi/docker-jitsi-meet/issues/2321), 2026-09-08, community report (closed by reporter with workaround)
- [docker-compose.yml, env.example, jibri.yml, jigasi.yml, transcriber.yml at tags stable-11031 and stable-11248](https://github.com/jitsi/docker-jitsi-meet/compare/stable-11031...stable-11248), diffed 2026-10-05, source code
- [base/rootfs/usr/bin/check-writable at stable-11248](https://github.com/jitsi/docker-jitsi-meet/blob/stable-11248/base/rootfs/usr/bin/check-writable), source code
- [Registry tag lists for ghcr.io/jitsi and Docker Hub jitsi/*](https://github.com/jitsi/docker-jitsi-meet/pkgs/container/web), and https://hub.docker.com/r/jitsi/web/tags, queried 2026-10-05 (GHCR: no tags before 2026-06-25, `stable` digest equals `stable-11248`, `stable-11031` returns MANIFEST_UNKNOWN; Docker Hub: stable-11031 present for web, prosody, jicofo, jvb, jibri, jigasi, last push 2026-06-25), registry data
- [Jitsi handbook, Self-Hosting Guide: Debian/Ubuntu server](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart), official doc
- [jitsi-meet doc/debian/jitsi-meet/jitsi-meet.example](https://github.com/jitsi/jitsi-meet/blob/master/doc/debian/jitsi-meet/jitsi-meet.example), source code
- [PR #2304 fix(prosody) create the data folder at runtime to fix its ownership](https://github.com/jitsi/docker-jitsi-meet/pull/2304), merged 2026-08-10, maintainer PR
- [jitsi-meet releases (2.0.11248, 2.0.11146, 2.0.11031)](https://github.com/jitsi/jitsi-meet/releases), release note
- [Docker docs, docker compose images](https://docs.docker.com/reference/cli/docker/compose/images/), official doc (Docker)
- [acme.sh source, `_savedomainconf "Le_HTTPPort"`](https://github.com/acmesh-official/acme.sh/blob/master/acme.sh), source code
- [web/rootfs/etc/s6-overlay/scripts/config at stable-11248 (`--httpport 8000`, one-time TLS migration)](https://github.com/jitsi/docker-jitsi-meet/blob/stable-11248/web/rootfs/etc/s6-overlay/scripts/config), source code

## Need a hand?

If you would rather not do this on a production server, our [support plans](/services/jitsi-support/) include upgrades done and verified for you. You can also [contact our engineers](/contact/?topic=troubleshooting) 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.


---

Jitsi Help is an independent service. It is not affiliated with, endorsed by or sponsored by 8x8, Inc. or the Jitsi project. Jitsi and Jitsi Meet are trademarks of 8x8, Inc., used here only to describe the software we host and support.
