Kick the tires with faasd today, for a lightweight serverless experience that doesn't require Kubernetes. Instead it uses containerd and the existing OpenFaaS ecosystem.
You can run faasd anywhere, even on a Raspberry Pi, but why would you want to do that? faasd offers many of the benefits of containers and OpenFaaS, but without the complexity and operational costs of Kubernetes. containerd is a low-level tool for automating containers, and a CNCF project.
The use-cases for Serverless / FaaS are fairly well-known, but you could use faasd at the edge, in a smart-car, as part of an IoT device, for crunching data before uploading samples to the cloud, for webhook receivers/alerting, bots, webservices, API integrations or even providing your own APIs. Compute is compute, and OpenFaaS with containerd makes it easy to both consume and provide.
New: faasd has become part of the OpenFaaS GitHub organisation! You can even deploy faasd to your favourite cloud in a few minutes.
Before we start
Use Raspbian or Raspbian Lite for this tutorial. Do not install Docker on the same Raspberry Pi as it may cause conflicts with networking and the version of containerd we are using.
You should use RPi 2, 3 or 4. If you have only one Raspberry Pi, then you'll be able to deploy pre-built images from the OpenFaaS Function Store, but if you have a second Raspberry Pi, then you can use it to build new functions too.
Installation only takes a few moments, and in the future should be quicker if we can secure binaries for containerd from the upstream project.
The faas-cli can be used to build, deploy, manage, and invoke functions with OpenFaaS. OpenFaaS offers a REST API, asynchronous invocations, authentication, metrics, and a UI out of the box.
curl -sLfS https://cli.openfaas.com | sudo sh
See also: OpenFaaS docs
Unfortunately the containerd maintainers only provide binaries for
x86_64 (regular PCs), so we have to install Go and build containerd from source.
We'll install some dependencies that will help us in the tutorial and after installation
sudo apt update \ && sudo apt install -qy \ runc \ bridge-utils \ tmux \ git
Pick option A or B.
A) Use my containerd armhf binaries (fast)
I have compiled binaries for containerd, which you can use if you wish. alexellis/containerd/armhf.
curl -sSL https://github.com/alexellis/containerd-armhf/releases/download/v1.3.2/containerd.tgz | sudo tar -xvz --strip-components=2 -C /usr/local/bin/
B) Build your own containerd binaries (slow)
Raspbian needs a few additional build-time dependencies for containerd:
sudo apt update \ && sudo apt install -qy \ build-essential \ libbtrfs-dev \ libseccomp-dev
You can use my utility script to build containerd now:
export GOPATH=$HOME/go/ cd $GOPATH/src/github.com/openfaas/faasd ./hack/build-containerd-armhf.sh
This could take a few moments on RPi2 or 3, and is a little quicker on RPi4.
Configure containerd to start on boot
Install a systemd unit file for
curl -SLfs https://raw.githubusercontent.com/containerd/containerd/v1.3.2/containerd.service | sudo tee /etc/systemd/system/containerd.service sudo systemctl enable containerd sudo systemctl start containerd
Enable the Kernel's bridge module
# One-off sudo modprobe br_netfilter sysctl net.bridge.bridge-nf-call-iptables=1 # Make it permanent echo "br_netfilter" | sudo tee -a /etc/modules-load.d/modules.conf echo "net.bridge.bridge-nf-call-iptables=1" | sudo tee -a /etc/sysctl.conf
Install CNI plugins
sudo mkdir -p /opt/cni/bin curl -sSL https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-arm-v0.8.5.tgz | sudo tar -xz -C /opt/cni/bin
Enable NAT between the container network and your wider network and the Internet:
sudo /sbin/sysctl -w net.ipv4.conf.all.forwarding=1 echo "net.ipv4.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
Optional step - if you have Docker running on your system, then disable it and reboot at this point.
sudo systemctl disable docker sudo systemctl disable docker.sock # or docker.socket - You may also need this step
Docker's IP address range conflicts with the one chosen by netns, this will be resolved when moving to CNI.
faasd is a Go binary that runs on your system and packages all of OpenFaaS, it's a similar tool to docker-compose.
One of faasd's commands
faasd provider runs an OpenFaaS provider which implements the OpenFaaS "CRUD" API for functions.
Clean-up old versions, if required:
sudo systemctl disable faas-containerd sudo systemctl stop faas-containerd # Stop systemd services, if you have them sudo systemctl stop faasd sudo systemctl stop faasd-provider # Remove old binaries if you have them sudo rm -rf /usr/local/bin/faasd sudo rm -rf /usr/local/bin/faas-containerd
Download the faasd binary:
sudo curl -sSLf "https://github.com/openfaas/faasd/releases/download/0.7.5/faasd-armhf" \ --output "/usr/local/bin/faasd" \ && sudo chmod a+x "/usr/local/bin/faasd"
Now run the installation:
export GOPATH=$HOME/go/ mkdir -p $GOPATH/src/github.com/openfaas cd $GOPATH/src/github.com/openfaas git clone https://github.com/openfaas/faasd cd faasd sudo faasd install
faasd install command creates two systemd unit files which can be used to start/stop
Check the installation
Check the services:
sudo systemctl status faasd sudo systemctl status faasd-provider
sudo journalctl -u faasd --lines 40 sudo journalctl -u faasd-provider --lines 40
You'll find your login details for the gateway in
/var/lib/faasd/secrets, the user is
admin and the password is in
Access OpenFaaS from your laptop
faasd deploys OpenFaaS along with its core logic
faasd-provider, which uses containerd instead of Kubernetes to start, stop, and manage functions.
faasd process will proxy any incoming requests on HTTP port 8080 to the OpenFaaS gateway container, you can also find the IP of the container in your
hosts file in the
Deploy a function from the store
Log in to the gateway, either on your RPi, or from your own computer.
# If connecting remotely export OPENFAAS_URL=http://RPI:8080 sudo cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login --password-stdin
Look at what's available for armhf, then deploy something:
faas-cli store list --platform armhf # Deploy figlet faas-cli store deploy --platform armhf figlet # Find the URLs for the function faas-cli store inspect figlet # Create some ASCII echo "faasd" | faas-cli invoke figlet
Try a function that can generate an
identicon like you may have seen for default avatar logos on GitHub.com
faas-cli store deploy --platform armhf identicon echo $USER | faas-cli invoke identicon > avatar.png
Here's an example of the avatar for the user
Run an async function
You can also run your functions in the background or asynchronously.
Create a webhook receiver using a public service such as:
faas-cli store deploy --platform armhf nodeinfo curl -d "verbose" \ http://127.0.0.1:8080/async-function/nodeinfo \ --header "X-Callback-Url: https://postb.in/1578562711776-8760500776115"
You'll be able to refresh your webhook receiver and see the value.
Build a new container image
You'll need a second Raspberry Pi for this step. You can also use one of my prebuilt functions such as ascii-2020, but you should only run the
faas-cli deploy step.
Log into the Raspberry Pi with
sshand install the
curl -sLfS https://cli.openfaas.com | sudo sh
curl -sLfS https://get.docker.com | sudo sh sudo usermod -aG docker pi newgrp
Log into the Docker Hub
Create a Golang function
export DOCKER_HUB_NAME="alexellis2" faas-cli store pull golang-middleware-armhf faas-cli new --lang golang-middleware-armhf \ hello-world \ --prefix $DOCKER_HUB_NAME
Now build and push the image
faas-cli push -f hello-world.yml faas-cli deploy -f hello-world.yml
Deploy your new container
You can set your OpenFaaS gateway to point at the RPi where faasd is running.
export OPENFAAS_URL=http://$RPI_IP:8080 faas-cli deploy
Your new function will be pulled down on the first Raspberry Pi and start executing. Find the IP address / URL for invoking the function, both synchronous and asynchronous invocations are working:
faas-cli describe -f hello-world.yml hello-world
Now, try editing your function's source-code in
hello-world/handler.go - get it to print a different message perhaps? The template we have chosen runs validation with
gofmt, so if you've edited on the RPi, you may want to format the text before doing a build.
Your workflow is just
faas-cli up, or the separate steps of
faas-cli push and
Using private Docker registries
Private registries are supported such as the Docker Hub, AWS ECR, GitLab, or your own private hosted registry.
Note: If you want to setup your own registry, follow my 5 minute tutorial with k3sup.
To use a private container image registry just run
docker login on your PC or laptop then copy the
$HOME/.docker/config.json file over to where you are running faasd. Make sure that the values inside the file are encoded with base64, instead of using the Docker for Mac/Windows credential helper and you'll be good to go.
For faasd to detect your credentials, place the file at:
Get a public IP and TLS for faasd
Inlets can help us to get a public IP and ingress to our functions, this is done through a tunnel to a host on a cloud IaaS or VPS provider such as DigitalOcean.
Download inlets - an OSS reverse proxy and service tunnel.
curl -sSLf https://inletsctl.inlets.dev | sudo sh # Download the inlets client inletsctl download
You'll need an access token for a cloud for one of the supported providers, see the list on:
inletsctl create --help
I picked DigitalOcean and ran the
create command to provision the exit server, this is the public-facing host with a public IP.
Note: the host we create will cost around 5 USD / mo, for as long as you keep it running, you can run
inletsctl deleteat any point, or remove the VM from your dashboard. You can run an exit-node for free with some cloud providers who offer a free tier, or credits.
inletsctl create \ --provider digitalocean \ --access-token-file ~/do-token
After a few seconds the host will start up running the
inlets server, now you can go ahead and run the
inlets client on your RPi. I like to run commands with
screen so that I can log out and they stay running. You can also make the
inlets client permanent with a systemd unit file, or a Cron entry such as
This is the command printed out by
inletsctl create, just edit the UPSTREAM so that it points to faasd on
127.0.0.1:8080, then hit enter.
export UPSTREAM=http://127.0.0.1:8080 inlets client --remote "ws://188.8.131.52:8080" \ --token "525ed828fdfe11897b26b8a0d5359ef6c7ddd4ad" \ --upstream $UPSTREAM
Your functions can now be accessed via the public Internet, feel free to share the URL with your friends.
Basic authentication means that the UI and API of OpenFaaS are protected, but by default functions can be accessed.
In my example I have a function running called
ascii-2020 from this repo.
From anywhere in the world users can invoke the function:
I wrote a tutorial you can follow for Caddy called HTTPS for your local endpoints with inlets and Caddy, here's what it looks like in action with a TLS certificate being served from the exit-node for my domain
Today I prefer to use inlets-pro which means that I can run Caddy directly on the RPi and obtain a LetsEncrypt certificate on the device itself.
inlets-pro also comes with link-level encrypted over the wire and the ability to proxy any L4 TCP traffic from behind NAT. Sign up for a free, 14 day trial in the inlets-pro repo.
Notes on rebooting/restarting faasd
Now try rebooting your RPi, you should see that the gateway comes up again afterwards. The functions that you have deployed will still be present, but scaled to zero, so the first invocation will warm them back up again, this process can take around 0.38 seconds.
If you ever need to remove a function manually, you can do it like this:
export FN="figlet" sudo ctr --namespace openfaas-fn task delete -f $FN sudo ctr --namespace openfaas-fn container delete $FN sudo ctr --namespace openfaas-fn snapshot remove $FN-snapshot
faasd brings a lightweight experience to your Raspberry Pi, cloud infrastructure and to bare metal, all without the need for Kubernetes. The solution is still under active development, so you may not want to put it out in production just yet, but we're moving fast and already have async invocations, authentication, and function store support.
This RPi3 has 1GB RAM and 4 cores. Right now it's running @openfaas @nats_io @PrometheusIO @inletsdev @containerd but there's no Kubernetes in sight. That means we have room left for some functions 🤓 https://t.co/WW5TZVdAeU pic.twitter.com/JYeV1nSToK— Alex Ellis (@alexellisuk) January 8, 2020
You may be wondering what to run on your new RPi with faasd. Any OpenFaaS code should be able to run, whether that's an API, a function, a microservice or a simple webpage. Here's a few ideas off the top of my head:
- A Slackbot that can receive webhooks from Slack and respond to users
- A webhook receiver for GitHub to log or track statistics
- A GitHub bot like Derek
- Automation of home servers and IoT devices - use a function to send a call to your smart home?
- Host a website or a blog using the node12 template or React
- Download your favourite videos with the youtubedl function from the store
- Set up cron jobs to execute functions on a regular basis
Try faasd on your favourite cloud, for work
You can use faasd for client projects and for work. I've provided a way for you to automate the whole thing using a single copy/paste and your cloud provider's dashboard.
faasd doesn't need complicated server management or cluster configuration, you can even integrate a CI/CD pipeline using GitLab or GitHub Actions and within in a few minutes you'll have TLS endpoints you can update by pushing code a git repository.
What can you do to help?
Contribute on GitHub to openfaas/faasd - you'll need a basic understanding of Golang and containers
Read the OpenFaaS docs
Read My introduction to faas-containerd - the OpenFaaS provider component installed with faasd
For questions, comments, and suggestions ping me on Twitter @alexellisuk
Thank you to those who helped test and review this blog post and faasd.