whisper.cpp Server
Overview
whisper.cpp is a high-performance implementation of OpenAI's Whisper automatic speech recognition model, written in plain C and C++. It runs entirely on the CPU, with no GPU, no Python runtime and no external service dependencies. This image ships the project's bundled HTTP server, so the machine becomes a private speech-to-text service the moment it boots: post an audio file and receive a transcript back.
This guide covers deploying and using whisper.cpp Server on Microsoft Azure with SecureAnyCloud's preconfigured Ubuntu 24.04 LTS virtual machine image. Because everything runs locally, audio never leaves the virtual machine you control. That suits recordings that cannot be sent to a third-party transcription service.
What you get:
- whisper.cpp 1.9.1, built from the pinned upstream source tag
- The bundled whisper.cpp HTTP server, listening on the loopback interface only
- The
ggml-base.enspeech recognition model, preinstalled and verified by checksum - nginx 1.24 as an authenticating reverse proxy on port 80
- ffmpeg, for automatic conversion of common audio and video formats
- A short test clip, so you can prove the service works before you use your own audio
- 24/7 SecureAnyCloud support
Key facts:
- Platform: Microsoft Azure
- Operating system: Ubuntu 24.04 LTS
- Default SSH user: azureuser
- Web interface and API: port 80, HTTP Basic authentication
- Inference engine: 127.0.0.1:8080, loopback only, not reachable from the network
- Model file: /var/lib/whisper-cpp-server/models/ggml-base.en.bin
- Credentials file: /root/whisper-cpp-server-credentials.txt
- Licence notices: /opt/whisper/
About the bundled model
whisper.cpp is a program, not a model. It needs a separate model file to do anything, and that file carries its own licence. This image ships ggml-base.en, and its provenance was traced to its root rather than assumed from a repository badge.
This image includes the ggml-base.en speech recognition model, a format conversion of OpenAI's original Whisper base.en checkpoint into the GGML format used by whisper.cpp. It is not a fine-tune and not a distillation of any third-party model. Both OpenAI's Whisper weights and whisper.cpp itself are distributed under the MIT License. Copies of both licences ship in the image at /opt/whisper/, together with a written provenance record.
The model file is pinned by name and by SHA-256 checksum at build time, so the image cannot silently ship a different model.
Prerequisites
Before you deploy, make sure you have:
- An active Microsoft Azure subscription
- An active Azure Marketplace subscription for this offer
- An SSH key pair for connecting to the virtual machine
- Permission to create virtual machines and network security groups in your target region
Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM). See Performance for what that size delivers and when to choose a larger one.
Network security group configuration
Allow these inbound ports:
- TCP 22: SSH access
- TCP 80: whisper.cpp web interface and transcription API, authenticated
Port 8080 is deliberately not in this list. The inference engine binds to the loopback interface only and is never reachable from the network. See Security model.
Deploy the virtual machine
Option A: Azure Portal
- In the Azure Portal, go to Marketplace and search for whisper.cpp Server on Ubuntu 24.04 LTS.
- Select the offer and click Create.
- Choose your subscription, resource group and region.
- Set the VM size to
Standard_B2sor larger. - Choose SSH public key as the authentication type and set the username to
azureuser. - Under Inbound port rules, allow SSH (22) and HTTP (80).
- Click Review + create, then Create.
Option B: Azure CLI
az vm image terms accept --urn sac1647283583153:whisper-cpp-server-ubuntu-24-04:default:latest
az vm create \
--resource-group my-resource-group \
--name my-whisper-vm \
--image sac1647283583153:whisper-cpp-server-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard \
--location eastusOpen the web port:
az vm open-port --resource-group my-resource-group --name my-whisper-vm --port 80Connect over SSH
Find the public IP address of your virtual machine:
az vm show --resource-group my-resource-group --name my-whisper-vm \
--show-details --query publicIps --output tsvConnect with SSH:
ssh azureuser@<vm-ip>Retrieve your credentials
On the first boot of every virtual machine, this image generates a password that is unique to that machine. Nothing is shared between deployments, and no password is baked into the image. Read the credentials file with:
sudo cat /root/whisper-cpp-server-credentials.txtOnly root can read the file. It contains your username, your password and the URL of your service:
WHISPER_USER=whisper WHISPER_PASSWORD=... WHISPER_URL=http://<vm-ip>/ WHISPER_API_URL=http://<vm-ip>/inferenceConfirm that the file exists with the correct ownership and permissions. The command below shows that the file is present and root-only, without printing the password itself:
sudo ls -l /root/whisper-cpp-server-credentials.txtConfirm the service is running
sudo systemctl status whisper-server.service --no-pagersudo systemctl status nginx.service --no-pagerBoth units should report active (running).
Check that the inference engine is answering on the loopback interface:
curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' http://127.0.0.1:8080/healthExpected output:
health: HTTP 200Transcribe the bundled test clip
The image ships a short test clip so you can prove the service works end to end before you use your own audio. It was synthesised on the build machine with eSpeak NG, so no third-party audio is bundled with this image.
Transcribe it, substituting the password from Step 3:
sudo curl -s -u whisper:<your-password> \
-F file=@/usr/local/share/whisper-cpp-server/samples/sac-test.wav \
-F response_format=json \
http://127.0.0.1/inferenceExpected output:
{"text":" A quick brown fox jumps over the lazy dog.\n"}If you get a transcript back containing those words, the model loaded correctly and the whole path works. An empty text value with an HTTP 200 would mean the model failed to load, so check the transcript content rather than the status code.
Use the browser interface
Open http://<vm-ip>/ in a browser. The browser prompts you for the username and password from Step 3.
After you sign in, the interface documents the API and provides an upload form. Choose an audio file, pick a response format and click Submit. The browser shows the transcript directly.
Note that the interface page documents a /load endpoint. That endpoint is deliberately blocked on this image and returns HTTP 403. See Security model for why.
Transcribe your own audio
Copy an audio file to the virtual machine:
scp <your-audio-file> azureuser@<vm-ip>:/tmp/Then transcribe it. ffmpeg is installed and the server converts input automatically, so it accepts MP3, M4A, FLAC, OGG, WAV and the audio track of most video files:
sudo curl -s -u whisper:<your-password> \
-F file=@/tmp/<your-audio-file> \
-F response_format=text \
http://127.0.0.1/inferenceResponse formats
Pass response_format to choose the output shape:
- text: plain transcript text
- json: {"text": "..."}
- verbose_json: transcript plus per-segment and per-word timings and probabilities
- srt: SubRip subtitles
- vtt: WebVTT subtitles
Generate subtitles for a recording:
sudo curl -s -u whisper:<your-password> \
-F file=@/usr/local/share/whisper-cpp-server/samples/sac-test.wav \
-F response_format=srt \
http://127.0.0.1/inferenceExpected output:
1 00:00:00,000 --> 00:00:03,600 A quick brown fox jumps over the lazy dog.Calling the API from another machine
Replace the loopback address with your virtual machine's public IP address and supply the same credentials:
curl -s -u whisper:<your-password> \
-F file=@recording.mp3 -F response_format=text \
http://<vm-ip>/inferencePerformance
whisper.cpp runs on the CPU, so transcription speed scales with the number of vCPUs. These figures were measured on this image running on Standard_B2s (2 vCPU) with the bundled ggml-base.en model:
- 3.5-second clip: approximately 2.9 seconds, dominated by fixed startup cost
- 90.7 seconds of speech: approximately 17.4 seconds, about 5 times faster than real time
In practice, on Standard_B2s, expect roughly 5 times faster than real time for sustained audio, so a 10-minute recording takes around 2 minutes. Short clips are dominated by a fixed per-request cost of a couple of seconds.
Use this guidance to choose a size:
Standard_B2sis a good fit for occasional transcription, evaluation and low-volume API use. B-series machines are burstable, so sustained batch work can exhaust CPU credits and slow down.- For sustained or batch transcription, choose a compute-optimised size such as
Standard_F4s_v2orStandard_F8s_v2. Throughput scales roughly with vCPU count. - Memory is not the constraint with this model. The
ggml-base.enmodel uses a few hundred megabytes, and 4 GB of RAM is sufficient.
Requests are processed one at a time. If you need concurrency, place a queue in front of the API or run several virtual machines behind a load balancer.
Using a different model
ggml-base.en balances accuracy and speed, and is English only. Larger models are more accurate and slower; multilingual models drop the .en suffix. To use a different one, download it and point the service at it:
sudo curl -fL -o /var/lib/whisper-cpp-server/models/ggml-small.en.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin
sudo chown whisper:whisper /var/lib/whisper-cpp-server/models/ggml-small.en.binThen edit the ExecStart line in /etc/systemd/system/whisper-server.service to reference the new file, and reload the service:
sudo systemctl daemon-reload
sudo systemctl restart whisper-server.serviceCheck the licence of any model you add. The models in the repository above are conversions of OpenAI's original Whisper checkpoints and are MIT licensed, but models elsewhere, including community fine-tunes, may carry more restrictive terms that prohibit commercial use.
Security model
whisper.cpp's bundled server has no authentication of any kind, and the upstream project's own documentation warns against exposing it. It accepts user file uploads and shells out to ffmpeg for conversion, and its /load endpoint allows a caller to load an arbitrary server-side file path as a model. Left open on a public IP address, that is an abuse and cost surface, and transcription endpoints handle audio that is often sensitive.
This image does not expose it. The protections below are applied in the image and verified on a freshly launched virtual machine before the image is published:
- The inference engine binds to the loopback interface only. It listens on
127.0.0.1:8080and is not reachable from the network, whatever your network security group allows. - Every path requires authentication. nginx fronts the engine on port 80 and demands HTTP Basic credentials. There is no anonymous route to any endpoint.
- The
/loadendpoint is blocked outright. It returns HTTP 403 even with valid credentials, so the remote model-swap primitive is unreachable rather than merely protected. - No default credential exists. The password is generated on each machine's first boot. Until that happens the credential store is empty, which fails closed: every request is refused.
- The service runs unprivileged as the
whispersystem user, under systemd hardening includingNoNewPrivileges,ProtectSystem=strictandPrivateTmp. - Audio and transcripts are not persisted. Uploads are handled per request, and nothing but the model file remains on disk afterwards.
You can confirm the first three points yourself. The engine must refuse a connection on the machine's own routable address:
curl -s -o /dev/null -m 5 -w '%{http_code}\n' http://$(hostname -I | awk '{print $1}'):8080/health || echo "connection refused, as expected"A status of 000 means curl could not connect at all, which is the desired result: nothing is listening on that address.
An anonymous request must be refused:
curl -s -o /dev/null -w 'anonymous: HTTP %{http_code}\n' http://127.0.0.1/inferenceA wrong password must be refused:
curl -s -o /dev/null -w 'wrong password: HTTP %{http_code}\n' -u whisper:definitely-not-the-password http://127.0.0.1/And the model-swap endpoint must be blocked even with the correct credentials:
sudo curl -s -o /dev/null -w 'load endpoint: HTTP %{http_code}\n' -u whisper:<your-password> http://127.0.0.1/loadExpected output from those four commands:
000 connection refused, as expected anonymous: HTTP 401 wrong password: HTTP 401 load endpoint: HTTP 403Recommended additional hardening
- Restrict port 80 by source address in your network security group so only your own networks can reach the service.
- Add TLS. Basic authentication sends credentials in a reversible encoding, so on any untrusted network you should terminate TLS in front of it. The nginx configuration at
/etc/nginx/sites-available/sac-whisper-cpp-serveris ready for a certificate. - Change the password if you wish, using
sudo htpasswd /etc/nginx/whisper-cpp-server.htpasswd whisper, and record the new value safely. nginx picks up the change immediately, with no reload. - Keep the engine on loopback. Do not change
--host 127.0.0.1in the service unit.
Verifying the bundled model
The model shipped with this image can be checked against the checksum recorded at build time:
sha256sum /var/lib/whisper-cpp-server/models/ggml-base.en.binExpected output:
a03779c86df3323075f5e796cb2ce5029f00ec8869eee3fdfb897afe36c6d002 /var/lib/whisper-cpp-server/models/ggml-base.en.binThe licence notices and the written provenance record are here:
ls -l /opt/whisper/cat /opt/whisper/MODEL-PROVENANCE.txtServer components
The image includes these components, listed with version and install path:
- whisper.cpp server: version 1.9.1, in /usr/local/bin/whisper-server
- Speech recognition model: ggml-base.en, in /var/lib/whisper-cpp-server/models/ggml-base.en.bin
- nginx: version 1.24, in /usr/sbin/nginx
- ffmpeg: version 6.1.1, in /usr/bin/ffmpeg
- eSpeak NG: version 1.51, in /usr/bin/espeak-ng
Filesystem layout
The virtual machine uses these mount points:
- / is the root filesystem.
- /boot holds the operating system kernel files.
- /boot/efi is the UEFI boot partition, Generation 2 Hyper-V.
- /mnt is the Azure temporary resource disk.
The key directories are:
- /var/lib/whisper-cpp-server/models/: speech recognition model files
- /usr/local/share/whisper-cpp-server/samples/: the bundled test clip
- /opt/whisper/: MIT licence notices and the model provenance record
- /etc/nginx/whisper-cpp-server.htpasswd: credential store for the reverse proxy
- /root/whisper-cpp-server-credentials.txt: per-machine credentials, root only
Managing the service
sudo systemctl status whisper-server.service --no-pagerRestart the inference engine:
sudo systemctl restart whisper-server.serviceView the engine log:
sudo journalctl -u whisper-server.service --no-pager -n 20Scripts and log files
- /usr/local/sbin/whisper-cpp-server-firstboot.sh: generates the per-machine credential on first boot
- /var/log/sac/sac-firstboot.log: first-boot log
- /var/lib/sac/data/whisper-cpp-server-firstboot.done: first-boot completion marker
On startup
On the first boot only, whisper-cpp-server-firstboot.service runs before the application and nginx start. It generates the password unique to this machine, writes it into the reverse-proxy credential store and into the root-only credentials file, resolves the machine's public address for the login banner, and then writes its completion marker. Subsequent boots skip it, so your password is never regenerated behind your back.
Read the first-boot log with:
sudo cat /var/log/sac/sac-firstboot.logTroubleshooting
Every request returns HTTP 401
Confirm that you are using the password from this machine. Passwords are per-machine and are not shared between deployments:
sudo grep -c WHISPER_PASSWORD /root/whisper-cpp-server-credentials.txtIf the credentials file is missing, first boot did not complete. Check the log:
sudo journalctl -u whisper-cpp-server-firstboot.service --no-pager -n 20A request returns HTTP 200 but the transcript is empty
An empty transcript with a healthy status usually means the model file is missing or corrupt. Verify it against its checksum as shown in Verifying the bundled model. Genuinely silent audio also returns an empty transcript, which is correct behaviour, so confirm your input actually contains speech.
The /load endpoint returns HTTP 403
This is intentional. The endpoint is blocked on this image because it allows an arbitrary server side file to be loaded as a model. To change the model, edit the service unit as described in Using a different model.
Transcription is slower than expected
If you use a B-series size, confirm that the machine has not run out of CPU credits, and consider a compute-optimised size. See Performance.
The browser will not prompt for credentials again
Browsers cache Basic authentication credentials for the session. Open a private window to be prompted again.
Security recommendations
- Restrict port 80 to trusted source addresses in your network security group.
- Terminate TLS in front of the service before you use it across any untrusted network.
- Keep the credentials file readable by root only, and do not copy it into shared storage.
- Leave the inference engine bound to the loopback interface.
- Apply operating system updates regularly. Unattended security upgrades are enabled in this image.
Support
For assistance, contact SecureAnyCloud support:
- Email: sales@secureanycloud.com
- Response time: 24/7 with a guaranteed 24-hour response SLA
- Website: https://secureanycloud.com
Deploy on Azure
Launch whisper.cpp Server with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
whisper.cpp is a high-performance implementation of OpenAI's Whisper automatic speech recognition model, written in plain C and C++. It runs entirely on the CPU, with no GPU, no Python runtime and no external service dependencies. This image ships the project's bundled HTTP server, so the instance becomes a private speech-to-text service the moment it boots: post an audio file and receive a transcript back.
This guide covers deploying and using whisper.cpp Server on Amazon Web Services with SecureAnyCloud's preconfigured Amazon Machine Image. Because everything runs locally, audio never leaves the instance you control. That suits recordings that cannot be sent to a third-party transcription service.
What you get:
- whisper.cpp 1.9.1, built from the pinned upstream source tag
- The bundled whisper.cpp HTTP server, listening on the loopback interface only
- The
ggml-base.enspeech recognition model, preinstalled and verified by checksum - nginx as an authenticating reverse proxy on port 80
- ffmpeg, for automatic conversion of common audio and video formats
- A short test clip, so you can prove the service works before you use your own audio
- 24/7 SecureAnyCloud support
Key facts:
- Platform: Amazon Web Services (EC2)
- Operating system: Ubuntu 24.04 LTS
- Default SSH user: ubuntu
- Web interface and API: port 80, HTTP Basic authentication
- Inference engine: 127.0.0.1:8080, loopback only, not reachable from the network
- Model file: /var/lib/whisper-cpp-server/models/ggml-base.en.bin
- Credentials file: /root/whisper-cpp-server-credentials.txt
- Licence notices: /opt/whisper/
About the bundled model
whisper.cpp is a program, not a model. It needs a separate model file to do anything, and that file carries its own licence. This image ships ggml-base.en, and its provenance was traced to its root rather than assumed from a repository badge.
This image includes the ggml-base.en speech recognition model, a format conversion of OpenAI's original Whisper base.en checkpoint into the GGML format used by whisper.cpp. It is not a fine-tune and not a distillation of any third-party model. Both OpenAI's Whisper weights and whisper.cpp itself are distributed under the MIT License. Copies of both licences ship in the image at /opt/whisper/, together with a written provenance record.
The model file is pinned by name and by SHA-256 checksum at build time, so the image cannot silently ship a different model.
Prerequisites
Before you deploy, make sure you have:
- An active AWS account
- A subscription to this product in AWS Marketplace
- An EC2 key pair for SSH access in your target region
- Permission to launch EC2 instances and manage security groups
Recommended instance type: m5.large (2 vCPU, 8 GB RAM). See Performance for what that type delivers and when to choose a larger one.
Connecting to your instance
Connect over SSH as the default login user for the operating system variant you launched:
- Ubuntu 24.04: ubuntu
Security group configuration
Allow these inbound ports:
- TCP 22: SSH access
- TCP 80: whisper.cpp web interface and transcription API, authenticated
Port 8080 is deliberately not in this list. The inference engine binds to the loopback interface only and is never reachable from the network. See Security model.
Launch the instance
Option A: AWS Marketplace console
- In AWS Marketplace, search for whisper.cpp Speech to Text Server and subscribe.
- Click Continue to Configuration, choose your region and the software version, then click Continue to Launch.
- Choose the instance type (
m5.largeor larger), your VPC and subnet, and your EC2 key pair. - Under the security group, allow SSH (22) and HTTP (80).
- Launch the instance.
Option B: AWS CLI
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name my-key \
--security-group-ids sg-xxxxxxxx \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=my-whisper}]'Replace <ami-id> with the AMI ID shown on the Marketplace launch page for your region, and use a security group that allows TCP 22 and 80.
Connect over SSH
Connect to the instance with SSH:
ssh ubuntu@<public-ip>Retrieve your credentials
On the first boot of every instance, this image generates a password that is unique to that instance. Nothing is shared between deployments, and no password is baked into the image. Read the credentials file with:
sudo cat /root/whisper-cpp-server-credentials.txtOnly root can read the file. It contains your username, your password and the URL of your service:
WHISPER_USER=whisper WHISPER_PASSWORD=... WHISPER_URL=http://<public-ip>/ WHISPER_API_URL=http://<public-ip>/inferenceConfirm that the file exists with the correct ownership and permissions, without printing the password itself:
sudo ls -l /root/whisper-cpp-server-credentials.txtConfirm the service is running
sudo systemctl status whisper-server.service --no-pagersudo systemctl status nginx.service --no-pagerBoth units should report active (running).
Check that the inference engine is answering on the loopback interface:
curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' http://127.0.0.1:8080/healthExpected output:
health: HTTP 200Transcribe the bundled test clip
The image ships a short test clip so you can prove the service works end to end before you use your own audio. It was synthesised on the build machine with eSpeak NG, so no third-party audio is bundled with this image.
Transcribe it, substituting the password from Step 3:
sudo curl -s -u "whisper:<your-password>" \
-F file=@/usr/local/share/whisper-cpp-server/samples/sac-test.wav \
-F response_format=json \
http://127.0.0.1/inferenceExpected output:
{"text":" A quick brown fox jumps over the lazy dog.\n"}If you get a transcript back containing those words, the model loaded correctly and the whole path works. An empty text value with an HTTP 200 would mean the model failed to load, so check the transcript content rather than the status code.
Use the browser interface
Open http://<public-ip>/ in a browser. The browser prompts you for the username and password from Step 3.
After you sign in, the interface documents the API and provides an upload form. Choose an audio file, pick a response format and click Submit. The browser shows the transcript directly.
Note that the interface page documents a /load endpoint. That endpoint is deliberately blocked on this image and returns HTTP 403. See Security model for why.
Transcribe your own audio
Copy an audio file to the instance, then transcribe it. ffmpeg is installed and the server converts input automatically, so it accepts MP3, M4A, FLAC, OGG, WAV and the audio track of most video files. Run these commands from your workstation, and substitute your own values for the angle-bracket items:
scp recording.mp3 ubuntu@<public-ip>:/tmp/ curl -s -u "whisper:<your-password>" \
-F file=@/tmp/recording.mp3 \
-F response_format=text \
http://<public-ip>/inferenceResponse formats
Pass response_format to choose the output shape:
- text: plain transcript text
- json: {"text": "..."}
- verbose_json: transcript plus per-segment and per-word timings and probabilities
- srt: SubRip subtitles
- vtt: WebVTT subtitles
Generate subtitles for a recording:
sudo curl -s -u "whisper:<your-password>" \
-F file=@/usr/local/share/whisper-cpp-server/samples/sac-test.wav \
-F response_format=srt \
http://127.0.0.1/inferenceExpected output:
1 00:00:00,000 --> 00:00:03,600 A quick brown fox jumps over the lazy dog.Performance
whisper.cpp runs on the CPU, so transcription speed scales with the number of vCPUs. With the bundled ggml-base.en model on m5.large (2 vCPU), a clip of a few seconds returns in roughly two to three seconds, dominated by a fixed per-request startup cost, and sustained speech transcribes several times faster than real time.
Use this guidance to choose a size:
m5.largeis a good fit for occasional transcription, evaluation and low-volume API use.- For sustained or batch transcription, choose a compute-optimised size such as
c5.2xlargeor larger. Throughput scales roughly with vCPU count. - Memory is not the constraint with this model. The
ggml-base.enmodel uses a few hundred megabytes, and 8 GB of RAM is ample.
Requests are processed one at a time. If you need concurrency, place a queue in front of the API or run several instances behind a load balancer.
Using a different model
ggml-base.en balances accuracy and speed, and is English only. Larger models are more accurate and slower; multilingual models drop the .en suffix. To use a different one, download it, point the service unit at it and restart. On the instance:
sudo curl -fL -o /var/lib/whisper-cpp-server/models/ggml-small.en.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin
sudo chown whisper:whisper /var/lib/whisper-cpp-server/models/ggml-small.en.bin
# edit ExecStart in /etc/systemd/system/whisper-server.service to reference the new file
sudo systemctl daemon-reload
sudo systemctl restart whisper-server.serviceCheck the licence of any model you add. The models in the repository above are conversions of OpenAI's original Whisper checkpoints and are MIT licensed, but models elsewhere, including community fine-tunes, may carry more restrictive terms that prohibit commercial use.
Security model
whisper.cpp's bundled server has no authentication of any kind, and the upstream project's own documentation warns against exposing it. It accepts user file uploads and shells out to ffmpeg for conversion, and its /load endpoint allows a caller to load an arbitrary server-side file path as a model. Left open on a public IP address, that is an abuse and cost surface, and transcription endpoints handle audio that is often sensitive.
This image does not expose it. The protections below are applied in the image and verified on a freshly launched instance before the image is published:
- The inference engine binds to the loopback interface only. It listens on
127.0.0.1:8080and is not reachable from the network, whatever your security group allows. - Every path requires authentication. nginx fronts the engine on port 80 and demands HTTP Basic credentials. There is no anonymous route to any endpoint.
- The
/loadendpoint is blocked outright. It returns HTTP 403 even with valid credentials, so the remote model-swap primitive is unreachable rather than merely protected. - No default credential exists. The password is generated on each instance's first boot. Until that happens the credential store is empty, which fails closed: every request is refused.
- The service runs unprivileged as the
whispersystem user, under systemd hardening includingNoNewPrivileges,ProtectSystem=strictandPrivateTmp. - Audio and transcripts are not persisted. Uploads are handled per request, and nothing but the model file remains on disk afterwards.
You can confirm the first three points yourself. The engine must refuse a connection on the instance's own routable address:
curl -s -o /dev/null -m 5 -w '%{http_code}\n' http://$(hostname -I | awk '{print $1}'):8080/health || echo "connection refused, as expected"An anonymous request must be refused:
curl -s -o /dev/null -w 'anonymous: HTTP %{http_code}\n' http://127.0.0.1/inferenceA wrong password must be refused:
curl -s -o /dev/null -w 'wrong password: HTTP %{http_code}\n' -u whisper:definitely-not-the-password http://127.0.0.1/And the model-swap endpoint must be blocked even with the correct credentials:
sudo curl -s -o /dev/null -w 'load endpoint: HTTP %{http_code}\n' -u "whisper:<your-password>" http://127.0.0.1/loadExpected output from those four commands:
connection refused, as expected anonymous: HTTP 401 wrong password: HTTP 401 load endpoint: HTTP 403Recommended additional hardening
- Restrict port 80 by source address in your security group so only your own networks can reach the service.
- Add TLS. Basic authentication sends credentials in a reversible encoding, so on any untrusted network you should terminate TLS in front of it. The nginx configuration at
/etc/nginx/sites-available/sac-whisper-cpp-serveris ready for a certificate. - Change the password if you wish, using
sudo htpasswd /etc/nginx/whisper-cpp-server.htpasswd whisper, and record the new value safely. nginx picks up the change immediately, with no reload. - Keep the engine on loopback. Do not change
--host 127.0.0.1in the service unit.
Verifying the bundled model
The model shipped with this image can be checked against the checksum recorded at build time:
sha256sum /var/lib/whisper-cpp-server/models/ggml-base.en.binExpected output:
a03779c86df3323075f5e796cb2ce5029f00ec8869eee3fdfb897afe36c6d002 /var/lib/whisper-cpp-server/models/ggml-base.en.binThe licence notices and the written provenance record are here:
ls -l /opt/whisper/cat /opt/whisper/MODEL-PROVENANCE.txtServer components
The image includes these components, listed with version and install path:
- whisper.cpp server: version 1.9.1, in /usr/local/bin/whisper-server
- Speech recognition model: ggml-base.en, in /var/lib/whisper-cpp-server/models/ggml-base.en.bin
- nginx: version 1.24, in /usr/sbin/nginx
- ffmpeg: version 6.1.1, in /usr/bin/ffmpeg
- eSpeak NG: version 1.51, in /usr/bin/espeak-ng
Filesystem layout
The key directories are:
- /var/lib/whisper-cpp-server/models/: speech recognition model files
- /usr/local/share/whisper-cpp-server/samples/: the bundled test clip
- /opt/whisper/: MIT licence notices and the model provenance record
- /etc/nginx/whisper-cpp-server.htpasswd: credential store for the reverse proxy
- /root/whisper-cpp-server-credentials.txt: per-instance credentials, root only
Managing the service
sudo systemctl status whisper-server.service --no-pagerRestart the inference engine:
sudo systemctl restart whisper-server.serviceView the engine log:
sudo journalctl -u whisper-server.service --no-pager -n 20On startup
On the first boot only, whisper-cpp-server-firstboot.service runs before the application and nginx start. It generates the password unique to this instance, writes it into the reverse proxy credential store and into the root-only credentials file, resolves the instance's public address (via EC2 IMDSv2) for the login banner, and then writes its completion marker. Subsequent boots skip it, so your password is never regenerated behind your back.
Read the first-boot log with:
sudo cat /var/log/sac/sac-firstboot.logTroubleshooting
Every request returns HTTP 401
Confirm that you are using the password from this instance. Passwords are per-instance and are not shared between deployments:
sudo grep -c WHISPER_PASSWORD /root/whisper-cpp-server-credentials.txtIf the credentials file is missing, first boot did not complete. Check the log:
sudo journalctl -u whisper-cpp-server-firstboot.service --no-pager -n 20A request returns HTTP 200 but the transcript is empty
An empty transcript with a healthy status usually means the model file is missing or corrupt. Verify it against its checksum as shown in Verifying the bundled model. Genuinely silent audio also returns an empty transcript, which is correct behaviour, so confirm your input actually contains speech.
The /load endpoint returns HTTP 403
This is intentional. The endpoint is blocked on this image because it allows an arbitrary server-side file to be loaded as a model.
Transcription is slower than expected
Consider a compute-optimised instance type. See Performance.
The browser will not prompt for credentials again
Browsers cache Basic authentication credentials for the session. Open a private window to be prompted again.
Support
For assistance, contact SecureAnyCloud support:
- Email: sales@secureanycloud.com
- Response time: 24/7 with a guaranteed 24-hour response SLA
- Website: https://secureanycloud.com
Deploy on AWS
Launch whisper.cpp Server on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com