Skip to content

Extend dockerization - #365

Merged
kaixxx merged 7 commits into
kaixxx:mainfrom
chrtmnn:extend-dockerization
Oct 19, 2026
Merged

kaixxx merged 7 commits into
kaixxx:mainfrom
chrtmnn:extend-dockerization

Conversation

@chrtmnn

@chrtmnn chrtmnn commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Add a Docker image that runs noScribe headless on the CPU

This adds docker/Dockerfile, an image for servers and batch jobs: audio in, transcript out. Everything runs locally in the container, and nothing is downloaded at runtime.

docker build -f docker/Dockerfile -t noscribe .
docker run --rm -v "${PWD}:/data" noscribe interview.mp3 interview.html --language de

What is in the resulting image

  • Models: both Whisper models (precise and fast) are fetched from Hugging Face during the build, pinned to a revision and checked by SHA-256. A local models folder is kept out of the build context.
  • PyTorch: installed from the CPU wheel index with a fixed version, so pip cannot pull in the much larger CUDA build. There is no GPU support yet.
  • Entrypoint: appends --no-gui, so all noScribe command line options work as usual. With 'gui` as the first argument it starts the regular window instead, which needs an X server from the host (experimental, only tried on Windows 11 with Docker Desktop/WSLg).
  • User: runs as UID 1000; --user "$(id -u):$(id -g)" works too. Settings live in /config/noScribe and can be kept in a volume.

Size is about 3 GB to download and about 8 GB on disk.

Closes #364

With text=auto alone, a Windows checkout with core.autocrlf turns *.sh
into CRLF files. Copied into a Linux container they fail on the first
line, because the kernel looks for an interpreter named "sh\r".
* `docker/Dockerfile` builds an image for servers and batch jobs.

* Both Whisper models (precise and fast) are fetched from Hugging Face
during the build, pinned to a revision and checked by SHA-256

* The local `models/` folder is kept out by `Dockerfile.dockerignore`.

* `docker/requirements.lcok`: The build installs under that set and fails if the result differs; `docker/update-lock.sh` regenerates it.

* The entrypoint appends --no-gui; with `gui` as first argument it starts
the regular window instead, which needs an X server from the host. The
container runs as UID 1000, with /config and $HOME writable for anyone
so that --user works too.
`docker/README.md` covers build, usage, file permissions, settings and
additional models, the experimental GUI mode, how the lock file is
maintained, and the name clash with the image tag used by
`generate_linux_binary.sh`. The main `README` links to it.
@kaixxx

kaixxx commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Thank you, this looks nice. Two questions:

  • requirements.lock pins torchaudio==2.8.0+cpu, but the Linux ARM64 wheel is versioned 2.8.0 without the +cpu suffix. I suspect this would cause the documented build to fail on Apple Silicon and ARM servers. Could you either adjust the dependency locking to support ARM64?
  • How difficult would it be to add optional CUDA support? I think that the advantages of CUDA might be one major reason for people wanting to run noScribe on a local server, and Docker would be ideal for that.

@chrtmnn

chrtmnn commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for taking a look!

ARM64

True: on the PyTorch CPU index, torch is 2.8.0+cpu on both architectures, but torchaudio is 2.8.0+cpu on x86_64 and plain 2.8.0 on aarch64. So the lock file as it stands only works on amd64.

When I was checking this I think I also found a second blocker that is independent of the lock: torchcodec==0.7.0 (pinned in environments/requirements_linux.txt) has no Linux aarch64 wheel and no sdist. The first release with aarch64 wheels seems 0.11.0, which is built for a much newer torch than 2.8. It can't simply be dropped either, since pyannote.audio 4 declares torchcodec>=0.7.0 as a hard dependency. So a native ARM64 image needs more than a per-architecture lock: either the torch 2.8 pin has to move, or torchcodec has to be installed in a version that doesn't match torch, relying on noScribe not loading it at runtime. That's your call, and I'd rather not mix it into this PR.

I would suggest for this PR as a first start: State clearly that the image is amd64 only, and document --platform linux/amd64 for Apple Silicon (works under emulation, but slowly). I'll open a follow-up issue for native ARM64 that describes the torchcodec problem, so it can be picked up once that question is settled. Would that be OK for you?

CUDA

Doable, and I totally agree it's the obvious use case. The torch index is already a build argument and noScribe detects CUDA by itself, so the main work left to do is:

  • a second lock file for the CUDA variant (+cu128 versions plus the nvidia-* packages)
  • making sure ctranslate2 finds cuBLAS/cuDNN from the pip packages (probably an LD_LIBRARY_PATH entry; haven't tested this yet)
  • checking how the CUDA fallback prompt behaves with --no-gui
  • docs for the NVIDIA Container Toolkit and --gpus all

The cuda image will be several GB larger, so I'd build it as a separate tag rather than making it the default. I'd prefer to do this as a follow-up PR so this one stays small. I have an NVIDIA card to test the build and GPU detection with, but it is short on VRAM, so I may need someone with a larger card to confirm a full run with the precise model.

The lock file is now checked out with LF and the comparison strips CRs;
with core.autocrlf=true every line differed and the build failed.

PYTHONSAFEPATH=1 stops `python -m` from putting the mounted folder
ahead of PYTHONPATH, where it could shadow the image's code and models.
@happyarts

Copy link
Copy Markdown
Contributor

Two things that may help with the torchcodec question on ARM64:

The torch 2.8 pin on Linux looks outdated. The comment in requirements_linux.txt ("pyannote 4 is currently not compatible with torch 2.9") no longer holds. In #327 we moved the macOS arm64 stack to torch 2.13 / torchaudio 2.11 / torchcodec 0.15, and pyannote 4.0.7 gave identical segments and speakers at the same speed as on torch 2.8. That stack is what requirements_macOS_arm64.txt installs today. All three packages have Linux aarch64 wheels on PyPI, so moving Linux to the same versions should make a native ARM64 image possible without a per-architecture lock. We only tested this on macOS, not on Linux or in a container. I agree it belongs in a separate PR, not in this one.

noScribe does not use torchcodec at runtime. Since 72fc97f the diarization worker reads noScribe's own converted WAV with soundfile and hands pyannote the waveform in memory (load_waveform in pyannote_mp_worker.py). pyannote imports torchcodec inside a try/except and only warns if that fails (pyannote/audio/core/io.py), so torchcodec is never asked to decode anything. A mismatched torchcodec would therefore be safe as a stopgap, but with the newer stack it isn't needed.

(Written with help from Claude Code; the facts are from our own testing in #327 and from the code.)

@kaixxx

kaixxx commented Oct 6, 2026

Copy link
Copy Markdown
Owner

I think this would be a good opportunity to take a look at the all the linux requirements, as they may be outdated. I don't have a test machine at the moment. Are you on Linux, @chrtmnn? Could you take a look?
The ARM built is something I can test later on my Mac mini.
If you think this is ready, I can merge the PR. It would be great if you could also create a CUDA version (in a separate PR).

@chrtmnn

chrtmnn commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor Author

@kaixxx:

Are you on Linux, @chrtmnn? Could you take a look?

Not natively, I'm on Windows. But the image is a Linux test bed anyway: Docker Desktop runs containers on WSL2, and the build installs requirements_linux.txt on Debian bookworm (amd64). So I can check the Linux requirements through the build, including a real transcription run. What I can't cover is the PyInstaller binary (generate_linux_binary.sh), a native Linux desktop (GUI, themes, fonts) and real ARM64 hardware. A --platform linux/arm64 build under emulation would still show whether all wheels exist.

Proposal, since happyarts' notes remove the torchcodec blocker (the macOS stack from #327 has Linux aarch64 wheels, and noScribe doesn't use torchcodec at runtime):

  1. This PR: stays amd64 + CPU. I'm adding a section to docker/README.md on Apple Silicon/ARM (--platform linux/amd64, slow under emulation).
  2. Follow-up PR "Linux requirements": move requirements_linux.txt to the macOS stack (torch 2.13 / torchaudio 2.11 / torchcodec 0.15), verify via the Docker build and a transcription run, and try an arm64 build. A native ARM64 test on your Mac mini would still be needed.
  3. Follow-up PR "CUDA": separate lock file (+cu128), cuBLAS/cuDNN library path, --no-gui fallback behaviour, docs for --gpus all. I can test the build and GPU detection, but my card has too little VRAM for a full run with the precise model, so I'd need someone to confirm that.

Proposed image tags: <noScribe version>[-r<N>][-<flavor>], e.g. 0.7.2-r1 for the default (headless, CPU) and 0.7.2-r1-cuda for the CUDA build, with latest pointing to the latest default. ARM64 would later be added as a multi-arch manifest under the same tags. Details are in the new "Image tags" section of docker/README.md.

If that works for you, feel free to merge this PR once I've pushed the README changes.

@chrtmnn

chrtmnn commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

@kaixxx, a small question about something outside the Docker image itself:

generate_linux_binary.sh builds the root Dockerfile and tags the result noscribe, the same name I use for the image in this PR:

docker buildx build --platform linux/amd64 -t noscribe .
docker run --rm -it --mount type=bind,src=.,dst=/usr/src/app noscribe pyinstaller noScribe-linux.spec

docker buildx build --platform linux/amd64 -t noscribe .
docker run --rm -it --mount type=bind,src=.,dst=/usr/src/app noscribe pyinstaller noScribe-linux.spec

Running the script therefore silently replaces a locally built noscribe image from docker/Dockerfile, and the other way around. For now I only warn about this in docker/README.md:

That script also tags its image noscribe, so running it replaces the image built here; rebuild afterwards or use a different tag for one of them.

Would you be fine with renaming the tag in the script, e.g. to noscribe-binary-build (both lines 3 and 4)? Nothing else in the repo refers to it as far as I can see. I'd do it as a separate commit in this PR, or leave it as is and keep only the README note. Your call, since it's your build script.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@kaixxx

kaixxx commented Oct 11, 2026

Copy link
Copy Markdown
Owner

@chrtmnn I'm not sure if I understand the issue completely. But don't you think we should replace generate_linux_binary.sh in its current form by your approach, which is more sophisticated? Why have two ways to build a docker image?

@chrtmnn

chrtmnn commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

@kaixxx The two Dockerfiles cover two different jobs:

  • Root Dockerfile + generate_linux_binary.sh: a build tool. The image only holds Python and the requirements (including PyInstaller), runs PyInstaller on the mounted repo and produces the Linux binary. The image is thrown away afterwards.
  • docker/Dockerfile: a runtime image with noScribe, the models and pinned dependencies. It runs transcription from source (python -m noScribe --no-gui) and is the thing people actually run.

They only share the installation of requirements_linux.txt.

The only overlap is the local image name noscribe. Since the build image is disposable, renaming its tag in the script (e.g. to noscribe-binary-build) wouldn't cause any problems, but the note in docker/README.md is really sufficient, so I'd leave the script untouched unless you prefer the rename.

@chrtmnn

chrtmnn commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor Author

One addition: generate_linux_binary.sh calls pyinstaller noScribe-linux.spec, but there is no such file in the repo anymore; the spec is pyinstaller/noScribe_linux.spec. I ran the script on current main content: the image builds, but the PyInstaller step fails with ERROR: Spec file "noScribe-linux.spec" not found!. So the script doesn't work as written.

The Linux binary is built without Docker in .github/workflows/pyinstaller.yml (pip install -r environments/requirements_linux.txt, then pyinstaller pyinstaller/noScribe_linux.spec), and I found no other reference to the root Dockerfile or the script in the repo. If you don't use them locally either, you could remove both, which would also settle the name clash. Your call, I wouldn't touch them in this PR otherwise.

@chrtmnn

chrtmnn commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Hi @kaixxx, a quick friendly ping on this one.

Current stand

  • From my side the PR is ready to merge: headless CPU image (amd64), docs and the image tag scheme are in.
  • It does not touch the root Dockerfile or generate_linux_binary.sh.

Your open question on generate_linux_binary.sh
I'd suggest merging #365 as it is and sorting this out in a small separate PR. Two options:

  1. Remove the root Dockerfile and generate_linux_binary.sh, since CI builds the Linux binary without Docker and the script currently fails anyway (it calls noScribe-linux.spec, the file is pyinstaller/noScribe_linux.spec).
  2. Keep them, fix the spec path and rename the image tag so it no longer clashes with noscribe.

Just tell me which you prefer and I'll open that PR.

Next up after the merge

Thanks!

@kaixxx

kaixxx commented Oct 19, 2026

Copy link
Copy Markdown
Owner

Thanks for the ping ;)
Regarding generate_linux_binary.sh: Could you do a quick fix (your option 2)? For now, I would like to keep it as .github/workflows/pyinstaller.yml is mainly used for testing, not to produce final builds. I'll have to look into this a little more.

@kaixxx
kaixxx merged commit 36b4d38 into kaixxx:main Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vorschlag: Docker-Image für die Transkription ohne GUI (CLI) – upstream oder separates Repo?

3 participants