## Summary - Documents the new at-rest encryption envelope (`ENCRYPTION_KEY` / `FALLBACK_ENCRYPTION_KEY`) introduced in v2.5+ and clarifies its relationship to the legacy `APP_SECRET`-as-encryption-key path. - Adds a new dedicated **Key rotation** guide covering manual / Enterprise-cron JWT signing-key rotation, signing-key revocation, and the online `ENCRYPTION_KEY` rotation procedure (including the new \`secret-encryption:rotate\` CLI shipped in a follow-up PR). - Updates the docker-compose quickstart to generate a dedicated \`ENCRYPTION_KEY\` from day 1. - Mentions the v2.5+ enc:v2 backfill in the upgrade guide. English-only — the localized mirrors will be picked up by i18n CI. ## Test plan - [ ] Mintlify build passes locally / in CI - [ ] Sidebar entry renders under **Self-Host → Key rotation** - [ ] Internal links to /developers/self-host/capabilities/key-rotation resolve from setup.mdx, docker-compose.mdx and upgrade-guide.mdx --------- Co-authored-by: github-actions <github-actions@twenty.com>
103 lines
4.0 KiB
Plaintext
103 lines
4.0 KiB
Plaintext
---
|
|
title: Upgrade guide
|
|
icon: "arrow-up-right-dots"
|
|
---
|
|
|
|
## General guidelines
|
|
|
|
**Always back up your database before starting the upgrade process** by running:
|
|
|
|
```bash
|
|
docker exec -it {db_container_name_or_id} pg_dumpall -U {postgres_user} > databases_backup.sql
|
|
```
|
|
|
|
To restore from backup:
|
|
|
|
```bash
|
|
cat databases_backup.sql | docker exec -i {db_container_name_or_id} psql -U {postgres_user}
|
|
```
|
|
|
|
If you use Docker Compose, follow these steps:
|
|
|
|
1. Stop Twenty: `docker compose down`
|
|
2. Change the `TAG` value in the `.env` file next to your `docker-compose.yml`
|
|
3. Start Twenty: `docker compose up -d`
|
|
|
|
The server runs all required upgrade migrations automatically on startup. No manual command is needed.
|
|
|
|
## Cross-version upgrades (v1.22+)
|
|
|
|
Starting from **v1.22**, Twenty supports cross-version upgrades. You can jump directly from any supported version to the latest release without stepping through each intermediate version.
|
|
|
|
For example, upgrading from v1.22 straight to v2.0 is fully supported.
|
|
|
|
## Upgrading to v2.5+ — at-rest encryption envelope
|
|
|
|
Starting in **v2.5**, Twenty stores at-rest secrets (OAuth tokens, application variables, signing-key private keys, sensitive config values, TOTP secrets) inside a versioned `enc:v2:` envelope encrypted with `ENCRYPTION_KEY` (or `APP_SECRET` if `ENCRYPTION_KEY` is unset).
|
|
|
|
The first boot on v2.5 runs slow upgrade commands that **backfill** existing rows into the new envelope. They are idempotent — interrupting and restarting the server resumes from where it left off — but they can take a while on large databases. You can monitor progress with `upgrade:status`.
|
|
|
|
You should set a dedicated `ENCRYPTION_KEY` **before** the v2.5 upgrade so the backfill writes rows under it from the start. Switching keys after the backfill requires a [rotation](/developers/self-host/capabilities/key-rotation).
|
|
|
|
## Rotating secrets and signing keys
|
|
|
|
For day-to-day operational tasks like rotating `ENCRYPTION_KEY`, rotating the JWT signing key, or revoking a leaked signing key, see the dedicated [Key rotation guide](/developers/self-host/capabilities/key-rotation).
|
|
|
|
## Checking upgrade status
|
|
|
|
The `upgrade:status` command lets you inspect the current state of your instance and workspace migrations. It is useful for debugging upgrade issues or when filing a support request.
|
|
|
|
Run it from the server container:
|
|
|
|
```bash
|
|
docker exec -it {server_container_name_or_id} yarn command:prod upgrade:status
|
|
```
|
|
|
|
Example output:
|
|
|
|
```sh
|
|
APP_VERSION: v1.23.0
|
|
|
|
Instance
|
|
Inferred version: 1.23.0
|
|
Latest command: 1.23.0_DropWorkspaceVersionColumnFastInstanceCommand_1785000000000
|
|
Status: Up to date
|
|
Executed by: v1.23.0
|
|
At: 2026-04-16T11:43:58.823Z
|
|
|
|
Workspace
|
|
Apple (20202020-1c25-4d02-bf25-6aeccf7ea419)
|
|
Inferred version: 1.23.0
|
|
Latest command: 1.23.0_UpdateGlobalObjectContextCommandMenuItemsCommand_1780000005000
|
|
Status: Up to date
|
|
Executed by: v1.23.0
|
|
At: 2026-04-16T11:44:09.361Z
|
|
|
|
Summary
|
|
Instance: Up to date
|
|
Workspaces: 1 up to date, 0 behind, 0 failed (1 total)
|
|
```
|
|
|
|
### Options
|
|
|
|
| Flag | Description |
|
|
| --- | --- |
|
|
| `-w, --workspace-id <id>` | Filter to a specific workspace. Can be passed multiple times. |
|
|
| `-f, --failed-only` | Hide up-to-date workspaces, only show behind and failed entries. |
|
|
|
|
## Troubleshooting
|
|
|
|
If the upgrade fails on some workspaces, the server will not advance past the failing step. Restarting the server (`docker compose up -d`) will retry the upgrade from where it left off.
|
|
|
|
To quickly identify problems, run:
|
|
|
|
```bash
|
|
docker exec -it {server_container_name_or_id} yarn command:prod upgrade:status --failed-only
|
|
```
|
|
|
|
This shows only workspaces that are behind or have failed, along with the error message for each failure.
|
|
|
|
## Before v1.22
|
|
|
|
If your instance is older than v1.22, you must upgrade incrementally through each major tagged version (v1.6 to v1.7, then v1.7 to v1.8, and so on) until you reach v1.22. From there, you can jump directly to the latest version.
|