Appwrite talks to a few outside services that the chart doesn't run: an SMTP server for email, a Git host for Git deployments, an SMS provider for phone sign-in, Sentry for errors. This guide shows how to connect each one.
| Integration | What it's for | Configure with |
|---|---|---|
| verification, password recovery, invitations | smtp |
|
| Git deployments | deploying Functions and Sites from a repository | _APP_VCS_* |
| Console sign-in | signing in to the console with GitHub, GitLab, Bitbucket or Google | _APP_CONSOLE_* |
| SMS | phone sign-in and SMS verification | _APP_SMS_* |
| Error tracking | sending server errors to Sentry | _APP_LOGGING_CONFIG |
| Antivirus | scanning uploads with ClamAV | _APP_STORAGE_ANTIVIRUS* |
Some settings live in the Appwrite console, per project, not in the chart: sign-in providers for your apps' users (OAuth2), Messaging providers (Mailgun, SendGrid, Twilio, FCM, APNs and others), and a project's own SMTP server, which replaces the chart's for that project's emails.
With networkPolicy.egress.enabled, Appwrite can only reach these services
once you allow them in networkPolicy.egress.extraRules
(security.md).
Integrations without a dedicated value are set through Appwrite's environment variables (reference):
appwrite.configfor plain settings. They go into the chart's ConfigMap.appwrite.secretConfigfor secrets you're happy to keep in your values file. The chart stores them in a Secret.appwrite.extraEnvFromfor a Secret you manage yourself, created withkubectl, External Secrets or Sealed Secrets. Name each key after the variable. This keeps secrets out of your values entirely.
appwrite:
config:
_APP_VCS_GITHUB_APP_NAME: my-appwrite
secretConfig:
_APP_LOGGING_CONFIG: sentry://...
extraEnvFrom:
- secretRef:
name: appwrite-integrationsWhen a Secret you don't control uses other key names, map a key to a
variable with appwrite.extraEnv:
appwrite:
extraEnv:
- name: _APP_VCS_GITHUB_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: github-app
key: client-secretAll four reach every pod that runs the Appwrite image: API, realtime, workers, tasks, MQTT and the migration Job.
From lowest to highest precedence:
- Appwrite's defaults (
charts/appwrite/files/env-defaults.yaml), then the variables the chart computes from its values (domains, SMTP host, database hosts). Both are in the ConfigMap. appwrite.config, which replaces keys in the ConfigMap.appwrite.secretConfig.appwrite.extraEnvFrom, in list order.appwrite.extraEnv, and the variables the chart sets on each container.
The ConfigMap already lists the non-secret Git and console sign-in
variables (_APP_VCS_GITHUB_APP_ID, _APP_CONSOLE_GITHUB_APP_ID, ...) with
empty values. Setting one in any of the places above replaces the empty
default.
The variables the chart sets on each container are of two kinds:
- Credentials, such as
_APP_SMTP_PASSWORD,_APP_DB_PASSand_APP_OPENSSL_KEY_V1. Nothing in steps 2 to 4 overrides them. Set them through their own values (smtp.password,database.password, ...; see credentials). - Per-pod sizing:
_APP_CPU_NUM,_APP_WORKERS_NUM,_APP_WORKER_MAX_COROUTINES,_APP_CONNECTIONS_DB_USAGEand_APP_CONNECTIONS_DB_EXECUTIONS. The chart leaves one out whenconfigorsecretConfigsets it, so those two can override it. A key inextraEnvFromcan't.
Pods restart on their own when config or secretConfig changes. After
changing a Secret you manage, restart them:
kubectl -n appwrite rollout restart deployment -l app.kubernetes.io/instance=appwriteWithout SMTP, Appwrite can't send verification emails, password recovery links, magic URLs or team invitations. Any SMTP server works:
kubectl -n appwrite create secret generic appwrite-smtp \
--from-literal=smtp-password='<SMTP password or API key>'smtp:
host: email-smtp.eu-west-1.amazonaws.com
port: 587 # the default
secure: tls # the default: STARTTLS on 587; use ssl with port 465
username: <SMTP username>
password:
secretName: appwrite-smtp # or value: ... inline
appwrite:
system:
emailName: Example
emailAddress: noreply@example.com # a sender your provider lets you use| Provider | host |
username |
Password |
|---|---|---|---|
| Amazon SES | email-smtp.<region>.amazonaws.com |
SMTP user name from SMTP settings (not an IAM access key) | its SMTP password |
| SendGrid | smtp.sendgrid.net |
apikey |
an API key |
| Postmark | smtp.postmarkapp.com |
a server API token | the same token |
| Mailgun | smtp.mailgun.org (EU: smtp.eu.mailgun.org) |
the domain's SMTP login | its SMTP password |
| Resend | smtp.resend.com |
resend |
an API key |
| Brevo | smtp-relay.brevo.com |
your SMTP login | an SMTP key |
All of them accept port 587 with secure: tls. The sender address
(appwrite.system.emailAddress, noreply@<domain> by default) must be on a
domain you've verified with the provider, or it rejects the message.
To check it, invite someone to a team in the console or use Forgot password. When nothing arrives, the log of the worker that sends mail says why:
kubectl -n appwrite logs deploy/appwrite-worker | grep -i mailworkers.mode |
Deployment that sends mail |
|---|---|
combined (default) |
appwrite-worker |
separate |
appwrite-worker-mails |
grouped |
appwrite-worker-<group>, for the group that lists mails |
Skipped mail processing. No SMTP configuration has been set. means
smtp.host is empty and the project has no SMTP server of its own.
Functions and Sites can deploy automatically from a Git repository on every push, and comment on pull requests with preview links. This needs Functions and Sites enabled, and Appwrite reachable from the Git host, which sends webhooks: from the internet for GitHub, gitlab.com and Bitbucket.
Callback URLs use your console's domain: appwrite.domain, unless you set
appwrite.consoleDomain. Webhooks go to appwrite.domain.
Appwrite connects through a GitHub App that you create and own.
1. Create the app under Settings → Developer settings → GitHub Apps of your user or organization:
| Setting | Value |
|---|---|
| Callback URLs, in this order | https://<console domain>/v1/vcs/github/callbackhttps://<console domain>/v1/account/sessions/oauth2/callback/github/console |
| Request user authorization (OAuth) during installation | on |
| Post installation: Redirect on update | on |
| Webhook: Active | on |
| Webhook URL | https://<domain>/v1/vcs/github/events |
| Webhook secret | a random string, e.g. openssl rand -hex 32 |
| Where can this GitHub App be installed? | Any account |
Give it the repository permissions Administration, Checks, Commit statuses, Contents, Issues, Pull requests and Webhooks (read and write) and Metadata (read-only), the account permission Email addresses (read-only), and subscribe it to the Pull request and Push events. Appwrite's guide has screenshots of each step.
2. Collect its credentials from the app's settings page: the App ID, the
Client ID, a client secret (Generate a new client secret), and a private
key (Generate a private key, which downloads a .pem file).
3. Give them to Appwrite. The private key goes in as a file, line breaks and all:
kubectl -n appwrite create secret generic appwrite-github \
--from-file=_APP_VCS_GITHUB_PRIVATE_KEY=my-appwrite.2026-10-09.private-key.pem \
--from-literal=_APP_VCS_GITHUB_CLIENT_SECRET='<client secret>' \
--from-literal=_APP_VCS_GITHUB_WEBHOOK_SECRET='<webhook secret>'appwrite:
config:
_APP_VCS_GITHUB_APP_NAME: my-appwrite # the slug in https://github.com/apps/<slug>
_APP_VCS_GITHUB_APP_ID: "123456"
_APP_VCS_GITHUB_CLIENT_ID: Iv23li...
extraEnvFrom:
- secretRef:
name: appwrite-githubUpgrade, then open a function or site in the console and choose Connect
Git. If GitHub shows a delivery error under the app's Advanced tab, check
that https://<domain>/v1/vcs/github/events is reachable from outside and
that the webhook secret matches. The secret is required: Appwrite 2.4.0 and
later refuse GitHub's events without _APP_VCS_GITHUB_WEBHOOK_SECRET.
These connect through an OAuth application that you create on the Git host.
Give it the redirect URL https://<console domain>/v1/vcs/<host>/callback
(gitlab, gitea or bitbucket) and the scopes Appwrite asks for:
| Host | Scopes |
|---|---|
| GitLab (gitlab.com or self-hosted) | api, read_user |
| Gitea | read:repository, write:repository, read:organization, read:issue, write:issue |
| Bitbucket (bitbucket.org only) | account, repository:write, pullrequest:write, webhook |
Then set its variables. Endpoints and client IDs go in appwrite.config;
client and webhook secrets in a Secret, as for GitHub. The webhook secret is
a random string you choose. Appwrite registers a webhook with it on each
repository you connect, so all three hosts require one.
| Host | appwrite.config |
Secret |
|---|---|---|
| GitLab | _APP_VCS_GITLAB_CLIENT_ID, plus _APP_VCS_GITLAB_ENDPOINT when self-hosted (default https://gitlab.com) |
_APP_VCS_GITLAB_CLIENT_SECRET, _APP_VCS_GITLAB_WEBHOOK_SECRET |
| Gitea | _APP_VCS_GITEA_ENDPOINT, _APP_VCS_GITEA_CLIENT_ID |
_APP_VCS_GITEA_CLIENT_SECRET, _APP_VCS_GITEA_WEBHOOK_SECRET |
| Bitbucket | _APP_VCS_BITBUCKET_CLIENT_ID (the OAuth consumer's key) |
_APP_VCS_BITBUCKET_CLIENT_SECRET, _APP_VCS_BITBUCKET_WEBHOOK_SECRET |
The endpoint must be reachable from the Appwrite pods. For GitLab it must
also be reachable from your users' browsers. For Gitea, set
_APP_VCS_GITEA_BROWSER_ENDPOINT when browsers reach it on another address,
such as when _APP_VCS_GITEA_ENDPOINT is a cluster-internal Service.
Webhooks go to https://<domain>/v1/vcs/<host>/events. When the Git host
can't reach that address, for example a Gitea in the same cluster, set
_APP_VCS_WEBHOOK_URL to a base URL it can reach, such as
http://appwrite-api.appwrite.svc.
By default, console users sign in with email and password. To offer a
provider as well, create an OAuth application there with the redirect URL
https://<console domain>/v1/account/sessions/oauth2/callback/<provider>/console
(github, gitlab, bitbucket or google), and set its ID and secret:
| Provider | ID (appwrite.config) |
Secret |
|---|---|---|
| GitHub | _APP_CONSOLE_GITHUB_APP_ID |
_APP_CONSOLE_GITHUB_SECRET |
| GitLab | _APP_CONSOLE_GITLAB_APP_ID, plus _APP_CONSOLE_GITLAB_ENDPOINT when self-hosted (default https://gitlab.com) |
_APP_CONSOLE_GITLAB_SECRET |
| Bitbucket | _APP_CONSOLE_BITBUCKET_APP_ID |
_APP_CONSOLE_BITBUCKET_SECRET |
_APP_CONSOLE_GOOGLE_APP_ID |
_APP_CONSOLE_GOOGLE_SECRET |
appwrite.console.whitelistRoot, whitelistEmails and whitelistIPs still
decide who may create an account.
Phone sign-in and SMS verification need an SMS provider. Appwrite reads it
from a DSN, sms://<user>:<secret>@<provider>, and sends from
_APP_SMS_FROM. Both must be set, or SMS stays off.
appwrite:
config:
_APP_SMS_FROM: "+15551234567" # or a Twilio Messaging Service SID (MG...)
secretConfig:
_APP_SMS_PROVIDER: sms://<account SID>:<auth token>@twilio<provider> |
<user> |
<secret> |
|---|---|---|
twilio |
Account SID | Auth token |
textmagic |
username | API key |
telesign |
customer ID | API key |
msg91 |
sender ID | auth key |
vonage |
API key | API secret |
MSG91 also needs a template ID: add ?templateId=<id> to the DSN, or
Appwrite uses _APP_SMS_FROM as the template ID. URL-encode the user and
secret if they contain anything but letters and digits. Appwrite's
SMS guide
has more on each provider.
Appwrite reports server errors to Sentry, from the API, workers, tasks and
realtime. Rewrite your project's Sentry DSN,
https://<key>@<sentry host>/<project id>, as:
appwrite:
secretConfig:
_APP_LOGGING_CONFIG: sentry://<project id>:<key>@<sentry host>/Sentry is the only provider Appwrite 2.5.0 supports. When the DSN is
invalid, the pods log Invalid _APP_LOGGING_CONFIG, error reporting is disabled at start-up. Logs go to stdout regardless, as JSON by default
(appwrite.logFormat), ready for your log pipeline.