Following up on my previous post about deploying Appwrite with K3s, I will now guide you through configuring K3s to support Appwrite Functions.


Prepartion

Install Ngrok

Since I am running Appwrite in my HomeLab, I need to utilize ngrok to enable external network access (such as GitHub) to our internal network. After signing up, install ngrok via Chocolatey:

choco install ngrok
ngrok config add-authtoken xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ngrok http http://appwrite.local/                      

Take note of the host; in my case, it is d7e9-42-60-49-2.ngrok-free.app.

Register GitHub App

After logging in to your GitHub account, navigate to your avatar on the top right, and select Settings. Then, click on Developer settings and select New GitHub App at the top right corner.

appwrite-register-new-github-app

Following the Appwrite Functions Docs, fill in the details sequentially.

First, enter the GitHub App name (e.g. sh-hello-world) and homepage URL (e.g. https://seehiong.github.io/).

For the callback URL, click Add Callback URL and enter the following two URLs:

https://d7e9-42-60-49-2.ngrok-free.app/v1/vcs/github/callback
https://d7e9-42-60-49-2.ngrok-free.app/v1/account/sessions/oauth2/callback/github/console

Enable the checkbox for requesting user authorization (OAuth) during installation, and also for redirect on update.

appwrite-github-identify-authorize-user

In the Webhook section, provide the Webhook URL and its secret (e.g. webhook-secret):

https://d7e9-42-60-49-2.ngrok-free.app/v1/vcs/github/events

appwrite-github-webhook

Follow the settings outlined in the official documentation for Repository permissions, Account permissions, and Subscribe to events sections.

appwrite-github-repo-permission

Under Where can this GitHub App be installed?, select the Any account option to allow for multiple Appwrite projects.

appwrite-github-installation

Finally, click on the generate a private key link and then the Generate a private key button, followed by generating a new client secret.

appwrite-github-private-key


Appwrite Setup

After opening the private key file (e.g. sh-hello-world.xxx.pem), add a “\n” to the end of each line and remove the “carriage return” so that it becomes a single-line string:

"-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA5f58x/UROPmLo60dSmTqE3hDO4THacvj0nUy9gnGJ5tf6vS4\n[...]-----END RSA PRIVATE KEY-----"

Appwrite-deployment.yaml

Update the relevant values:

- name: _APP_VCS_GITHUB_APP_NAME
  value: sh-hello-world
- name: _APP_VCS_GITHUB_PRIVATE_KEY
  value: "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA5f58x/UROPmLo60dSmTqE3hDO4THacvj0nUy9gnGJ5tf6vS4\n[...]-----END RSA PRIVATE KEY-----"
- name: _APP_VCS_GITHUB_APP_ID
  value: "850000"
- name: _APP_VCS_GITHUB_WEBHOOK_SECRET
  value: webhook-secret
- name: _APP_VCS_GITHUB_CLIENT_SECRET
  value: "aeb0000000000000000000000000000000000000"
- name: _APP_VCS_GITHUB_CLIENT_ID
  value: "Iv1.0000000000000000"

Also, update the ngrok-related values:

- name: _APP_DOMAIN
  value: d7e9-42-60-49-2.ngrok-free.app
- name: _APP_DOMAIN_TARGET
  value: d7e9-42-60-49-2.ngrok-free.app
- name: _APP_DOMAIN_FUNCTIONS
  value: d7e9-42-60-49-2.ngrok-free.app

As we migrated to using K3s, modify the _APP_EXECUTOR_HOST as follows:

- name: _APP_EXECUTOR_HOST
  value: http://openruntimes-executor-svc.appwrite.svc/v1

Appwrite-worker-builds-deployment.yaml

To support functions, update these values:

- name: _APP_EXECUTOR_HOST
  value: http://openruntimes-executor-svc.appwrite.svc/v1
- name: _APP_VCS_GITHUB_APP_NAME
  value: sh-hello-world
- name: _APP_VCS_GITHUB_PRIVATE_KEY
  value: "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA5f58x/UROPmLo60dSmTqE3hDO4THacvj0nUy9gnGJ5tf6vS4\n[...]-----END RSA PRIVATE KEY-----"
- name: _APP_VCS_GITHUB_APP_ID
  value: "850000"
- name: _APP_DOMAIN
  value: d7e9-42-60-49-2.ngrok-free.app

Appwrite-worker-certificates-deployment.yaml

Update the ngrok-related values:

- name: _APP_DOMAIN
  value: d7e9-42-60-49-2.ngrok-free.app
- name: _APP_DOMAIN_TARGET
  value: d7e9-42-60-49-2.ngrok-free.app
- name: _APP_DOMAIN_FUNCTIONS
  value: d7e9-42-60-49-2.ngrok-free.app

Appwrite-worker-functions-deployment.yaml

To support functions, update these values:

- name: _APP_EXECUTOR_HOST
  value: http://openruntimes-executor-svc.appwrite.svc/v1

Appwrite-worker-migrations-deployment.yaml

Update the ngrok-related values:

- name: _APP_DOMAIN
  value: d7e9-42-60-49-2.ngrok-free.app
- name: _APP_DOMAIN_TARGET
  value: d7e9-42-60-49-2.ngrok-free.app

Appwrite-maintenance-deployment.yaml

Update the ngrok-related values:

- name: _APP_DOMAIN
  value: d7e9-42-60-49-2.ngrok-free.app
- name: _APP_DOMAIN_TARGET
  value: d7e9-42-60-49-2.ngrok-free.app
- name: _APP_DOMAIN_FUNCTIONS
  value: d7e9-42-60-49-2.ngrok-free.app

Openruntimes-executor-deployment.yaml

To allow executors to have access to Docker, set the pod to run as root (not advisable in production environments):

spec:
  securityContext:
    runAsUser: 0
    containers:
      - env:
          - name: OPR_EXECUTOR_NETWORK
            value:     
    ...

Alternatively, identify the node that runs the pod, and issue the command to create the runtimes network:

docker network create runtimes
docker network ls

Appwrite-realtime-svc.yaml

For proper Traefik routing, update the appwrite-realtime-svc.yaml file as follows:

apiVersion: v1
kind: Service
metadata:
  name: appwrite-realtime-svc
  namespace: appwrite
spec:
  selector: 
    io.kompose.service: appwrite-realtime
  ports:
  - name: appwrite-realtime
    protocol: TCP
    port: 80
    targetPort: 80

Appwrite-ing.yaml

To support Traefik routing, modify as follows:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: appwrite-ingress
  namespace: appwrite
spec:
  defaultBackend:
    service:
      name: appwrite-svc
      port:
        number: 80
  rules:
  - host: "d7e9-42-60-49-2.ngrok-free.app"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: appwrite-svc
            port:
              number: 80
      - pathType: Prefix
        path: "/v1/realtime"
        backend:
          service:
            name: appwrite-realtime-svc
            port:
              number: 80

Install GitHub App

Once all K8s files are redeployed, proceed with the setup. Click on Install & Authorize button from GitHub.

appwrite-install-github-app

Upon successful installation, you should see the GitHub repository under Git configuration in Appwrite.

appwrite-git-configuration-app


Create Appwrite Function

Next, proceed to create a function.

appwrite-create-function

Start with the Node.js template:

appwrite-create-function-config-nodes

Upon completion, click on generate API key:

appwrite-create-function-variables

Create a new repo in GitHub:

appwrite-create-function-connect

Enter the new repo name and set it as private:

appwrite-create-function-repo-nodes


Appwrite Function Deployments

With the function codes generated in GitHub, navigate from Appwrite to Functions and the newly created Starter function Node. You should see that the deployment is active as shown:

appwrite-function-deployments

For reference, here are some screenshots of my K3s Appwrite HomeLab:

appwrite-worker-builds

appwrite-openruntimes-executor

appwrite-ingresses

That concludes the setup, and your functions should now be deployed successfully!


Troubleshooting

Function execution failed

The function failed to be executed, with the openruntimes-executor pod logging these errors:

[Error] Type: Exception
[Error] Message: An internal curl error has occurred within the executor! Error Msg: Could not resolve host: 66069922a8ab3
[Error] File: /usr/local/app/http.php
[Error] Line: 1027
Warning: Swoole\Table::set(): key[executor-755b4586d7-5bmp8-660002098ba892717675-6600395e2b19005c2ddd] is too long in /usr/local/app/http.php on line 1283

appwrite-function-execution-failed

Wanted to run ngrok with custom subdomain, but this is only a feature off the paid plans:

ERROR:  failed to start tunnel: Custom subdomains are a feature on ngrok's paid plans.
ERROR:  Failed to bind the custom subdomain 'ngrok-free.app' for the account 'xxxxxx'.