Botium Speech Processing: a self-hosted STT and TTS stack for voice testers
Botium Speech Processing
At a glance
- What is it?
- Botium Speech Processing bundles Kaldi, MaryTTS and SoX behind one HTTP API and a Docker Compose file. It is opinionated, resource-hungry, and aimed at people who need speech in a test or IVR pipeline without a cloud bill.
- Who is it for?
- Adopt Botium Speech Processing if you need offline speech-to-text and text-to-speech inside an automated test or IVR pipeline and you can give Docker 8GB of RAM and 40GB of disk. Skip it if you need many languages, streaming-grade accuracy, or a small footprint, because the default install ships German and English only and the README describes the configuration options as rudimentary.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 18 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 19, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Botium Speech Processing removes
Wiring speech into a test suite usually means gluing together three unrelated tools: something that turns audio into text, something that turns text into audio, and something that converts the file formats in between. Each has its own install story, its own language data, and its own way of failing. Botium Speech Processing takes that glue work and ships it as one set of Docker images with a single HTTP surface in front.
The README is blunt about the trade-off. It calls the project a get-shit-done-style stack whose configuration options are rudimentary and which is highly opinionated about the included tools. That is not marketing language, it is a design statement: Kaldi for recognition, MaryTTS for synthesis, SoX for audio conversion, and no plugin marketplace to browse.
The audience follows from that. The README lists synthesizing audio tracks for tutorials, voice-enabled chatbot services such as IVR systems, classification of audio transcriptions, and automated testing of voice services with Botium. The Rasa custom voice channel under connectors/rasa is the concrete example of the chatbot case. If you are building a general-purpose speech platform, this is the wrong shape. If you are testing a voice bot and want the recognition step to live on your own machine, it is the right one.
How the Docker Compose topology fits together
The stack is a set of services defined in docker-compose.yml, fronted by nginx on port 80. The frontend image, botium/botium-speech-frontend, is the only service that speaks HTTP to the outside world; nginx proxies to it using nginx.conf mounted from the repository root.
Behind the frontend sit the workers. stt-en and stt-de run the Kaldi images for English and German. tts runs the MaryTTS image. dictate runs the dictate.js interface used for browser-based recognition testing. A watcher service mounts ./watcher into /app/watch and a named logs volume shared with the Kaldi services.
Provider selection happens through environment variables on the frontend: BOTIUM_SPEECH_PROVIDER_TTS defaults to marytts and BOTIUM_SPEECH_PROVIDER_STT defaults to kaldi. Google credentials can be supplied through BOTIUM_SPEECH_GOOGLE_CLIENT_EMAIL and BOTIUM_SPEECH_GOOGLE_PRIVATE_KEY. The README recommends not editing .env directly but creating a .env.local file to override defaults, so a future git pull does not collide with your changes.
Caching is part of the data flow. Speech-to-text and text-to-speech results are cached by MD5 hash of the audio or the input text, and the README says to empty frontend/resources/.cache/stt or frontend/resources/.cache/tts to force reprocessing. That is convenient until you change a model or a provider setting and wonder why the old transcript keeps coming back.
Installing Botium Speech Processing and running a first transcription
The README lists the requirements plainly: 8GB of RAM accessible to Docker, 40GB of free disk space for a full installation, internet connectivity, docker, and docker-compose. It notes that memory use drops if only one Kaldi language is needed, because the default configuration ships two.
Clone the repository and bring the stack up. This pulls the released prebuilt images from Docker Hub rather than building anything locally, so the first run is mostly a download.
docker-compose up -dThere is a separate command for the developer images, which the README documents as follows.
docker-compose --env-file .env.develop upOnce the containers are running, http://127.0.0.1 serves the Swagger UI, which is the intended way to browse and exercise the API definition in frontend/src/swagger.json. The README also points to http://127.0.0.1/dictate/ for a dictate.js test interface, with the caveat that Google Chrome only allows it over HTTPS, so you would need to publish it yourself, for example through an ngrok tunnel.
For a first real transcription, the README gives this curl invocation against the English endpoint with a WAV file.
curl -X POST "http://127.0.0.1/api/stt/en" -H "Content-Type: audio/wav" -T sample.wavIf you would rather not run a command, the watcher offers a file-drop path. Place an audio file in watcher/stt_input_en and the transcript appears in watcher/stt_output_en. Text files dropped into watcher/tts_input_en produce synthesized speech in watcher/tss_output. Note the spelling of that output folder as the README writes it.
There is also a streaming endpoint. Calling /api/sttstream/{language} opens a websocket stream and returns three URLs: wsUri to send audio chunks to, statusUri to check whether the stream is still open, and endUri to close it. By default it accepts wav-formatted chunks.
Where the opinionated defaults bite
The most obvious limitation is language coverage. The default configuration includes German and English, and the stt-en and stt-de services are separate images. Adding a language is not a configuration flag; it means building your own images, which is why docker-compose-dev.yml exists and why the README warns that building takes some time.
Quality is the second constraint, and the README states it directly rather than burying it: the included tools in most cases cannot compete with the big cloud-based products. For many applications the price-to-quality trade-off is described as at least reasonable. That is an honest framing, but it means this stack is a poor fit when recognition accuracy is the product rather than a step in a test.
The configuration surface is deliberately small. If you want to tune decoding parameters, swap the synthesis engine, or run a different acoustic model, the project does not offer a plugin layer to do it cleanly. The cloud-specific compose files are the escape hatch: docker-compose-azure.yml, docker-compose-deepgram.yml, docker-compose-google.yml and docker-compose-ibm.yml each install a slim variant where only the frontend service is required, with your subscription or API key added to the file. That trades self-hosting for a cloud dependency, which may be exactly what you do not want.
Finally, the browser-based dictate interface only works with Kaldi, and the README flags the HTTPS requirement in Chrome. If your testing environment is plain HTTP on a LAN address, that page will not do what you expect.
Securing and operating the API
By default the API is open to anyone who can reach port 80. The README documents one access control mechanism: the BOTIUM_API_TOKENS environment variable holds a list of valid tokens separated by whitespace or comma, and the BOTIUM_API_TOKEN HTTP header is validated on every call. There is no mention of per-token scopes, rate limiting, or audit logging, so treat this as a shared secret rather than an authorization system.
Request bodies can override server defaults. For JSON or multipart requests, a credentials section replaces the default cloud credentials and a config section replaces the default settings for the cloud API call, per request. The README's Google example shows both: one call passes private_key and client_email, another switches the encoding to MP3. That flexibility is useful when different test tenants need different keys, but it also means credentials travel in request bodies, which is worth thinking about before you put this behind a public address.
On the licence side, the repository is MIT. That covers the code in this repository. It does not automatically relicense Kaldi, MaryTTS or SoX, which are pulled in as separate images and carry their own terms. If you plan to redistribute the built images or bundle them into a product, check each component's licence rather than assuming the MIT header settles it. This is not legal advice; it is a pointer to where the question actually lives.
Comparing the self-hosted stack with cloud speech APIs
The real alternative is not another open source project, it is the cloud APIs this stack can also front. Google, IBM, Azure and Deepgram each have a compose file here, and choosing one of those changes the architecture rather than just the backend: the slim installation needs only the frontend service, because recognition and synthesis happen on the provider's machines.
The difference in approach is stark. Self-hosted Kaldi and MaryTTS keep audio on your hardware, cost nothing per request, and keep working without a network connection to a vendor. In exchange you pay 8GB of RAM and 40GB of disk, you accept the accuracy ceiling the README describes, and you own the upgrade path for the models. The cloud variants invert every one of those: no local compute, no model maintenance, better recognition, and a per-request bill plus a data transfer question you have to answer for yourself.
The pragmatic middle is the one the project enables without preferring: run the self-hosted stack for the languages it covers, and switch a single request to a cloud provider by passing stt=google or stt=ibm in the query string with credentials in the body. The README demonstrates both. That lets a test suite keep a free local default and escalate specific cases, at the cost of maintaining two sets of credentials and two failure modes.
Upgrades, release cadence and what to check
The repository is not archived. The most recent push recorded is 2026-09-02, and release 1.8.2 carries the same timestamp, with 1.8.1 the day before and 1.8.0 in June 2026. So the project is being tagged, but the README does not document a rollback procedure, a migration path between versions, or a compatibility policy for the API.
Upgrades are image pulls. The compose files reference botium/botium-speech-frontend:${TAG} and similar image names, so the TAG variable is what pins a version. The README does not state what happens to cached results across a version change, and since the cache key is an MD5 hash of the audio or text, a model change behind the same input would not invalidate the cache on its own. Emptying frontend/resources/.cache/stt and frontend/resources/.cache/tts after an upgrade is the documented way to force fresh processing.
The repository also ships a Makefile with docker_build, docker_publish, develop and release targets that tag and push images to an AWS registry. Those targets are the maintainers' publishing path, not a deployment guide for users, and the Makefile assumes SPEECH_VERSION and AWS_REGISTRY_HOSTNAME are set. If you build your own images, expect to manage that yourself. The README does not describe a supported upgrade path for the watcher's input and output directories either, so any files sitting in watcher/stt_input_en during a restart are worth clearing deliberately.
Editorial conclusion
Adopt Botium Speech Processing if you need offline speech-to-text and text-to-speech inside an automated test or IVR pipeline and you can give Docker 8GB of RAM and 40GB of disk. Skip it if you need many languages, streaming-grade accuracy, or a small footprint, because the default install ships German and English only and the README describes the configuration options as rudimentary. Before committing, verify that your host meets the RAM and disk requirements, decide which language images you actually need, and check whether the MIT licence and the bundled Kaldi, MaryTTS and SoX components fit how you plan to redistribute the images.
Frequently asked questions
How do I install Botium Speech Processing?
Clone the repository and run docker-compose up -d, which pulls the released prebuilt images from Docker Hub. The README lists 8GB of RAM accessible to Docker, 40GB of free disk space, internet connectivity, docker and docker-compose as requirements.
Which speech-to-text and text-to-speech engines does Botium Speech Processing use by default?
The default configuration uses Kaldi for speech-to-text, MaryTTS for text-to-speech and SoX for audio file conversion, with German and English language data included. The frontend environment variables BOTIUM_SPEECH_PROVIDER_STT and BOTIUM_SPEECH_PROVIDER_TTS control these defaults.
How do I secure the Botium Speech Processing API?
Set the BOTIUM_API_TOKENS environment variable to a whitespace- or comma-separated list of valid tokens. The server then validates the BOTIUM_API_TOKEN HTTP header on each call to the API.
Why does Botium Speech Processing return an old transcription after I change something?
Speech-to-text and text-to-speech results are cached by MD5 hash of the audio or the input text. To force reprocessing, empty the frontend/resources/.cache/stt or frontend/resources/.cache/tts directories.
Community notes