# Why did Let's Encrypt renewal stop after upgrading Jitsi Docker?

> Docker releases stable-11146 and stable-11146-1 could issue certificates but could not renew them because Debian cron required root. [S1][S2][S3] PR #2305 replaced cron with a rootless acme.sh service in stable-11146-2; stable-11248 remains the latest release checked on 2026-10-05. [S2][S3][S4] Upgrade with matching Compose files, preserve the persistent certificate state, then check renewal and the certificate actually served over HTTPS. [S5][S6][S10]

Source: https://jitsi.help/troubleshooting/lets-encrypt-renewal-broken-rootless/
Updated: October 5, 2026
Publisher: Jitsi Help (https://jitsi.help/)

## Symptoms

On the affected Docker releases, the web container obtains its first certificate, then repeatedly fails to start cron. Eventually HTTPS presents an expired certificate. Issue #2301 records these lines: [S1][S2]

```text
web-1  | no crontab for s6
web-1  | seteuid: Operation not permitted
```

Read them from the project directory: [S13]

```bash
docker compose logs --tail 200 web
```

Challenge routing can also fail after the scheduler is fixed. [S14]

## Cause

The rootless web image runs as UID 1000. Debian cron needs privileges, exits on `seteuid: Operation not permitted`, and s6 starts it again. Initial issuance succeeds because startup calls acme.sh directly. [S2]

Stable-11146-2 replaces cron with the `acme-renewal` service and installs acme.sh with `--nocron`. The service waits 60 seconds, then runs `acme.sh --cron --home /storage/acme.sh` every 43,200 seconds, or 12 hours. It renews certificates when due. It disables itself if `DISABLE_HTTPS=1`, `ENABLE_LETSENCRYPT` is not 1, or the installed executable is missing. [S3][S6]

State locations in stable-11248 are: [S5][S7]

| Purpose | Container path | Host path |
|---|---|---|
| Client, account and renewal metadata | `/storage/acme.sh` | `${CONFIG}/storage/web/acme.sh` |
| Installed chain and key | `/storage/acme-certs/meet.example.com/fullchain.pem`, `/storage/acme-certs/meet.example.com/key.pem` | `${CONFIG}/storage/web/acme-certs/meet.example.com/` |

`${CONFIG}/tmp/web-crontabs` was the obsolete cron mount, removed by PR #2305. `${CONFIG}/tmp/web-load-test` remains unrelated to certificate renewal. Startup copies old `/config/acme.sh` and `/config/acme-certs` into storage only when their new destinations do not exist. [S2][S5][S7]

## Fix

Before changing either installation, run the first command in Verify and record the served certificate's serial and `notAfter`. [S10]

1. **Docker, affected releases: upgrade the release and its Compose files.** Use the stable-11248 release archive, preserve your `.env`, passwords and existing `CONFIG` directory, and apply the rootless directory migration if upgrading from an older release. Its writable storage must be accessible to UID 1000. [S2][S4][S5][S8]

   Set this in `.env`, then run the commands from the updated project directory. This recreates services, so schedule a maintenance window. Keep any additional Compose files you normally use in the invocation. [S5][S8][S13]

   ```ini
   JITSI_IMAGE_VERSION=stable-11248
   ```

   ```bash
   docker compose config --images
   docker compose pull
   docker compose up -d --force-recreate
   docker compose ps
   ```

   The core images must end with `/web:stable-11248`, `/prosody:stable-11248`, `/jicofo:stable-11248` and `/jvb:stable-11248`. Custom Compose deployments need the matching storage mount and port mappings, not just a new web image. [S5]

2. **Docker stable-11248: check renewal prerequisites.** For direct HTTPS termination in the web container, confirm these `.env` settings: [S7][S8]

   ```ini
   ENABLE_LETSENCRYPT=1
   DISABLE_HTTPS=0
   LETSENCRYPT_DOMAIN=meet.example.com
   LETSENCRYPT_EMAIL=admin@example.com
   HTTP_PORT=80
   HTTPS_PORT=443
   ```

   After changing these values, recreate web. Restarting alone does not apply environment changes. [S13]

   ```bash
   docker compose up -d --no-deps --force-recreate web
   ```

   Public TCP 80 must reach the standalone HTTP challenge. The corrected Compose mapping sends host port 80 to container port 8000, and PR #2300 adds `--httpport 8000`. TLS expiry does not prevent HTTP-01 validation. If an external proxy owns TLS, renew through its certificate manager. [S3][S5][S7][S8][S15]

3. **Docker stable-11248: run a due renewal check first.** Use `/command/with-contenv` to load the service environment, including PATH for its Nginx hooks. Check due renewals and list records: [S6][S13]

   ```bash
   docker compose exec -T web /command/with-contenv /storage/acme.sh/acme.sh --cron --home /storage/acme.sh
   docker compose exec -T web /command/with-contenv /storage/acme.sh/acme.sh --list --home /storage/acme.sh
   ```

4. **Docker stable-11248: recover an expired certificate or force one controlled renewal.** After fixing connectivity, select the record backing the served certificate. Use the ECC command below for an `ec-` key length. For an RSA certificate, omit `--ecc`. Renewal uses stored challenge settings and install paths. Its hooks stop and start Nginx, so use a maintenance window and avoid concurrent renewal attempts. [S7][S9]

   ```bash
   docker compose exec -T web /command/with-contenv /storage/acme.sh/acme.sh --renew -d meet.example.com --ecc --force --home /storage/acme.sh
   ```

   After successful renewal, restart web to ensure it loads the installed certificate, then run Verify. Preserve the ACME account and certificate directories. Repeated forced requests are not a substitute for fixing validation and can encounter CA rate limits. [S7][S9][S13][S15]

   ```bash
   docker compose restart web
   ```

5. **Debian/Ubuntu packages: identify the installed certificate client.** Docker tags and its cron regression do not apply to the package helper. At `jitsi-meet_11248`, it uses acme.sh under `/opt/acmesh/.acme.sh`, installs `/etc/jitsi/meet/meet.example.com.crt` and the matching `.key`, and saves web-server and coturn reload commands. Run its due-renewal check: [S11]

   ```bash
   sudo /opt/acmesh/.acme.sh/acme.sh --cron --home /opt/acmesh/.acme.sh
   ```

   To recover that helper's issuance and installation configuration, use: [S11]

   ```bash
   sudo /usr/share/jitsi-meet/scripts/install-letsencrypt-cert.sh admin@example.com meet.example.com
   ```

   This requests a certificate and configures reload hooks. Older installations actually managed by Certbot should instead test their saved renewal configuration, then renew due certificates: [S11][S12]

   ```bash
   sudo certbot renew --dry-run
   sudo certbot renew
   ```

## Verify

For Docker stable-11248 and package installs, inspect the endpoint from a machine with OpenSSL 3.0: [S10]

```bash
openssl s_client -connect meet.example.com:443 -servername meet.example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -dates -serial
openssl s_client -connect meet.example.com:443 -servername meet.example.com </dev/null 2>/dev/null | openssl x509 -noout -checkend 0
openssl s_client -connect meet.example.com:443 -servername meet.example.com -verify_hostname meet.example.com -verify_return_error </dev/null
```

The expiry check should print `Certificate will not expire` and exit zero. The trust and hostname check should show `Verify return code: 0 (ok)`. Compare the new serial and `notAfter` with the pre-renewal certificate. These checks prove current validity; one manual renewal cannot prove future scheduling. [S10]

## If it still fails

On Docker stable-11248, read web logs again. `acme.sh is not installed in /storage/acme.sh, skipping renewals` identifies a disabled renewal service. Check the writable storage mount and HTTPS settings. [S6][S7]

Issue #2321 reports a different failure on stable-11146-2: persisted metadata retained `/var/run/s6/services/nginx` instead of `/run/service/nginx`, plus the old validation port. The reporter rebuilt the saved ACME state and restored renewal. This is a community recovery report, not proof that PR #2305 failed. Preserve backups before rebuilding account state; the exact safe migration requires checking your stored certificate records. [S14]

For DNS, blocked TCP 80 or failed initial issuance, use [Let's Encrypt failed](/troubleshooting/jitsi-lets-encrypt-failed/). Apply the version-specific package-client distinction above. [S16]

## FAQ

### Which release fixes rootless cron renewal?

Stable-11146-2 includes PR #2305. Stable-11248 is the latest release checked on 2026-10-05 and retains the service. [S3][S4][S6]

### Should I run the web container as root?

Use the corrected rootless service. It calls acme.sh directly and no longer needs Debian cron. [S2][S6]

### Is certificate state stored in CONFIG/tmp?

No. Current state is under `${CONFIG}/storage/web`; the removed temporary mount held crontabs. [S2][S5]

### Can I renew after the certificate has expired?

Yes, HTTP-01 uses port 80. Fix validation, renew with the matching client, and check the certificate served on port 443. [S9][S10][S15]

## Sources

[S1] [Issue #2301](https://github.com/jitsi/docker-jitsi-meet/issues/2301), 2026-08-08, community report, closed 2026-08-17.

[S2] [PR #2305](https://github.com/jitsi/docker-jitsi-meet/pull/2305), 2026-08-17, maintainer explanation and source code.

[S3] [stable-11146-2](https://github.com/jitsi/docker-jitsi-meet/releases/tag/stable-11146-2), 2026-08-17, release note, includes #2300 and #2305.

[S4] [Latest release](https://github.com/jitsi/docker-jitsi-meet/releases/latest), checked 2026-10-05, release note, stable-11248 published 2026-09-14.

[S5] [Compose, stable-11248](https://github.com/jitsi/docker-jitsi-meet/blob/stable-11248/docker-compose.yml), checked 2026-10-05, source code.

[S6] [Renewal service, stable-11248](https://github.com/jitsi/docker-jitsi-meet/blob/stable-11248/web/rootfs/etc/s6-overlay/scripts/acme-renewal) and [s6 environment, v3.2.3.0](https://github.com/just-containers/s6-overlay/tree/v3.2.3.0#container-environment), checked 2026-10-05, source code and official doc.

[S7] [Web startup](https://github.com/jitsi/docker-jitsi-meet/blob/stable-11248/web/rootfs/etc/s6-overlay/scripts/config) and [TLS paths](https://github.com/jitsi/docker-jitsi-meet/blob/stable-11248/web/rootfs/defaults/ssl.conf), checked 2026-10-05, source code.

[S8] [Docker handbook](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker/), checked 2026-10-05, official doc.

[S9] [acme.sh 3.0.7](https://github.com/acmesh-official/acme.sh/blob/3.0.7/acme.sh), [Docker client pin](https://github.com/jitsi/docker-jitsi-meet/blob/stable-11248/web/Dockerfile) and [renewal instructions](https://github.com/acmesh-official/acme.sh#13-how-to-renew-certificates), checked 2026-10-05, source code and official doc.

[S10] [OpenSSL x509](https://docs.openssl.org/3.0/man1/openssl-x509/), [s_client](https://docs.openssl.org/3.0/man1/openssl-s_client/), [x509 output](https://github.com/openssl/openssl/blob/openssl-3.0/apps/x509.c) and [TLS output](https://github.com/openssl/openssl/blob/openssl-3.0/apps/s_client.c), checked 2026-10-05, official docs and source code.

[S11] [Package helper, jitsi-meet_11248](https://github.com/jitsi/jitsi-meet/blob/jitsi-meet_11248/resources/install-letsencrypt-cert.sh), checked 2026-10-05, source code.

[S12] [Certbot renewal](https://eff-certbot.readthedocs.io/en/stable/using.html#renewing-certificates), checked 2026-10-05, official doc.

[S13] [Compose commands](https://docs.docker.com/reference/cli/docker/compose/), [exec](https://docs.docker.com/reference/cli/docker/compose/exec/), [up](https://docs.docker.com/reference/cli/docker/compose/up/) and [restart](https://docs.docker.com/reference/cli/docker/compose/restart/), checked 2026-10-05, official docs.

[S14] [Issue #2321 and reporter's follow-up](https://github.com/jitsi/docker-jitsi-meet/issues/2321), checked 2026-10-05, community report.

[S15] [HTTP-01](https://letsencrypt.org/docs/challenge-types/#http-01-challenge) and [rate limits](https://letsencrypt.org/docs/rate-limits/), checked 2026-10-05, official docs.

[S16] [Let's Encrypt failed](https://jitsi.help/troubleshooting/jitsi-lets-encrypt-failed/), updated 2026-10-04, independent community guide.

## Open questions

No running Jitsi server was used to test forced renewal, reload timing or expired-site recovery. Confirm those in a maintenance window. No universal migration for the stale metadata in #2321 was verified. Older package certificate-client transitions were not traced release by release.

---

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.
