Running faasd on Azure Arm-based Virtual Machines

Running faasd on Azure Arm-based Virtual Machines

Using Pulumi YAML and Caddy

TL;DR: Code

As usual, here you go


Since April 2022, Azure offers virtual machines with Ampere Altra Arm-based processors. To try them out, you need to request access to the preview by filling out this form.

I applied, and got accepted for the preview. Since then, I played with different use-cases to discover the potential from Arm-based virtual machines.

And instantly the ecosystem around OpenFaaS come into my mind. Because with faasd, the little brother of OpenFaaS, we have an option to run OpenFaaS just like on Kubernetes. It's the same API, same UI, CLI and ecosystem, but without the complexity and stress of running Kubernetes.

You can use faasd to deploy containers, which conform to the OpenFaaS serverless workload definition. Or you can:

  • Deploy a microservice
  • Get a HTTP API for any binary or CLI through the use of the Classic Watchdog
  • Deploy a function written in any language from our templates or the template store

Question would be: What sort of use-cases might work well for faasd? faasd works well for the same kinds of use-cases as OpenFaaS on Kubernetes, but is much simpler to manage.

Some ideas out of my head:

  • Static sites
  • An API
  • A single-page app
  • A bot
  • Batch jobs, overnight processing via cron

So let's roll with our demo:


To provision our infrastructure, we're going to use Pulumi. This time too keep thins really simple, we use the new Pulumi YAML language support.

Pulumi YAML is a great option for smaller scale cloud infrastructure.

Like all other Pulumi languages, Pulumi YAML programs have access to all the core features of Pulumi’s infrastructure-as-code tooling, including native providers, secrets management, stack references, Pulumi Packages, and all the features of the Pulumi Service. Critically, Pulumi YAML programs can interoperate seamlessly with the rest of the Pulumi ecosystem, consuming the outputs of other Pulumi programs and using Pulumi components built in existing Pulumi languages.

For more details, I highly recommend the official documentation

So with the command:

pulumi new azure-yaml (--force)

I created my Pulumi program and stack for the Azure provider.

To connect to my virtual machine, I need to generate a SSH keypair. I do this via:

ssh-keygen -t rsa -f faasd

And then I can start to create my variables block in my yaml file:

  sshPublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDIBq1WoJOr81nYCdrbkmcGtdVtkshDU36IKpNMg3MBi4dk408ITluFCYykZcVCqbJWCRvwr9iOjKajtMJpErHevUdpUD/ViOyW68KgwZrjVQLfp6VpAGzbdyFcBzM1jqOjSBhdPRCJfA5jKZJPncVWDsL/c0IarI1+lYds3Mf5OARd+46evm4aPOPcSHRnIDm4ylY2Wo/Lsd+EHCt9Ya7XpB3u15uaagnI/5VM5Oy4vSoDl6tU8cONrT+ofEdCojVR79SJFDBr+GdM5dQxgz4CngQLrX+QcTAAlyvvlthCwLIH44+/orbvAgyA0Q0Jcw56sWI1M59F2adKhiJNCwx++u1GGfVKGvrFH7CjiPVTFUSAmUF+GdCwzoy9GpWBP/eXiKudi5OcbVA4Ze4Isy8gAwUAINrjbK52HPh54Euk1JvxkTYUx2zBKaw3YlSulCu7xsRpVULneiOjUWR/Sp4CQK30RtFtWA0drUlO/OtRm23rvxfsVb3Qhcw604bztBM= dirien@SIT-SMBP1766"
  adminUsername: ubuntu

    Fn::ToBase64: |

The variable called sshPublicKey is the content of my file. The feature to read directly from a file is already merged. And hopefully, with the next release of the yaml provider, so we don't need to copy and paste the content of the public key anymore.

Next big block is my cloud-config. But what is cloud-init? Cloud-init is a service used for customizing Linux-based operating systems in the cloud. It allows you to customize virtual machines provided by a cloud vendor on boot.

The service is used as an industry standard for early-stage initialization of a VM once it has been provisioned.

For more details, please head over to the official documentation

We need to use the Fn::ToBase64 function of Pulumi YAML, as Azure is expecting the user-data of a virtual machine base64 encoded.

The actual installation of faasd, is seperated into four separate pieces:

  • Install containerd.
  • Install CNI.
  • Install faasd
  • Install Caddy.

The only thing we need to keep in mind is, that we need to install the Arm version of all tools.

The Caddy install is also straight forward, the only part we need to keep in mind is during the creation of the Caddyfile

} {

I use the staging endpoint fo Let's Encrypt, remove this whole block if you want to use the production endpoint. For the reverse proxy part, please use your Domain.

Another detail, I would like to explain, is the usage of Spot instance here.

priority: Spot
evictionPolicy: Deallocate

Setting the priority to Spot enables us another cost saving. With the evictionPolicy set to Deallocate we are not going to lose our disk. The rest should be self explaining from the code.

As usual with Pulumi, we deploy the whole Stack with just calling:

pulumi up

Check the cli output, and wait for the deployment to be finished.


View Live:
     Type                                          Name                  Status      
 +   pulumi:pulumi:Stack                           pulumi-faasd-arm-dev  created     
 +   ├─ azure-native:resources:ResourceGroup       faasdRg               created     
 +   ├─ azure-native:network:PublicIPAddress       faasdPublicIP         created     
 +   ├─ azure-native:network:NetworkSecurityGroup  faasdSG               created     
 +   ├─ azure-native:network:VirtualNetwork        faasdVnet             created     
 +   ├─ azure-native:network:NetworkInterface      faasdNic              created     
 +   └─ azure-native:compute:VirtualMachine        faasd                 created     
 +   └─ azure-native:compute:VirtualMachine        faasd                 creating     
    faasdIP: "<ip>"

    + 7 created

Duration: 1m9s

After the virtual machine is created in Azure, I could take some minutes until everything is up and running. To get the admin password, you can use following command:

export PASSWORD=$(ssh -i faasd ubuntu@ "sudo cat /var/lib/faasd/secrets/basic-auth-password")


Now we are ready to use faasd, you can use either the cli or the UI to deploy your functions. In this article, I just use the UI. As mentioned before, I am more interested in the deployment part, for testing the Arm-based virtual machines from Azure.

But if you want, here is a quick deployment of figlet via the cli:

echo $PASSWORD | faas-cli login --password-stdin --tls-no-verify

faas-cli store deploy figlet --env write_timeout=1s --tls-no-verify

time curl -k $OPENFAAS_URL/function/figlet -d "Azure Arm Rocks"
    _                             _                   
   / \    _____   _ _ __ ___     / \   _ __ _ __ ___  
  / _ \  |_  / | | | '__/ _ \   / _ \ | '__| '_ ` _ \ 
 / ___ \  / /| |_| | | |  __/  / ___ \| |  | | | | | |
/_/   \_\/___|\__,_|_|  \___| /_/   \_\_|  |_| |_| |_|

 ____            _        
|  _ \ ___   ___| | _____ 
| |_) / _ \ / __| |/ / __|
|  _ < (_) | (__|   <\__ \
|_| \_\___/ \___|_|\_\___/
curl -k $OPENFAAS_URL/function/figlet -d "Azure Arm Rocks"  0.02s user 0.01s system 0% cpu 2.499 total

faasd have built-in metrics that will show us the replica count and invocations:

faas-cli list --tls-no-verify
Function                        Invocations     Replicas
figlet                          3               1

Let's scale our replicas to zero with the help of the API:

curl -k https://admin:$ -d '{"serviceName":"figlet", "replicas": 0}'

faas-cli list --tls-no-verify
Function                        Invocations     Replicas
figlet                          3               0

Now I am curious, how long the cold start will take us:

time curl -k $OPENFAAS_URL/function/figlet -d "Azure Arm rocks"
    _                             _                   
   / \    _____   _ _ __ ___     / \   _ __ _ __ ___  
  / _ \  |_  / | | | '__/ _ \   / _ \ | '__| '_ ` _ \ 
 / ___ \  / /| |_| | | |  __/  / ___ \| |  | | | | | |
/_/   \_\/___|\__,_|_|  \___| /_/   \_\_|  |_| |_| |_|

 _ __ ___   ___| | _____ 
| '__/ _ \ / __| |/ / __|
| | | (_) | (__|   <\__ \
|_|  \___/ \___|_|\_\___/

curl -k $OPENFAAS_URL/function/figlet -d "Azure Arm rocks"  0.02s user 0.01s system 1% cpu 2.416 total

That is a very good time for a "cold" start.

Now let us use the UI, to deploy the cows function. Open the page UI using your $OPENFAAS_URL/ui/. For the basic auth challenge, use the username admin and password from the $PASSWORD env variable, we just used before.


Click on Deploy new Function and select ASCII Cows


You can left everything with the default values:


Click on the invoke button, to see a the ascii art of a cow.


Or use curl

❯ curl -k $OPENFAAS_URL/function/cows
        o oo o
 / |     ||
*  ||----||
   ^^    ^^
  Injun Cow

❯ curl -k $OPENFAAS_URL/function/cows
       /   @@      ______
      |  /\_|     |      \
      |  |___     |       |
      |   ---@    |_______|
      |  |   ----   |    |
      |  |_____
CompuCow After an All-niter


Type pulumi destroy to clean up all the cloud resources, you just created.

Always clean up your unused cloud resources: Avoid cloud waste and save money!

Wrap Up

Azure Arm-based Virtual Machines are really awesome, they are fairly quick to create and offer a whole new bunch of use-cases, you could before not do.

And the new VM series include general-purpose Dpsv5 and memory-optimized Epsv5 VMs, which can deliver up to 50 percent better price-performance than comparable x86-based VMs.

I will keep exploring Arm-based virtual machines for all my projects, when using Azure and can't wait if this is GA and everybody can use it.