> Solana Developer Platform docs, served as Markdown. The [docs index](https://platform-beta.solana.com/docs.md) lists every page and [llms.txt](https://platform-beta.solana.com/docs/ai/llms.txt) the key ones; [SKILL.md](https://platform-beta.solana.com/SKILL.md) has the rules for building on SDP.

# Upgrade & Backup

Move a self-hosted Solana Developer Platform to a new release and protect its data.

Upgrade to a new release, roll back, and back up the data that matters.

## Upgrade

A release ships new images and an updated `compose.yml`.

1. **Refresh the install files.** Re-run the install script. It replaces `compose.yml` with the new release's, verified against its checksums. It never touches `.env`, keeps an existing `.env.example`, and prints a link to the new release's template so you can check for new variables.

   ```bash title="Terminal"
   curl -fsSL https://github.com/solana-foundation/solana-developer-platform/releases/latest/download/install.sh | bash
   ```

   To install a specific release instead of the latest, set `INSTALL_VERSION` to its tag:

   ```bash title="Terminal"
   curl -fsSL https://github.com/solana-foundation/solana-developer-platform/releases/latest/download/install.sh | INSTALL_VERSION=vX.Y.Z bash
   ```

2. **Pin the version.** Set `SDP_VERSION` in `.env` to the same release tag, not `latest`, so every service runs one known build.

3. **Pull and restart.**

   ```bash title="Terminal"
   cd ~/sdp
   docker compose pull
   docker compose up -d
   ```

   `sdp-migrate` applies any new database migrations before the API starts.

4. **Verify** that the services are healthy, as in the [Quickstart](/docs/self-hosting/quickstart#4-verify).

### Roll back

To run the previous release again, re-run the install script with its tag in `INSTALL_VERSION`, set `SDP_VERSION` to that tag, and run `docker compose up -d`.

Migrations don't reverse. If the newer release applied any, restore a backup taken before the upgrade instead of only switching versions.

## Backup

Two things hold your state:

- **The Postgres database:** all platform data. The bundled database lives in the `sdp-postgres-data` volume (`sdp_sdp-postgres-data` in `docker volume ls`). Back up an external `DATABASE_URL` with your database provider's tools.
- **Your `.env`:** configuration and secrets you can't regenerate. Without the same `API_KEY_PEPPER`, no existing API key matches. Without the same `CUSTODY_ENCRYPTION_KEY`, SDP can't decrypt the custody secrets it stored, such as local wallet keys. Keep the file in a secret manager.

Redis is a cache and doesn't need a backup.

### Back up the database

Take a logical dump with `pg_dump` inside the running container. Running it through `sh -c` lets it read `POSTGRES_USER` and `POSTGRES_DB` there, so custom values work:

```bash title="Terminal"
cd ~/sdp
docker compose exec -T postgres \
  sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > sdp-backup.sql
```

Keep dumps off the host, on a schedule that fits your recovery needs.

### Restore the database

A plain `pg_dump` holds the full schema and the migration history, so restore it into an **empty** database and let the stack start on top. Restoring over an already migrated database collides with the schema `sdp-migrate` created.

```bash title="Terminal"
cd ~/sdp
docker compose down                       # stop every service
docker volume rm sdp_sdp-postgres-data    # discard the current database (destructive)
docker compose up -d postgres             # start a fresh, empty Postgres
until docker compose exec -T postgres \
  sh -c 'pg_isready -U "$POSTGRES_USER"' >/dev/null 2>&1; do sleep 1; done
docker compose exec -T postgres \
  sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"' < sdp-backup.sql
docker compose up -d                      # start the rest
```

On that final `up`, `sdp-migrate` skips every migration the dump already records, so an older dump gets only the newer migrations.

Restore the matching `.env` too, so secrets such as `CUSTODY_ENCRYPTION_KEY` match the data they protect. Restore an external `DATABASE_URL` through your database provider instead of the bundled `postgres` service.
