# How to Upgrade a Self-Hosted Specivo Instance

Upgrading Specivo means moving to a newer `specivo/specivo` Docker image. The container entrypoint applies any new database migrations on startup automatically, so the process is straightforward: back up, pin the target version, pull, restart, and verify. This guide walks through an upgrade from 0.1.9 to 0.1.10 performed on the same kind of 1 vCPU / 2 GB Ubuntu 24.04 droplet used in the install guide.

The working directory throughout is `/opt/specivo`, which is where the install guide puts the Compose file, `.env`, and the `specivo-data/` directory.

## What you'll do

- Confirm the stack is healthy before touching anything
- Take a database dump and archive the data directory
- Pin the target version in `.env`
- Pull the new image and restart the stack
- Verify everything came up on the new version

## Before you start

You need an existing Specivo install (the setup produced by the install guide), shell access as root, and a few minutes. Before you pull anything, read the release notes for the version you're moving to — the CHANGELOG is in the repository and linked from each GitHub release. Know what changed before you run the upgrade.

## Step 1 — Check what you're running now

SSH into the server and switch to the install directory:

```bash
cd /opt/specivo
```

Confirm all containers are healthy:

```bash
docker compose ps
```

You should see six containers — `nginx`, `api`, `db`, `redis`, `celery-worker`, and `celery-beat` — all reporting `Up (healthy)`. If any container is restarting or unhealthy, resolve that before proceeding.

Check the current schema revision:

```bash
docker compose exec -T api alembic current
```

Note the output. In this walkthrough it was `0020 (head)`. You'll check this again after the upgrade.

Check disk space. The pull will download a new image layer and the backup archive will sit on disk until you move it off the host:

```bash
df -h /
```

The droplet used here showed 43 GB free on a 48 GB disk — comfortably enough for the pull plus a backup archive that landed at about 280 MB.

## Step 2 — Back up first

> **This step is mandatory every time you upgrade.** Migrations can change the database schema and are not always reversible. If an upgrade goes wrong after a migration has already run, your backup is the only way back. Do not skip it.

Create a directory to hold backups if you don't have one already:

```bash
mkdir -p /opt/specivo-backups
```

Dump the database. The `-T` flag is required to pipe output correctly without allocating a pseudo-TTY:

```bash
docker compose exec -T db pg_dump -U specivo specivo \
  > /opt/specivo-backups/backup-$(date +%Y%m%d-%H%M%S).sql
```

The dump produced here was about 210 KB — a complete plain-SQL snapshot of every issue, wiki page, comment, and history record.

Archive the data directory. This captures attachments, logs, themes, the bundled database files, and the embedding model on disk:

```bash
tar czf /opt/specivo-backups/specivo-data-$(date +%Y%m%d-%H%M%S).tar.gz specivo-data
```

The archive here was about 279 MB, most of which is the ~400 MB embedding model already compressed on disk plus the PostgreSQL data files.

Move both files off the host — to object storage, an external drive, or a remote server — before continuing. A backup that lives only on the same machine you're upgrading is not a real backup.

## Step 3 — Pin the target version

Open `.env` in your editor and change `SPECIVO_VERSION` from the current value to the release you want:

```bash
SPECIVO_VERSION=0.1.10
```

If your `.env` still has `SPECIVO_VERSION=latest` from the initial install, this is the right moment to switch. Pinning an explicit tag means you decide when each upgrade happens, and you always know exactly what image is running. Rolling back is equally simple: re-pin the old tag.

## Step 4 — Pull the new image

```bash
docker compose pull
```

Compose fetches only the layers that changed. The pull ends with a line like:

```
Image specivo/specivo:0.1.10 Pulled
```

The `db` (pgvector), `redis`, and `nginx` images are not part of the Specivo release cycle, so unless you changed their pinned versions they won't be re-pulled here.

## Step 5 — Restart the stack

```bash
docker compose up -d
```

Compose compares the running containers against the Compose file and recreates only the ones whose image changed. In this upgrade that was three containers: `specivo-api-1`, `specivo-celery-worker-1`, and `specivo-celery-beat-1`. The `db`, `redis`, and `nginx` containers were left untouched because their images didn't change.

Compose waited for `db` and `redis` to report healthy before starting `api` — you don't need to manage that ordering yourself.

**Migrations run automatically.** The entrypoint runs `alembic upgrade head` on every startup. You will see lines like this in the API log:

```
Running database migrations...
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
```

When a release does include schema changes, you'll also see `Running upgrade <rev> -> <rev>` lines between those — alembic applying each new migration in order.

You never run a migrate command by hand. Watch the log to confirm a clean start:

```bash
docker compose logs -f api
```

Press `Ctrl-C` to stop following when you see `Application startup complete.`

One thing to be aware of: this particular hop from 0.1.9 to 0.1.10 added no new schema migration — `alembic current` was `0020 (head)` both before and after. The entrypoint still ran the migration step, which is idempotent. Other version hops may include schema migrations, which is exactly why the pre-upgrade backup is mandatory every time regardless of whether you expect schema changes.

## Step 6 — Verify

Confirm all containers are on the new image:

```bash
docker ps
```

The `api`, `celery-worker`, and `celery-beat` rows should all show `specivo/specivo:0.1.10`. The `api` container should report `Up (healthy)`.

Check that the login page responds:

```bash
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9933/
```

A `302` redirect to the sign-in page is the expected healthy response.

Verify the schema revision is still consistent:

```bash
docker compose exec -T api alembic current
```

Finally, open the app in a browser, sign in, open an issue, and run a search. If all three work, the upgrade is done.

## Rolling back

If the new version misbehaves, roll back by re-pinning the previous tag in `.env`:

```bash
SPECIVO_VERSION=0.1.9
```

Then pull and restart:

```bash
docker compose pull
docker compose up -d
```

> **If the failed upgrade already applied a schema migration, re-pinning alone is not enough.** The database schema may be ahead of the older image, and the older image will refuse to start or behave incorrectly. In that case you must restore the pre-upgrade database dump and the `specivo-data/` archive, and then start the older image. Restore the data directory first, then load the SQL dump into the database:
>
> ```bash
> docker compose down
> tar xzf /opt/specivo-backups/specivo-data-<timestamp>.tar.gz
> docker compose up -d db
> docker compose exec -T db psql -U specivo specivo \
>   < /opt/specivo-backups/backup-<timestamp>.sql
> docker compose up -d
> ```
>
> This is exactly why the pre-upgrade backup is mandatory every time.

## What's new in 0.1.10

Before upgrading, always read the CHANGELOG for the release you're moving to. The highlights in 0.1.10:

- **Multi-language UI.** Russian, Chinese, French, Spanish, and Thai are now supported. A workspace-level default language can be set by an admin, and each user can override it in their profile settings.
- **Markdown editor.** Issue descriptions and wiki pages now use EasyMDE, with a toolbar and a live preview. A new preview endpoint backs the in-editor preview rendering.
- **Unified edit for title and description.** A single button opens both fields together, replacing the previous separate edit flows.
- **New MCP tooling.** Metadata-schema management tools are now exposed over MCP. The `list_issues` tool accepts a `metadata key=value` filter, making it practical to query issues by structured metadata from an AI assistant.
- **Bug fixes.** Content-hashed CSS and JS filenames bust stale browser caches after upgrades. Celery task registration was fixed. Markdown editor stability improvements.

## Good habits

- Upgrade one minor version at a time rather than jumping several releases at once. The migration sequence is designed to run incrementally.
- Read the release notes for every version before you pull.
- Back up every time, even for releases that appear to be patch-only.
- Keep `SPECIVO_VERSION` pinned to an explicit tag in `.env`. Never run a production instance on `latest`.
- After every upgrade, do a quick smoke test: sign in, open an issue, run a search.