Who this is for
Use this after captions work and you need a retained file or controlled delivery. Follow the existing Vosk setup or bridge guide for recognition. Client caption options and server file saving have separate controls. [S3][S9][S11][S12]
How it works
Jigasi’s SAVE_TXT and SAVE_JSON enable complete file publishers. SEND_JSON sends live results for captions; SEND_TXT sends recognized speech into meeting chat. Neither emails a completed file. BASE_URL constructs advertised links; it does not create a web server. [S3]
Saved names follow DIRECTORY/<UTC-Instant>_<UUID>/transcript_<UTC-Instant>_<UUID>.txt, or .json. Directory and filename identifiers differ. Each format has its own publisher and generated directory, so both formats need not share one folder. Files appear when the transcription session ends. [S3]
TXT contains meeting details, participant names, UTC speech timestamps and meeting events. JSON retains structured events, alternatives and metadata, which can include email or identity fields. Both use UTF-8 and retain final speech results, not interim revisions. [S3]
| Installation | Host folder | Inside transcriber |
|---|---|---|
| Stock Docker stable-11031, before rootless | ${CONFIG}/transcripts |
/tmp/transcripts |
| Stock Docker stable-11146 through stable-11248 | ${CONFIG}/storage/transcripts |
/tmp/transcripts |
| Released Debian/Ubuntu Jigasi | /var/lib/jigasi/transcripts |
Same host path |
The Docker move shipped with rootless PR #2258 in stable-11146. Web mounts the current folder read-only at /usr/share/jitsi-meet/transcripts. Existing custom DIRECTORY=/config/transcripts configurations use their own path; check their mounts instead of assuming this table applies. [S1][S2][S3]
Before you start
Checked October 6, 2026: Docker’s latest release remains stable-11248. Package steps cover Jigasi 1.1-415-g2750d54-1. Keep Compose files and images from one release, preserve recognition/XMPP settings, and schedule recreation without active meetings. [S1][S6][S10]
Obtain participant agreement for retention and delivery. Choose who can retrieve files and when copies expire. For S3, prepare a private bucket and credentials restricted to the archive prefix. No real credentials belong in this guide or your repository. [S3][S8]
Steps
-
Docker stable-11248: protect downloads first. Set
CONFIGto your actual.envvalue. These commands use its handbook default. Keep existing backend overlays in the helper. [S1][S4][S10]TerminalCONFIG="$HOME/.jitsi-meet-cfg" mkdir -p "${CONFIG}/storage/transcripts" "${CONFIG}/web/nginx-custom" sudo chown 1000:1000 "${CONFIG}/storage/transcripts" sudo chmod 750 "${CONFIG}/storage/transcripts" dc() { docker compose -f docker-compose.yml -f transcriber.yml "$@" }Save this as
${CONFIG}/web/nginx-custom/transcripts-private.conf, a filename chosen here. Startup copies it into the included runtime directory. These rules override the stock static-file regex: [S4][S10]Nginxlocation = /transcripts { return 403; } location ^~ /transcripts/ { return 403; }Run
dc up -d --force-recreate web, thendc exec -T web nginx -t -c /run/web/config/nginx/nginx.conf. This blocks participant download links too. Deliver through your authenticated application with meeting-specific authorization, or an administrator, rather than removing protection. [S4][S10] -
Docker stable-11248: enable files. In
.env, setJIGASI_TRANSCRIBER_ENABLE_SAVING=1andJIGASI_TRANSCRIBER_ADVERTISE_URL=false. Append these assignments to${CONFIG}/transcriber/custom-sip-communicator.properties, preserving existing entries and avoiding conflicting duplicates: [S1][S5]Propertiesorg.jitsi.jigasi.transcription.DIRECTORY=/tmp/transcripts org.jitsi.jigasi.transcription.SAVE_TXT=true org.jitsi.jigasi.transcription.SAVE_JSON=true org.jitsi.jigasi.transcription.SEND_JSON=true org.jitsi.jigasi.transcription.SEND_TXT=falseMake the custom file readable by UID 1000. Run
dc config --quietanddc up -d --force-recreate transcriber. At session end, administrators can retrieve generated files directly from host storage. Without protection, the URL is${PUBLIC_URL}/transcripts/<generated-directory>/<generated-filename>; hiding its advertisement is not access control. [S1][S3][S4][S5][S10] -
Docker stable-11248: optional S3 hook. PR #2225 closed without merging on June 7, 2026. Its proposed hook environment variables are unavailable in this release. Use custom properties and an executable script instead. The hook receives one absolute transcript-directory argument, once per publisher; Jigasi does not wait, retry or check its exit status. [S5][S7]
Run
mkdir -p "${CONFIG}/transcript-hooks", then save this example as${CONFIG}/transcript-hooks/upload-s3.sh. Replaceamzn-s3-demo-bucketwith your private bucket. It uploads only TXT/JSON, records its own result and retains local files. [S7][S8][S10]Terminal#!/bin/sh set -eu umask 077 dir=${1:?Missing transcript directory} exec >>"$dir/s3-upload.log" 2>&1 found=false for file in "$dir"/*.txt "$dir"/*.json; do [ -f "$file" ] || continue found=true if aws s3 cp "$file" "s3://amzn-s3-demo-bucket/jigasi/$(basename "$dir")/" --only-show-errors; then : else printf '%s\n' 'S3 upload failed' exit 1 fi done [ "$found" = true ] || { printf '%s\n' 'No saved transcript found'; exit 1; } printf '%s\n' 'S3 upload completed'Run
chmod 755 "${CONFIG}/transcript-hooks" "${CONFIG}/transcript-hooks/upload-s3.sh". Stock Jigasi lacks AWS CLI. Save this example asDockerfile.transcript-s3: [S5][S8][S10]dockerfileFROM ghcr.io/jitsi/jigasi:stable-11248 USER root RUN apt-dpkg-wrap apt-get update \ && apt-dpkg-wrap apt-get install -y --no-install-recommends awscli \ && apt-cleanup USER s6Build with
docker build -f Dockerfile.transcript-s3 -t jigasi-transcript-s3:stable-11248 .. Provision an existing protected shared-credentials file at${CONFIG}/transcript-aws/credentials, outside Git, readable by UID 1000 with mode 600. Save this astranscript-s3.yml; set your bucket region: [S5][S8][S10]YAMLservices: transcriber: image: jigasi-transcript-s3:stable-11248 environment: AWS_SHARED_CREDENTIALS_FILE: /opt/transcript-aws/credentials AWS_DEFAULT_REGION: us-east-1 volumes: - ${CONFIG}/transcript-hooks:/opt/transcript-hooks:ro - ${CONFIG}/transcript-aws/credentials:/opt/transcript-aws/credentials:roAppend to the same custom properties file: [S7]
Propertiesorg.jitsi.jigasi.transcription.EXECUTE_SCRIPTS=true org.jitsi.jigasi.transcription.SCRIPTS_TO_EXECUTE_LIST=/opt/transcript-hooks/upload-s3.sh org.jitsi.jigasi.transcription.SCRIPTS_TO_EXECUTE_LIST_SEPARATOR=,Add
-f transcript-s3.ymltodc, validate, then recreatetranscriber. An IAM role is another credential route; containers need working metadata access if relying on an instance role. Never bake keys into the image. [S8][S10] -
Debian/Ubuntu Jigasi baseline: enable storage and the same hook. [S3][S6][S10]
Terminalsudo install -d -o jigasi -g jitsi -m 750 /var/lib/jigasi/transcriptsEdit
/etc/jitsi/jigasi/sip-communicator.properties: use step 2’s flags, withorg.jitsi.jigasi.transcription.DIRECTORY=/var/lib/jigasi/transcriptsandorg.jitsi.jigasi.transcription.ADVERTISE_URL=false. Packages do not create Docker’s download route. [S3][S6]For uploads, use the official system installer: [S8][S10]
Terminalcurl -fsSL https://awscli.amazonaws.com/v2/install.sh | sudo bash -s -- --systemSave the same script locally as
upload-s3.sh, then runsudo install -m 755 upload-s3.sh /usr/local/bin/jitsi-transcript-upload-s3. Configure credentials for service userjigasi, not just your SSH login. Use step 3’s hook properties with that absolute script path. Runsudo systemctl restart jigasiduring maintenance. [S6][S7][S8][S10] -
Bridge proxy
6747f187: capture separately. Add to your existingopus-transcriber-proxyservice, retaining the bridge guide’s session-ID configuration: [S9][S11]YAMLenvironment: DUMP_TRANSCRIPTS: "true" DUMP_BASE_PATH: /data volumes: - ${CONFIG}/storage/bridge-transcripts:/data:ZRun
mkdir -p "${CONFIG}/storage/bridge-transcripts"and make it writable by the proxy’s actual runtime user. Its image does not declare Jitsi’s UID 1000. Recreate the proxy. Final messages become/data/<sessionId>/transcript.jsonl, one JSON object per line with capture timestamp and full message. This documented debug capture has no automatic cleanup or Jigasi download-link delivery. Keep it outside web-served storage. [S9][S10] -
Both paths: retain and delete deliberately. Example Docker candidates older than 30 days: [S10]
Terminalfind "${CONFIG}/storage/transcripts" -type f -mtime +30 -printCheck upload success and your policy before replacing
-printwith-delete. Use the corresponding package/bridge directory separately. Configure S3 lifecycle expiration too, including noncurrent versions where versioning is enabled. Deleting local files does not remove S3 objects, backups or cached downloads. [S8][S10]
Configuration reference
Jigasi rows below use the exact prefix org.jitsi.jigasi.transcription. in properties files. Docker appends custom properties after its generated settings. [S3][S5][S7]
| Setting | Default: released code / stock Docker | Purpose |
|---|---|---|
DIRECTORY |
/var/lib/jigasi/transcripts / /tmp/transcripts |
Saved files. [S3][S1] |
BASE_URL |
http://localhost/ / ${PUBLIC_URL}/transcripts |
Advertised URL base. [S3][S1] |
SAVE_TXT, SAVE_JSON |
false, false / true, false |
Complete file formats. [S3][S1] |
SEND_TXT, SEND_JSON |
false, true / same |
Live chat/captions. [S3][S1] |
ADVERTISE_URL |
false / false |
Announce saved-file links. [S3][S1] |
EXECUTE_SCRIPTS |
false / no mapping |
Enable hook. [S7] |
SCRIPTS_TO_EXECUTE_LIST |
script/example_handle_transcript_directory.sh / no mapping |
Executable paths; explicitly override. [S7] |
SCRIPTS_TO_EXECUTE_LIST_SEPARATOR |
, / no mapping |
Java regex delimiter; avoid spaces around list entries. [S7] |
JIGASI_TRANSCRIBER_ENABLE_SAVING |
Docker .env: 1 |
Emit saving block. [S1] |
JIGASI_TRANSCRIBER_ADVERTISE_URL, JIGASI_TRANSCRIBER_SEND_TXT |
Docker .env: false, false |
Map corresponding properties. [S1] |
CONFIG, PUBLIC_URL |
Docker .env: ~/.jitsi-meet-cfg, setup-specific |
Storage root and external URL. [S1] |
location, return |
Web custom Nginx config: no block by default | Example disables transcript downloads. [S4][S10] |
AWS_SHARED_CREDENTIALS_FILE, AWS_DEFAULT_REGION |
AWS CLI environment: ~/.aws/credentials, unset region |
Example selects private credentials and bucket region. [S8] |
DUMP_TRANSCRIPTS, DUMP_BASE_PATH |
Proxy environment: false, /tmp |
Optional JSONL capture. [S9] |
Common mistakes
Jigasi’s README says TXT saving defaults on, but released code defaults off. Docker explicitly enables TXT. A custom directory from an older setup can override the new storage mapping. Changing generated /run/jigasi/config/sip-communicator.properties disappears on recreation. [S1][S3][S5][S12]
JIGASI_TRANSCRIBER_ENABLE_SAVING=0 omits Docker’s saving block, allowing code defaults to disable files unless custom flags enable them again. For explicit disablement, set both saving properties false. Startup still checks /tmp/transcripts write access. [S1][S3][S5]
Verify
Finish a test transcription session after speaking a unique sentence. Run dc logs --tail=100 transcriber and: [S3][S10]
find "${CONFIG}/storage/transcripts" -type f \( -name '*.txt' -o -name '*.json' \) -print
TRANSCRIPT_FILE=$(find "${CONFIG}/storage/transcripts" -type f -name '*.txt' -print -quit)
TRANSCRIPT_PATH=${TRANSCRIPT_FILE#"${CONFIG}/storage/transcripts/"}
[ -n "$TRANSCRIPT_FILE" ] && curl -sS -o /dev/null -w '%{http_code}\n' \
"https://meet.example.com/transcripts/${TRANSCRIPT_PATH}"Expect the exact log prefix Wrote final transcript to followed by the generated path. Open the actual file and confirm your sentence. The known-file HTTP check must print 403; test every public entry point, including the origin if using a CDN. For S3, require your hook’s S3 upload completed log and verify the actual object with aws s3 ls s3://amzn-s3-demo-bucket/jigasi/ --recursive, using your bucket. [S3][S8][S10]
For bridge dumps, check the log prefix Transcript dump enabled: and inspect the generated JSONL with jq -r '.message.transcript[].text' transcript.jsonl. This command assumes you have copied the actual dump to that local filename. Check final words, participant IDs and persistence after recreation. [S9]
If it still fails
Look for Unable to write transcript to file , Was unable to make a directory called or Could not execute in transcriber logs. Package logs are /var/log/jitsi/jigasi.log. Inspect ownership, effective custom properties, CLI availability, credentials and each directory’s s3-upload.log. There is no built-in upload retry; rerun the script against a failed directory after fixing the cause. [S3][S6][S7][S8]
For missing speech or XMPP errors, use transcription troubleshooting. [S11]
FAQ
Does SEND_TXT send the transcript file to participants?
It sends recognized speech to meeting chat. File delivery requires a separate authenticated download or your application workflow. [S3]
Can I save captions from an embedded meeting?
Stable-11248 exposes transcriptionChunkReceived with { data }; final text is data.final, while data.stable is still interim. Capture final messages in your app and handle reconnection, deduplication and permission checks. [S9][S12]
Does the bridge replacement save the same TXT file?
No. Its optional dump is JSONL of final provider messages, not Jigasi’s complete transcript format or an automatic download service. For managed delivery, see our transcription setup service. [S9][S11]
Sources
All checked 2026-10-06.
[S1] Latest release, current transcriber, web volumes, transcription template, Docker handbook, stable-11248 released 2026-09-14, release note/source code/official doc.
[S2] stable-11031 overlay, old web volumes, rootless PR #2258, stable-11146, 2026-08-03 release, source code/release note.
[S3] Released Jigasi flags, publisher, TXT, JSON, final filter, session end, 2026-09-11 snapshot, source code.
[S4] Docker Nginx route, web config copy, Nginx launch, stable-11248, source code.
[S5] Docker custom properties, Jigasi image, apt wrapper, cleanup, stable-11248, source code.
[S6] Jigasi package index, installed paths, ownership, service, package 1.1-415, official repository/source code.
[S7] Script invocation, source code; Docker PR #2225, closed 2026-06-07, unmerged proposal.
[S8] AWS upload, listing, CLI installation, CLI environment, roles, container metadata, public-access blocking, lifecycle, Debian awscli, official docs/package index.
[S9] Bridge proxy dump documentation, capture code, image, 2026-10-05 snapshot; Meet caption event, payload, source code/official doc.
[S10] Docker CLI, Nginx location, return, switches, coreutils, find, systemctl, curl, shell, official docs.
[S11] jitsi.help Vosk, bridge, troubleshooting, service, independent guides.
[S12] Meet client configuration, event test, stable-11248; Jigasi README, 2026-09-11 snapshot, source code/official repository docs.
Open questions
Real meeting saves, the derived AWS image, end-to-end S3 delivery and proxy restart persistence need server testing. No stock hook retry, per-meeting download authorization, or automatic bridge TXT export was verified. Test custom routing and purge any previously cached public transcript copies. [S3][S4][S7][S8][S9]