The quickest way to deploy a network performance testing server in Azure…

… or how to quickly deploy a “ntttcp for Linux” server as a container in Azure without any previous infrastructure deployed.

Do you need to quickly test the throughput you can push from a specific location to one of Azure regions?

I have been in that situation more than once, and so far my technique has been always to deploy a VM in one of my Azure VNets, install whichever software I’d like to use test with (e.g. ntttcp), open the required ports on the NSGs and then finally test. This is time consuming and even if I deallocate the VM (so next time I just have to boot it up and test), I might have the need to test on another region!

I have now found a better solution which implies deploying an ACI running ntttcp for Linux on my region of choice. Here are the instructions for Azure CLI:

git clone
  • Login to your Azure account:
az login
  • [OPTIONAL] Create a Resource Group for your ACI in your chosen region (mine is West Europe):
az group create --name netperftest --location "West Europe"
  • Deploy the template on the above Resource Group:
az group deployment create --resource-group netperftest --template-file ntttcp-linux-aci/azuredeploy.json

Azure CLI will show you the result of the operation and the metadata for the container in JSON format. Look at the Output section to find out the assigned public IP address:

 "outputs": { 
      "containerIPv4Address": {
           "type": "String",
           "value": ""

Write that down as we will run the tests against that IP address.

Note: Due to a limitation in ACI where you can only declare 5 mapped ports, our container only supports up to 4 thread tests as ntttcp for Linux uses 1 port as control port in total and 1 port per thread as data port.

root@box:~/ntttcp-for-linux/src# ./ntttcp -s -m 4,*,
NTTTCP for Linux 1.3.0
08:39:00 INFO: Network activity progressing...
08:39:00 INFO: 16 threads created
08:40:00 INFO: ##### Totals: #####
08:40:00 INFO: test duration :60.00 seconds
08:40:00 INFO: total bytes :13463191552
08:40:00 INFO: throughput :1.80Gbps
08:40:00 INFO: cpu cores :1
08:40:00 INFO: cpu speed :1797.917MHz
08:40:00 INFO: user :13.41%
08:40:00 INFO: system :14.11%
08:40:00 INFO: idle :38.68%
08:40:00 INFO: iowait :0.00%
08:40:00 INFO: softirq :33.80%
08:40:00 INFO: cycles/byte :4.91

The above was tested from Digital Ocean in Frankfurt to Azure West Europe.

Once we’re done with our tests it would be a good idea to delete the container and save some money:

az container delete -n ntttcp-linux -g netperftest

Behind the magic

The above feels like magic to me, but nothing further from reality 🙂 this is just how simple things have become in the container era. If we look into the template we will how easy is this to replicate for other tools.

Container Image

 "image": { 
     "type": "string",
     "metadata": {
         "description": "Container image to deploy. Should be of the form accountName/imagename:tag for images stored in Docker Hub or a fully qualified URI for a private registry like the Azure Container Registry."
     "defaultValue": "pedroperezmsft/ntttcp-linux"

The above pulls pedroperezmsft/ntttcp-linux from Docker Hub:

You can find the Dockerfile for the above container in my repo:

Note: Running pedroperezmsft/ntttcp-linux without parameters starts the ntttcp server.


 "controlport": {
     "type": "string",
     "metadata": {
         "description": "Control port."
     "defaultValue": "5000"
 "dataport1": {
     "type": "string",
     "metadata": {
         "description": "Data port.."
     "defaultValue": "5001"
 "dataport2": {
     "type": "string",
     "metadata": {
         "description": "Data port.."
     "defaultValue": "5002"
 "dataport3": {
     "type": "string",
     "metadata": {
         "description": "Data port.."
     "defaultValue": "5003"
 "dataport4": {
     "type": "string",
     "metadata": {
         "description": "Data port.."
     "defaultValue": "5004"

This part of the template defines which ports should be mapped to the assigned public IP address. Both external and internal port will be the same number. In our case we have chosen 5000/TCP as the control port and the range 5001-5004 as the data ports.

CPU and RAM resources

 "cpuCores": {
     "type": "string",
     "metadata": {
         "description": "The number of CPU cores to allocate to the container. Must be an integer."
     "defaultValue": "2.0"
 "memoryInGb": {
     "type": "string",
     "metadata": {
         "description": "The amount of memory to allocate to the container in gigabytes."
     "defaultValue": "1.5"

In this section we define the number of CPU cores and the amount of RAM in GBytes. In most cases 1.5GB is enough, but the CPU cores number is important for multi-threaded tests (which is how you want to test anyway).

The default value of 2 might not be enough for your testing, so feel free to push that number higher. As mentioned before, we are limited on the number of data ports we can have, so you won’t be able to run tests with more than 4 connections.

This is how you modify any of the parameters during deployment time to suit your needs:

az group deployment create --resource-group netperftest --template-file ntttcp-linux-aci/azuredeploy.json --parameters cpuCores=4

The –parameters modifier helps you change values for any of the parameters in the templace. In the example we change cpuCores to 4 (instead of the default 2). I have deployed my container again, this time with 4 cores and unfortunately I have a bottleneck somewhere else, as the performance didn’t increase (quite the opposite!):

root@box~/ntttcp-for-linux/src# ./ntttcp -s -m 4,*,
NTTTCP for Linux 1.3.0
09:20:10 INFO: Network activity progressing...
09:20:10 INFO: 16 threads created
09:21:10 INFO: ##### Totals: #####
09:21:10 INFO: test duration :60.00 seconds
09:21:10 INFO: total bytes :12108169216
09:21:10 INFO: throughput :1.61Gbps
09:21:10 INFO: cpu cores :1
09:21:10 INFO: cpu speed :1797.917MHz
09:21:10 INFO: user :12.02%
09:21:10 INFO: system :14.10%
09:21:10 INFO: idle :28.71%
09:21:10 INFO: iowait :0.00%
09:21:10 INFO: softirq :45.17%
09:21:10 INFO: cycles/byte :6.35

Please note that the reported CPU cores and speed are from the client side. Other tools like qperf would be able to show server-side resource data, too.

As an exercise for the reader, you might want to modify the Azure template (and maybe something else?) to deploy a qperf server instead. I have an already built qperf image here:

Why are we using a template?

The main reasons to use a template instead of native AzCLI commands are:

  1. It’s way faster to get a default container ready to use.
  2. Less error-prone (e.g. not mapping ports)

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.