Privacy coins rallied in the last days in light of the current geopolitical events. So is it the perfect time to finally start mining? And how do we do it? We already saw that mining Monero on Raspberry Pis makes no sense. And we surely don’t want to buy a whole server farm, do we?

So how about mining in the cloud? After all, Monero prices are rising, so it should be profitable, right?

Spoiler: No, it isn’t. But if you want to know how to set up Monero mining on Azure with Packer and Ansible and see the gathered data, keep on reading.

A simple mining setup on Azure

In mining Monero on Raspberry Pis we learned that we would need a lot of processing power to get rewarded with some XMR. Azure allows for starting a lot of VMs in parallel by using a scale set. We just have to provide an image that will start our miner. To build that image we will use the tool Packer. And as always, we will use Ansible to control the scale set.

I assume that you already have a working Azure dev setup and a virtual network where we can run the scale set. Otherwise, check out my post about deploying VMs on Azure and follow the basic steps including the virtual network and subnet.

How to set up the miner image with Packer

Packer makes it easy to create preconfigured images for different platforms like Azure, AWS or Docker. It works by starting a VM or container on the chosen platform, executing your scripts and saving the result as an image. To make this work for us, you have to set up you Azure dev environment including the credentials in environment variables.

Our script will create a minimal image that just installs and enables xmrig as a service. You can add other functionality, of course. I will split the scripts in four parts to be able to explain them better. You can concatenate them to make it work.

The first section takes over the azure credentials from our environment variables. The env function is relatively new, but finally we can get rid of the PKR_VAR prefixes for the environment variables.

variable "azure_client_id" {
default = env("AZURE_CLIENT_ID")

variable "azure_subscription_id" {
default = env("AZURE_SUBSCRIPTION_ID")

variable "azure_secret" {
default = env("AZURE_SECRET")

Then we tell Packer to load the Azure plugin which we will use later to build the image.

packer {
required_plugins {
azure = {
version = ">= 1.0.0"
source = ""

Now for the image: we set our Azure credentials, select a base image and the region we want to work on. The image has to end up in the region we want to set up our scale set so we can then reference it.

source "azure-arm" "xmrig" {
# Here we just reference the credentials from above
client_id = "${var.azure_client_id}"
subscription_id = "${var.azure_subscription_id}"
client_secret = "${var.azure_secret}"

# The image will end up in our resource group tnglab under the name xmrig
managed_image_resource_group_name = "tnglab"
managed_image_name = "xmrig"

# We will use Ubuntu 21.10 as base image
os_type = "Linux"
image_publisher = "Canonical"
image_offer = "0001-com-ubuntu-server-impish"
image_sku = "21_10-gen2"

# They have cheap VMs there
location = "North Central US"
# We don't need a huge VM to build the image
vm_size = "Standard_B2s"

Now we need to install xmrig. In Packer we can do install tasks using provisioners. We will use two kinds: the file provisioner to copy the systemd unit file to the image and the shell provisioner to install xmrig.

One could use a xmrig apt package, but there is none in the official repositories. So I chose to compile xmrig myself.

build {
name = "xmrig-build"
sources = [

provisioner "file" {
# You should adapt this path
source = "../../roles/xmrig/files/xmrig.service"
# We will have sufficient rights in the /tmp directory
destination = "/tmp/xmrig.service"

provisioner "shell" {
environment_vars = [
inline = [
# The Ubuntu cloud image does some magic on boot. Wait for that to finish
"cloud-init status --wait",
"sudo apt update",
"sudo apt install -y git build-essential cmake libuv1-dev libssl-dev libhwloc-dev",
# install xmrig
"git clone",
"mkdir xmrig/build && cd xmrig/build",
"cmake ..",
"make -j$(nproc)",
"sudo mkdir -p /usr/local/bin",
"sudo cp xmrig /usr/local/bin/xmrig",
"sudo chmod 755 /usr/local/bin/xmrig",
"sudo mkdir /etc/xmrig",
# We did copy the xmrig.service file to /tmp
"sudo mv /tmp/xmrig.service /etc/systemd/system/xmrig.service",
"sudo systemctl enable xmrig.service"

We start xmrig as a systemd service at boot. The file has two workarounds for problems that occured in my tests:

There, xmrig did not recognize the number of cores of the VM and only used one thread to mine. I don’t know if that’s a xmrig or Azure issue, but of course we want our VM to mine as fast as it can. So we start xmrig in another shell where we can evaluate nproc and pass the correct number of threads to xmrig.

Also, we don’t use a config file. Mixing settings from the config file with the threads parameter made xmrig ignore some settings in my tests. So I ditched the config file.

Description = xmrig Monero Mining Service

# Use your wallet address instead of XXXX
ExecStart = /bin/bash -c "/usr/local/bin/xmrig --threads $(nproc) --pass $(hostname) --user XXXX --tls --keepalive --url"


Start/Stop the Azure scale set

The scale set only needs one Ansible command to start and stop. Again, you need to set up your Azure dev setup and a vnet with subnet beforehand.

To be able to log in on the VMs and maybe do some debugging, we pass a ssh public key. It would be a good idea to use a different one than for the jumphost so you don’t compromise your jumphost key if you want to copy the scale set key to the jumphost.

If you want to use the jumphost, check my post on deploying VMs on Azure.

- name: Start Miners on Azure
hosts: localhost
gather_facts: no
connection: local
- name: Ensure VM scale set
name: tnglab-xmrigs
resource_group: tnglab
# Pick your chosen size
vm_size: Standard_D2as_v4
# How many VMs you want in the scale set
capacity: 1
virtual_network_name: tnglab-vnet
subnet_name: xmrig-subnet
upgrade_policy: Manual
admin_username: azureadmin
ssh_password_enabled: no
- path: /home/azureadmin/.ssh/authorized_keys
key_data: ssh-rsa AAAA... benjamin@tnglab
managed_disk_type: Standard_LRS
# Here we reference our image from above
name: xmrig
resource_group: tnglab

To stop the mining, we just delete the scale set:

- name: Stop Miners on Azure
hosts: localhost
connection: local
gather_facts: no
- name: Remove VM scale set
name: tnglab-xmrigs
resource_group: tnglab
state: absent


The following table shows the VM sizes I used for testing and what hashrate they generated. The hashrate is more like a ballpark figure as the VM performance was inconsistent during my tests.

VM Size Region CPU Cores RAM H/s Price/$ Spot-Price/$
F2s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 2 4 800 0,097 0,0148
F4s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 4 8 1600 0,194 0,0295
F8s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 8 16 2958 0,388 0,059
F16s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 16 32 6143 0,776 0,1179
F32s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 32 64 8442 1,552 0,2358
F48s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 48 96 9064 2,328 0,3537
F64s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 64 128 11367 3,104 0,4715
F72s v2 West Europe Intel(R) Xeon(R) Platinum 8272CL CPU 72 144 10630 3,492 0,5305
D2as v4 North Central US AMD EPYC 7452 32-Core Processor 2 8 903 0,096 0,013
D4as v4 North Central US AMD EPYC 7452 32-Core Processor 4 16 1818,3 0,192 0,0259
D8as v4 North Central US AMD EPYC 7452 32-Core Processor 8 16 3366,8 0,384 0,0517
D16as v4 North Central US AMD EPYC 7452 32-Core Processor 16 64 6747,6 0,768 0,1033
D32as v4 North Central US AMD EPYC 7452 32-Core Processor 32 128 9674 1,536 0,2065
D48as v4 North Central US AMD EPYC 7452 32-Core Processor 48 192 11855 2,304 0,3097
D64as v4 North Central US AMD EPYC 7452 32-Core Processor 64 256 13759 3,072 0,4129
Azure VM size, prices and Monero hashrate

Is Monero Mining on Azure profitable?

No. At least not at the current XMR price of $193 and the network hashrate of 2.7GH/s. The following table shows how much money you will currently loose when mining Monero with our setup.

The table includes normal earnings and spot price earnings, assuming that you would mine only at spot price.

VM Size Cost/Day Spot Cost/Day Earnings Net Earnings/Day Spot Net Earnings/Day
D2as v4 $2,30 $0,31 $0,03 -$2,27 -$0,28
F2s v2 $2,33 $0,36 $0,03 -$2,30 -$0,33
D4as v4 $4,61 $0,62 $0,06 -$4,54 -$0,56
F4s v2 $4,66 $0,71 $0,06 -$4,60 -$0,65
D8as v4 $9,22 $1,24 $0,12 -$9,10 -$1,12
F8s v2 $9,31 $1,42 $0,10 -$9,21 -$1,31
D16as v4 $18,43 $2,48 $0,23 -$18,20 -$2,24
F16s v2 $18,62 $2,83 $0,21 -$18,41 -$2,62
D32as v4 $36,86 $4,96 $0,34 -$36,53 -$4,62
F32s v2 $37,25 $5,66 $0,29 -$36,95 -$5,37
D48as v4 $55,30 $7,43 $0,41 -$54,88 -$7,02
F48s v2 $55,87 $8,49 $0,32 -$55,56 -$8,17
D64as v4 $73,73 $9,91 $0,48 -$73,25 -$9,43
F64s v2 $74,50 $11,32 $0,40 -$74,10 -$10,92
F72s v2 $83,81 $12,73 $0,37 -$83,44 -$12,36
Daily profit from mining Monero on Azure per VM

xmrig performance on Azure VMs

xmrig does not scale well on Azure VMs. From 8 cores on, the hashrate per core drops. I can’t tell wether that’s a xmrig or Azure issue. If I would continue to mine on Azure, I just would use smaller VM sizes.

The following chart shows the drop in hashrate of xmrig on Fs and Das series VMs on Azure.

Inconsistent VMs

But xmrig scalability is not the only issue. During my tests I repeatedly came across underperforming VMs. I did a small test with up to 20 VMs:

Azure limits the amount of cores you can use on VMs of a specific size. I did not get enough cores approved to make a proper statistic on the performance issue.

Also, VMs of the same size might have different CPUs in different regions. My Fs VMs had Xeon Platinum 8168 from the Skylake generation in North Central US and Xeon Platinum 8276CL of the Cascade Lake generation in West Europe.

In the end, I just used what I could get.

How about mining at spot prices?

Yes, this howto doesn’t include a section on how to mine at spot prices. But that’s probably a topic for another post.