2.2.0
Appwrite 2.2 brings email policies to self-hosted installs, finishes the move of function and site executions to ClickHouse, surfaces the real reason a build failed, and folds in three weeks of fixes across auth, databases, functions, messaging and the installer. It ships with the 1.1.96 self-hosted Console. Upgrading from 2.1.0 is a single command and a short migration on the console database.
Highlights
- Email policies on self-hosted.
PATCH /v1/project/policies/deny-disposable-email,deny-free-email,deny-aliased-emailanddeny-corporate-email, plus their entries inGET /v1/project/policies, now ship in the self-hosted API. The Console's Auth → Policies → Emails toggles work instead of returning 404. (#13641, fixes #13618) - Executions live in ClickHouse. Function and site executions are written to and read from ClickHouse only; the project database copy and the
_APP_EXECUTIONS_DUAL_WRITEflag are gone, and execution timestamps are ISO-8601 again. (#13668, #13686) - Build failures say why. Orchestrator 2.2 reports the cause of a failed build (unrecognized archive, download returned 404, out of memory) and the message lands in the build logs instead of
Build failed with exit code -1.Build durations are recorded as measured rather than capped at the timeout, and an artifact failure fails the build immediately. (#13603, #13582, #13674) - Alphanumeric Twilio sender IDs.
POST /v1/messaging/providers/twilioaccepts a sender ID such asAppwriteinfrom, matching update, and_APP_SMS_FROMno longer mistakes a sender starting withMGfor a Messaging Service SID. (#13626) - Console 1.1.96, self-hosted build. Activity and realtime pages work on mobile, manual deployments on macOS can pick
.tar.gzfiles, impersonation shows names for recent users, SMTP has a Resend quick setup, and/.well-known/change-passwordlets password managers deep-link to the password screen.
Fixes
Console
- Activity and realtime pages are usable on mobile. (appwrite/vibes#357, Console 1.1.96)
- The manual deployment file picker on macOS accepts
.tar.gzarchives. (appwrite/vibes#359, Console 1.1.96) - The Twilio provider form describes the sender as a phone number or alphanumeric ID. (appwrite/vibes#344)
- Impersonation shows names for recent users and remembers new picks. (appwrite/vibes#348)
Auth and users
- OAuth2 and VCS redirect routes return JSON errors to SDK requests instead of the HTML error page. (#13625, fixes #8090)
- A rate-limiter Redis socket shared across coroutines could crash an HTTP worker under concurrent requests. The limiter now leases its connection from a pool. (#13640)
- SMS team invitations keep
&separators in the redirect URL so apps can readuserIdandsecret. (#13646, fixes #8607) - Initials avatars for handle-style names such as
@ItzNotABugrender instead of a blank square. (#13662, fixes #8042)
Databases and TablesDB
- Shrinking an encrypted string attribute below 150 characters on update is rejected, as it already was on create. (#13649)
- Clearing an optional attribute's default with
default: nullclears it in the stored schema, so new documents receivenull. Point, Line and Polygon updates withoutdefaultbehave the same. (#13648, fixes #9714) - Loading relationships no longer exceeds the configured query-value limit. (#13610, utopia-php/database 7.3.9)
- GraphQL returns array attributes as arrays, and empty arrays as
[], instead of objects with numeric keys. (#13627)
Functions and Sites
- Executions created with
X-Appwrite-JWTreceive that JWT inx-appwrite-user-jwt, synchronously and asynchronously. (#13647, fixes #7247) - Domain rules are repointed before a deployment is marked active, so a site never briefly serves "No active deployments" after activation. (#13658)
Messaging
- Msg91 providers created without a template ID stay disabled until one is configured. (#13663)
Installer and workers
- The MariaDB healthcheck no longer depends on the entrypoint-managed
healthcheckuser, which made upgrades from 1.x fail with "Failed to start containers". The service also restarts unless stopped. (#13652) - Queue consumers stop taking new jobs as soon as SIGTERM arrives, so rolling restarts drain faster. (#13629, utopia-php/queue 2.2.4)
Under the hood
- Console notifications can name the team they belong to (
teamId,teamInternalIdand a_key_teamindex), applied by the V25 migration. (#13664) - Shell commands in the builds worker, certificate issuance and migrations are built with
Utopia\Commandas argument arrays instead of concatenated strings. (#13616) - Realtime error reporting runs on spans; the
logErrorcompatibility function is removed. (#13675, #13679) - Compose orchestrator 2.1.1 → 2.2.0, open-runtimes/sdk-for-php 2.0, autogravity 0.0.10, utopia-php/console 0.2, database 7.3.9, queue 2.2.4. (#13603, #13610, #13616, #13629, #13682, #13683)
- E2E flake fixes for the Organization and Teams suites; CI pulls MinIO from quay.io. (#13654, #13656, #13657, #13643)
Removed
_APP_EXECUTIONS_DUAL_WRITE. Executions are ClickHouse only._APP_MAINTENANCE_RETENTION_USAGE_HOURLYand thestatscollection it pruned. Usage has been ClickHouse only since 2.0. (#13630)_APP_CONNECTIONS_DB_LOGSand thelogsdatabase. Nothing read it. (#13655)
Install
docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:2.2.0
Upgrade
Upgrading from 2.1.0 uses the same command with --entrypoint="upgrade", then run the migration:
cd appwrite
docker compose exec appwrite migrate
The migration adds two optional columns and one index to the console notifications collection and is quick. ClickHouse is now required for function and site executions; installs that ran 2.0.0 or 2.1.0 with dual write enabled already have their executions there, and executions that only ever existed in the project database are no longer listed. Remove _APP_EXECUTIONS_DUAL_WRITE, _APP_MAINTENANCE_RETENTION_USAGE_HOURLY and _APP_CONNECTIONS_DB_LOGS from your .env if you set them. Back up your data first.
Contributors
Thank you to everyone who contributed to 2.2.0:
@ChiragAgg5k @HarshMN2345 @lohanidamodar @loks0n @Meldiron @TorstenDittmann