# Deploy edge functions with the edge-functions template

Source: https://docs.quake.ai/resources/deployments/deploy-edge-functions-template
Markdown: https://docs.quake.ai/resources/deployments/deploy-edge-functions-template.md

---

# Deploy edge functions with the edge-functions template

Stand up an [OpenFaaS faasd](https://github.com/openfaas/faasd) function gateway on one Quake AI instance using the [validated OpenTofu template](/docs/platform/validation#how-infrastructure-templates-are-checked) `edge-functions`. You apply the template, read the generated gateway credentials, invoke the starter function over the floating IP, deploy an additional function from the OpenFaaS store, add a secret and consume it from a function, schedule a periodic invocation with cron on the gateway host, and optionally point a domain for automatic TLS.

You operate the gateway yourself on a regional VM. Functions execute in that region; this is not a global edge network like Cloudflare Workers or Vercel Edge Functions.

<Figure size="md" caption="What you'll build: faasd on a gateway instance with a floating IP runs containerized functions behind Caddy on ports 80 and 443">

```d2
direction: right

client: API client {shape: person}
fip: Floating IP\n80 / 443
gw: Gateway VM\nCaddy + faasd {
  runtime: containerd\nfunctions
}

client -> fip: HTTPS + basic auth
fip -> gw.runtime
```

</Figure>

<PricingCompanion
  components={[
    { kind: "template", slug: "edge-functions", required: true },
  ]}
/>

## Prerequisites

You need:

- OpenTofu 1.6.0 or later (or Terraform 1.6.0 or later) installed locally.
- Your OpenStack credentials sourced into the shell (`source openrc.sh`). See [the OpenStack CLI guide](/docs/tools/openstack-cli).
- An SSH keypair that already exists in your project. Record its name for the `key_name` variable.
- A copy of the `edge-functions` template directory from [the template reference page](/resources/iac-templates/edge-functions).
- `faas-cli` installed on your workstation, or willingness to run `faas-cli` commands over SSH on the gateway host (the faasd install script places `faas-cli` on the instance). See [OpenFaaS CLI installation](https://docs.openfaas.com/cli/install/).
- A domain you can point at the gateway floating IP when you enable TLS (optional for the HTTP verification steps below).

## Step 1: Apply the functions template

Copy the template's example variables file and set `key_name`. Leave `domain` empty so Caddy serves plain HTTP on port 80 while you verify functions:

```bash
cp terraform.tfvars.example terraform.tfvars
```

```hcl
key_name = "YOUR_KEY_NAME"
```

Initialize, preview, and apply:

```bash
tofu init
tofu plan
tofu apply
```

OpenTofu provisions a private network, router, security group, data volume, gateway instance, and floating IP. cloud-init installs faasd via the upstream install script, generates a gateway password on first boot, deploys the `nodeinfo` starter function from the OpenFaaS store, and starts Caddy as the public front door.

Record the outputs:

```bash
tofu output floating_ip
tofu output private_ip
tofu output gateway_url
```

Wait five to eight minutes for cloud-init, faasd, and the starter function deploy to finish before you test the gateway.

## Step 2: Read the gateway credentials

faasd does not ship credentials in the repository. SSH to the gateway instance and read the credentials file cloud-init wrote on first boot:

```bash
ssh ubuntu@YOUR_FLOATING_IP 'sudo cat /root/faasd-gateway-credentials'
```

Record the `username`, `password`, and `starter_function` URL. The password is also stored at `/var/lib/faasd/secrets/basic-auth-password` on the instance.

## Step 3: Invoke the starter function over the floating IP

From your workstation, call the starter function with basic auth:

```bash
curl -s -u admin:YOUR_PASSWORD http://YOUR_FLOATING_IP/function/nodeinfo
```

The response should be JSON describing the gateway host (hostname, platform, CPU count, and related fields). If you get `401 Unauthorized`, recheck the password from step 2. If you get `404`, cloud-init may still be deploying; wait two minutes and retry.

## Step 4: Deploy a function from the OpenFaaS store

Log in to the gateway with `faas-cli` from your workstation (replace the gateway URL with your floating IP):

```bash
export OPENFAAS_URL=http://YOUR_FLOATING_IP
faas-cli login -u admin -p YOUR_PASSWORD --gateway "$OPENFAAS_URL"
```

Deploy `figlet` from the OpenFaaS function store:

```bash
faas-cli store deploy figlet --gateway "$OPENFAAS_URL"
```

Wait until the deploy reports success, then invoke it over the floating IP:

```bash
curl -s -u admin:YOUR_PASSWORD \
  http://YOUR_FLOATING_IP/function/figlet \
  -d "edge functions"
```

The response body should be ASCII art spelling your input.

List deployed functions to confirm both `nodeinfo` and `figlet` are ready:

```bash
faas-cli list --gateway "$OPENFAAS_URL"
```

## Step 5: Add a secret and consume it from a function

Create a secret that holds the string `figlet` should print when the scheduled job runs:

```bash
echo -n "cron secret payload" | faas-cli secret create cron-payload --from-file=- --gateway "$OPENFAAS_URL"
```

Verify the secret exists:

```bash
faas-cli secret list --gateway "$OPENFAAS_URL"
```

The list should include `cron-payload`.

On faasd, secrets are files under `/var/openfaas/secrets/` inside function containers. Store functions like `figlet` do not mount custom secrets by default; consume the secret in the cron step by reading it on the host and passing the value as the request body:

```bash
ssh ubuntu@YOUR_FLOATING_IP 'PASS=$(sudo cat /var/lib/faasd/secrets/basic-auth-password); PAYLOAD=$(sudo cat /var/lib/faasd/secrets/cron-payload); curl -s -u admin:$PASS http://127.0.0.1:8080/function/figlet -d "$PAYLOAD"'
```

The response should be ASCII art for `cron secret payload`, which confirms the secret value reached the function invocation path.

## Step 6: Schedule a cron invocation on the gateway host

faasd on a single VM does not ship a separate cron connector. Schedule periodic invocations with a root crontab entry on the gateway that reads the secret and calls `figlet` on localhost:

```bash
ssh ubuntu@YOUR_FLOATING_IP 'sudo bash -s' <<'EOF'
PASS=$(cat /var/lib/faasd/secrets/basic-auth-password)
CRON_LINE='*/5 * * * * root PAYLOAD=$(cat /var/lib/faasd/secrets/cron-payload); curl -sf -u admin:'"$PASS"' http://127.0.0.1:8080/function/figlet -d "$PAYLOAD" >> /var/log/faasd-cron.log 2>&1'
grep -q faasd-cron.log /etc/crontab || echo "$CRON_LINE" >> /etc/crontab
EOF
```

Wait six minutes, then inspect the log on the gateway:

```bash
ssh ubuntu@YOUR_FLOATING_IP 'sudo tail -5 /var/log/faasd-cron.log'
```

The log should show successful HTTP responses from repeated `figlet` invocations. Each line corresponds to one cron tick.

## Step 7: Point a domain and enable HTTPS (optional)

Skip this step if HTTP on the floating IP is enough for your test. To enable automatic TLS with Caddy in front of faasd:

1. Create a DNS **A record** for your domain (for example `functions.example.com`) pointing at `YOUR_FLOATING_IP`. Follow [How to point a domain at a Quake AI resource](/docs/network/how-to/point-domain-to-quake-ai). Wait until it resolves:

```bash
dig +short functions.example.com
```

2. Set `domain` in `terraform.tfvars` and re-apply:

```hcl
domain = "functions.example.com"
```

```bash
tofu apply
```

cloud-init switches to the HTTPS branch: Caddy terminates TLS on ports 80 and 443 and forwards to faasd on `127.0.0.1:8080`. Wait three to five minutes for Caddy to obtain a Let's Encrypt certificate.

3. Confirm HTTPS end to end:

```bash
curl -s -u admin:YOUR_PASSWORD https://functions.example.com/function/figlet -d "tls ok"
```

The response should be ASCII art for `tls ok`.

Update `OPENFAAS_URL` and re-login if you deploy more functions over HTTPS:

```bash
export OPENFAAS_URL=https://functions.example.com
faas-cli login -u admin -p YOUR_PASSWORD --gateway "$OPENFAAS_URL"
```

For certificate background, see [How to issue and auto-renew a TLS certificate with Let's Encrypt](/docs/network/how-to/lets-encrypt-certificate).

## What you built

- **Applied the `edge-functions` template** to provision a private network, gateway security group, data volume, faasd host, and floating IP
- **Invoked the starter `nodeinfo` function** over the floating IP with gateway basic auth
- **Deployed `figlet` from the OpenFaaS store** and invoked it over the public URL
- **Created and consumed a `cron-payload` secret** by passing its value into a function invocation
- **Scheduled a five-minute cron job** on the gateway host that invokes `figlet` with the secret payload

## Scope of this deployment

This gateway runs in one region on a VM you operate. It executes functions in that region on containerd via faasd. It is not a global edge network: Quake AI has no anycast, no global PoPs, and no first-party serverless edge. For geographic distribution, [front workloads with a third-party CDN](/docs/network/how-to/front-with-cdn).

For Deno edge functions beside a self-hosted BaaS stack, see [Supabase self-host](/resources/iac-templates/supabase-selfhost) and its [deployment walkthrough](/resources/deployments/deploy-supabase-selfhost-template). For WebAssembly functions on Kubernetes, see the [Kubernetes cluster template](/resources/iac-templates/k8s-cluster).

## Next steps

- [Edge functions template](/resources/iac-templates/edge-functions): parameters, ports, and resource map
- [Edge reverse proxy template](/resources/iac-templates/edge-reverse-proxy): TLS termination without a function runtime
- [API gateway template](/resources/iac-templates/api-gateway): rate limits and key auth in front of long-running services
- [Front with a CDN](/docs/network/how-to/front-with-cdn): geographic edge for static assets and public APIs
- [Security hardening checklist](/docs/security/hardening-checklist): audit security groups and floating IP usage

## Clean up

When you no longer need the deployment, destroy the OpenTofu stack:

```bash
tofu destroy
```

Remove any DNS A record you pointed at the gateway floating IP and delete cron entries if you added them manually on the host before destroy.
