Skip to content

Self-hosting

Updates and migrations_

Keep your self-hosted Appwrite instance up-to-date. Learn how to perform updates, manage versions, and ensure your self-hosted Appwrite stays current.

6 min read

Raw

To upgrade your Appwrite server from an older version, you should use the Appwrite migration tool after you have installed the new version. The migration tool will adjust your Appwrite data to the new version's structure to make sure your Appwrite data is compatible with any internal changes.

You can upgrade to a newer patch version without running the migration unless the release notes indicate a migration is required. For example, you can upgrade from 1.6.0 to 1.6.1 without running the migrate command, but upgrading from 1.6.0 to 1.6.2 or later will require the migrate command because 1.6.2 requires a migration.

If you're trying to migrate to a newer minor version, you should upgrade to each minor version's latest patch. For example, if you're upgrading from 1.5.1 to 1.7.4 you should upgrade to:

  1. 1.5.11
  2. 1.6.2
  3. 1.7.4

1.9 is the exception: 1.9.6 added a new migration in a patch release, and each migration only runs its own changes. From 1.8.x or earlier, upgrade to 1.9.5 and run the migration before moving to 1.9.10, or the 1.9 migration is skipped. For example, from 1.8.1 to 2.7.1 you should upgrade to:

  1. 1.9.5
  2. 1.9.10
  3. 2.7.1

Recent versions share a migration and can skip minor versions. The latest upgrade section names the oldest version that upgrades to the current release in one step. From an older version, upgrade to that version first.

Before upgrading, be sure to:

  1. back up your server data before running the migration
  2. review the changelog for any breaking changes
  3. test the migration process on a non-production instance to make sure your application is working well

Upgrading to 2.7.1

Back up your data first.

2.7.1 is a security release. It updates the Open Runtimes executor from 1.1.2 to 1.3.1. The old executor followed symlinks that a function or site planted in its logs, build output or build cache, so a deployment could read files from the executor container, including _APP_EXECUTOR_SECRET. With that secret, code in a function can call the executor directly, and the executor controls Docker on the host. If anyone you don't fully trust can deploy functions or sites on your instance, upgrade and then rotate _APP_EXECUTOR_SECRET.

From 2.7.0, the upgrade only replaces the images. 2.7.1 has no schema changes and keeps the same 1.2.138 Console, so the migration has nothing to change.

Appwrite 2.7.1 upgrades from 1.9.6 or any later version. Run the upgrade command with the 2.7.1 tag, then run the migration.

Bash
docker run -it --rm \
--publish 20080:20080 \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="upgrade" \
appwrite/appwrite:2.7.1

Then run the migration from the appwrite directory.

Bash
cd appwrite/
docker compose exec appwrite migrate

If you maintain your own docker-compose.yml, set the openruntimes-executor image to openruntimes/executor:1.3.1 and run docker compose up -d.

To rotate the executor secret, set a new _APP_EXECUTOR_SECRET in .env and run docker compose up -d. This recreates the API, the workers and the executor together, so nothing needs the old value.

From 2.6.x or earlier, the V25 migration is idempotent and adds:

  • prefs on projects; alertsUnsubscribed on users; the GeoIP attributes on sessions; and folder with its index on buckets created before 2.0.0.
  • type on transactions, backfilled to legacy for existing ones.
  • topicInternalId indexes on the push ledger. Ledger entries are now bound to their topic, so entries written before the upgrade have no topicInternalId and are never replayed. Push messages a device had not yet received over MQTT when you upgrade are not delivered when it reconnects; the age-based prune removes those entries. New messages replay as before.
  • The transfers, monitors, monitorAlerts and reviews collections, used by API features that have no self-hosted Console screen yet.

If you're upgrading from 2.6.x or earlier, also check these, which come from 2.7.0:

  • Server key: nothing to change. Keep _APP_OPENSSL_KEY_V1. To rotate it later, recreate the containers after each configuration change with docker compose up -d (docker compose restart keeps the old environment): set _APP_OPENSSL_KEY_V2 in .env and run docker compose up -d; set _APP_OPENSSL_KEY_CURRENT=2 and run docker compose up -d; run docker compose exec appwrite encryption (a dry run that counts what's left), then docker compose exec appwrite encryption --commit=true (resume with --after=<projectId> if it stops); then set _APP_OPENSSL_KEY_OLDEST=2 and run docker compose up -d to stop accepting V1 tokens. JWTs and resource tokens signed with V1 stop working at that point. Files already encrypted in storage are not re-encrypted, so keep _APP_OPENSSL_KEY_V1 configured.
  • Realtime and MQTT clients: a connection over 100 subscriptions, channels or queries now gets an error, and a user's 33rd connection on one worker is closed. Raise the realtime limits with the variables above if your app needs more.
  • Bulk writes: bulk update, upsert and delete from client sessions now return 401. They need an API key or a console user.
  • API keys: a key that creates a migration now needs the write scope of every resource it imports, and a key that configures a function or site can only add scopes it holds itself.
  • MFA: turning MFA off now needs a recent MFA challenge, and a TOTP code can be used once.
  • PostgreSQL: the bundled PostgreSQL starts with max_connections=500. Set _APP_DB_MAX_CONNECTIONS lower on small hosts.
  • Functions domain: if you changed _APP_DOMAIN_FUNCTIONS, list the old domain in _APP_DOMAIN_FUNCTIONS_FALLBACK to keep serving preview URLs on it.
  • Maintenance: _APP_MAINTENANCE_INTERVAL should be a multiple of 300 seconds. The default, 86400, is.
  • New optional variables: _APP_OPENSSL_KEY_V2, _APP_OPENSSL_KEY_CURRENT, _APP_OPENSSL_KEY_OLDEST, _APP_LIMIT_REALTIME_SUBSCRIPTIONS, _APP_LIMIT_REALTIME_CHANNELS, _APP_LIMIT_REALTIME_QUERIES, _APP_DB_MAX_CONNECTIONS and _APP_DOMAIN_FUNCTIONS_FALLBACK.

If you skip releases, also follow the upgrade notes of each release you skip: 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.6.0, 2.6.1, 2.6.2, 2.7.0.

Upgrading within the 1.9 line

If you are staying on Appwrite 1.9, upgrade to 1.9.10. It fixes the Brotli decoder vulnerability CVE-2026-111497 and backports security fixes from 2.6.2. It doesn't change the database schema. Some fixes tighten defaults: project SMTP, messaging SMTP providers and migration sources that point at a private or reserved address now need that address in _APP_ALLOWED_INTERNAL_ADDRESSES, which 1.9.10 adds to the 1.9 line. Read the 1.9.10 release notes before upgrading.

From 1.9.8 or earlier, 1.9.10 also includes the fixes of each release you skip: 1.9.9 moves the runtime to PHP 8.5.12, and 1.9.8 refreshes outbound TLS trust after the eMudhra certificate compromise and applies the SVG sanitizer fixes.

Back up your database and persistent storage. From the parent directory of your installation, run:

Bash
docker run -it --rm \
--publish 20080:20080 \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="upgrade" \
appwrite/appwrite:1.9.10
cd appwrite/
docker compose exec appwrite migrate

Outbound HTTPS endpoints that still present a compromised certificate chain must replace their certificate chain. See the 1.9.10, 1.9.9 and 1.9.8 release notes for details.

Installing the next version

The first step is to install the latest version of Appwrite. Head to the directory where you ran your previous Appwrite install command.

Plain text
parent_directory <= you run the command in this directory
└── appwrite
└── docker-compose.yml

The parent directory is where you will find the appwrite directory, inside which there are docker-compose.yml and .env files.

Unix

Bash
docker run -it --rm \
--publish 20080:20080 \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="upgrade" \
appwrite/appwrite:<APPWRITE_VERSION>

CMD

PowerShell
docker run -it --rm ^
--publish 20080:20080 ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="upgrade" ^
appwrite/appwrite:<APPWRITE_VERSION>

PowerShell

PowerShell
docker run -it --rm `
--publish 20080:20080 `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="upgrade" `
appwrite/appwrite:<APPWRITE_VERSION>

This will pull the docker-compose.yml for the selected version/tag and perform the upgrade steps. Once the setup completes, verify that you have the latest version of Appwrite.

Bash
docker ps | grep appwrite/appwrite

Verify that the STATUS doesn't have any errors and all the appwrite/appwrite containers have the same version.

Running the migration

We can now start the migration. Navigate to the appwrite directory where your docker-compose.yml is present and run the following command.

Bash
cd appwrite/
docker compose exec appwrite migrate

The data migration can take longer depending on the amount of data your Appwrite instance contains. The Appwrite migration command uses multi-threading to speed up the process, meaning that adding more CPU cores can help speed up the process.

Once the migration process has been completed successfully, you're all set to use the latest version of Appwrite!

Was this page helpful?

Share what worked or what we should fix. Once approved, our agents automatically apply suggested updates to the docs.