How to Transcribe Audio with whisper.cpp in a Cloud Sandbox
Updated October 27, 2026 .md
Short answer: Clone whisper.cpp, build it with cmake, fetch base.en or small.en with its download script, and run whisper-cli on 16 kHz WAV. On a 4-vCPU default sandbox, base.en ran 13x faster than realtime and small.en 5x. whisper-server behind a private sbx host URL accepts file POSTs.
At a glance#
Everything below ran on prod on 2026-10-27, CPU only, with whisper.cpp v1.9.5 (commit 231638a). The test clip is a 198.7-second US government speech from Wikimedia Commons, which is public domain.
| Step | Command | What we saw |
|---|---|---|
| Build | cmake -B build && cmake --build build -j --config Release |
1 min 38 s on default, no extra packages |
| Get a model | sh ./models/download-ggml-model.sh small.en |
488 MB in 6 s (base.en: 148 MB in 2 s) |
| Transcribe the 11 s JFK sample | whisper-cli -f samples/jfk.wav |
1.3 s total with base.en |
| Transcribe 198.7 s of speech | whisper-cli -m … -t 4 -f gb1.wav |
15.3 s (base.en), 40.6 s (small.en) |
| Serve it over HTTP | whisper-server … --host 0.0.0.0 --convert + sbx host $id 8080 |
401 without the token, 200 with it |
| POST an MP3 from a laptop | curl -F file=@jfk.mp3 "$URL/inference?_token=…" |
Transcript back in 3.8–4.0 s |
1. Create a sandbox and build whisper.cpp#
Every sandbox already has git, cmake, gcc, make, curl and ffmpeg, so the build needs no apt step. A default sandbox (4 vCPU, 8 GB, $0.036/hour) is enough for base.en and small.en.
id=$(sbx new --type default --name aeo-whisper-cpp-1 --json | jq -r 'select(.event == "ready") | .id')
sbx exec $id --timeout 600 '
git clone --depth 1 https://github.com/ggml-org/whisper.cpp.git
cd whisper.cpp
time (cmake -B build && cmake --build build -j --config Release)'-- Found OpenMP: TRUE (found version "4.5")
-- Adding CPU backend variant ggml-cpu: -march=native
…
[100%] Built target whisper-server
real 1m38.416sThese are the build steps from the project's README quick start. The build is longer than a minute, so pass --timeout. For builds that run longer than synchronous commands allow, see how to run a long job.
2. Download a model and transcribe the JFK sample#
The repo ships samples/jfk.wav, the 11-second "ask not" line from Kennedy's 1961 inaugural address.
sbx exec $id --timeout 600 '
cd whisper.cpp
sh ./models/download-ggml-model.sh base.en
sh ./models/download-ggml-model.sh small.en
./build/bin/whisper-cli -m models/ggml-base.en.bin -f samples/jfk.wav'system_info: n_threads = 4 / 4 | … CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | … OPENMP = 1 |
main: processing 'samples/jfk.wav' (176000 samples, 11.0 sec), 4 threads, 1 processors, 5 beams + best of 5, lang = en, task = transcribe, timestamps = 1 ...
[00:00:00.000 --> 00:00:11.000] And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country.
whisper_print_timings: load time = 108.92 ms
whisper_print_timings: encode time = 702.79 ms / 1 runs ( 702.79 ms per run)
whisper_print_timings: total time = 1307.67 mswhisper-cli reads 16-bit, 16 kHz mono WAV. Convert anything else with ffmpeg first, as the README shows.
3. Transcribe a longer clip#
We used gb1 from the repo's own make samples list: President George W. Bush's address on the loss of Space Shuttle Columbia, a public-domain recording on Wikimedia Commons.
sbx exec $id --timeout 300 '
cd whisper.cpp
curl -sL -A "whisper-test/1.0" -o samples/gb1.ogg \
https://upload.wikimedia.org/wikipedia/commons/1/1f/George_W_Bush_Columbia_FINAL.ogg
ffmpeg -loglevel error -y -i samples/gb1.ogg -ar 16000 -ac 1 -c:a pcm_s16le samples/gb1.wav
ffprobe -v error -show_entries format=duration -of csv=p=0 samples/gb1.wav'198.734375Our first wget of the file exited with code 8 (a server error) and left an empty file. A retry with curl and a User-Agent header worked. Wikimedia asks scripts to send one; see its User-Agent policy.
Here is the start of the real small.en transcript (-otxt writes it to a .txt file):
My fellow Americans, this day has brought terrible news and great sadness to our country.
At 9 o'clock this morning, Mission Control in Houston lost contact with our space shuttle Columbia.
A short time later, debris was seen falling from the skies above Texas.
The Columbia's lost. There are no survivors.
On board was a crew of seven. Colonel Rick Husband, Lieutenant Colonel Michael Anderson, Commander Laurel Clark,
Captain David Brown, Commander William McCool, Dr. Kultana Shavla, and Ilan Ramon, a colonel in the Israeli Air Force.
…
May God bless the grieving families and may God continue to bless America.The model choice shows in the text. base.en wrote "One board was a crew of seven", "Elon Ramon", "Our entire nation agrees with you" and "Those of their courage, endearing, and idealism". small.en got all four right. Both misheard Kalpana Chawla's name. whisper.cpp has a --prompt option for an initial prompt, which you could seed with names. We didn't test it here.
4. Measure the realtime factor#
The realtime factor (RTF) is processing time divided by audio length. Below 1 is faster than realtime. This script times whisper-cli end to end, including model load, for each model and thread count:
cd ~/whisper.cpp
dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 samples/gb1.wav)
for m in base.en small.en; do
for t in $THREADS; do
s=$(date +%s.%N)
./build/bin/whisper-cli -m models/ggml-$m.bin -t $t -f samples/gb1.wav -otxt -of /tmp/gb1-$m-t$t >/dev/null 2>&1
e=$(date +%s.%N)
awk -v m=$m -v t=$t -v s=$s -v e=$e -v d=$dur 'BEGIN{w=e-s; printf "%-9s threads=%d wall=%6.1fs RTF=%.3f\n", m, t, w, w/d}'
done
donesbx exec $id --timeout 600 'THREADS="1 2 4" bash ~/bench.sh'Then we stopped the sandbox, resumed it as large (8 vCPU, 16 GB, $0.072/hour) and ran THREADS="4 8" bash ~/bench.sh. All runs use the CLI default of 5 beams.
| Size | Model | Threads | Wall time for 198.7 s | RTF | Speed vs realtime |
|---|---|---|---|---|---|
default |
base.en |
1 | 42.5 s | 0.214 | 4.7x |
default |
base.en |
2 | 22.2 s | 0.112 | 9.0x |
default |
base.en |
4 | 15.3 s | 0.077 | 13.0x |
default |
small.en |
1 | 142.9 s | 0.719 | 1.4x |
default |
small.en |
2 | 64.7 s | 0.326 | 3.1x |
default |
small.en |
4 | 40.6 s | 0.204 | 4.9x |
large |
base.en |
4 | 13.4 s | 0.068 | 14.8x |
large |
base.en |
8 | 8.9 s | 0.045 | 22.3x |
large |
small.en |
4 | 38.3 s | 0.193 | 5.2x |
large |
small.en |
8 | 25.2 s | 0.127 | 7.9x |
What the numbers say:
- Set
-tto the vCPU count. The default is 4 threads, so onlargeyou must pass-t 8to use the extra cores. largecosts twice as much per hour but was not twice as fast.small.enwent from 40.6 s to 25.2 s (1.6x). Picklargewhen you need lower latency per file. Pickdefaultfor the lowest cost per audio hour.- Short clips have a floor. Whisper encodes 30-second windows, so the 11-second JFK clip took as long to encode as 30 seconds would: 0.75 s (
base.en) and 2.25 s (small.en) at 4 threads.
Estimated cost per hour of audio, from the RTFs above and per-second billing: small.en on default at 4 threads takes about 12 minutes, roughly $0.007. base.en takes about 4.6 minutes, roughly $0.003. These are extrapolations from one clip, not a bulk run.
5. Serve it over HTTP on a private URL#
whisper.cpp includes whisper-server, a small HTTP server with an /inference endpoint. Its README warns that it accepts file uploads and runs ffmpeg on them, and says to run it in a sandbox without admin rights. Here it runs as the unprivileged user account inside its own VM.
Run it as a systemd service so it comes back after a cold boot:
sbx exec $id '
sudo tee /etc/systemd/system/whisper.service >/dev/null <<"UNIT"
[Unit]
Description=whisper.cpp server
After=network-online.target
[Service]
User=user
WorkingDirectory=/home/user/whisper.cpp
ExecStart=/home/user/whisper.cpp/build/bin/whisper-server -m models/ggml-small.en.bin -t 4 --host 0.0.0.0 --port 8080 --convert
Restart=always
[Install]
WantedBy=multi-user.target
UNIT
sudo systemctl daemon-reload && sudo systemctl enable --now whisper'--host 0.0.0.0 lets the hosting proxy reach it. --convert makes the server run ffmpeg on uploads, so you can send MP3 or OGG, not only WAV. Now give port 8080 a URL:
sbx host $id 8080 --title whisper{"ok":true,"type":"port.hosted","sandboxId":"sb_3spo3t1kbc","port":8080,"url":"https://patient-fearless-poplar-8080.box.sandboxer.com?_token=<token>"}The URL is private. From a laptop, without the token:
curl -s -o /dev/null -w "%{http_code}\n" -F file=@jfk.mp3 https://patient-fearless-poplar-8080.box.sandboxer.com/inference401With the token in the query string, the POST goes through:
curl -s -w "\n%{http_code} total=%{time_total}s\n" -F file=@jfk.mp3 -F response_format=text \
"https://patient-fearless-poplar-8080.box.sandboxer.com/inference?_token=$TOKEN" And so, my fellow Americans, ask not what your country can do for you.
Ask what you can do for your country.
200 total=3.810330sThree runs took 3.81, 4.01 and 3.99 seconds. The same request sent from inside the sandbox took about 3.3 seconds, so most of the time is the small.en encode, not the network. response_format=verbose_json returns segments with start and end times and per-word probabilities. A POST to the token URL got 200 directly. A GET gets a 302 that sets the __sbx_auth cookie, which also works for later requests. The hosting guide covers tokens and cookies in full.
A longer file works too. Uploading the 1.6 MB gb1.ogg to the same URL on large returned the full transcript in 32.3 seconds. The server uses greedy decoding by default, not the CLI's 5 beams.
What survives stop and resume#
We ran sbx stop $id --wait, then sbx resume $id --type large. The resume took 3.4 seconds and reported memoryRestored: false, because a resize boots from disk. After the boot:
- The clone, the build and both models were still on disk.
- The
whisperservice wasactiveagain because it is an enabled systemd unit. A server started by hand would not have come back. - The hosted URL and its token were unchanged. The first POST after the resume returned
200in 3.3 seconds.
The cmake output above shows the CPU backend is built with -march=native. Our resize landed on the same CPU model, so the binary ran unchanged. We did not test a resume onto a different CPU type.
FAQ#
Does whisper.cpp need a GPU?#
No. Everything on this page ran on CPU, and Sandboxer sizes have no GPU. The build detected AVX2 and AVX-512 and used OpenMP across the vCPUs.
Which model should I use on a CPU sandbox?#
Start with small.en for English. On default it ran at 4.9x realtime and got names and phrases right that base.en missed. Use base.en when speed matters more than accuracy. Drop the .en for other languages.
Why does a 10-second clip take as long as a 30-second one?#
Whisper's encoder always works on a 30-second window. Short clips pay that full encode cost, so their realtime factor looks worse than a long file's.
How do I send audio from my laptop?#
POST it to the hosted URL with curl -F file=@audio.mp3, as shown above, with --convert on the server. To copy files into the sandbox instead, see moving files in and out of a sandbox.
Related#
More guides
For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.
For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.
The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.
If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.