# Upgrading (/en/docs/operations/upgrade)

Upgrade commands for each installation method, the database migrations that run automatically at startup, and what to watch out for when rolling back.

## Before upgrading [#before-upgrading]

1. Read the [release notes](https://github.com/x-haose/imgyoung/releases) of the target version.
2. Keep a copy of the metadata database to roll back to: in the admin console, go to **Settings → Backups & export → Metadata snapshots**, click **Back up now**, then **Download** that snapshot and store it off the server. Alternatively, stop the service and copy `<data dir>/imgyoung.db` (only consistent once stopped).
3. Make sure `master.key` is backed up; see [Configuration](../configuration.mdx#master-key).

Check the running version with `imgyoung -version`, or under **Server version** in **Settings → Runtime settings → Bootstrap configuration**.

## Upgrade commands [#upgrade-commands]

<Tabs items={['Install script', 'Docker', 'Compose', 'Binary', 'Source']}>
<Tab value="Install script">

Rerunning the install script upgrades to the latest stable release; add `--version X.Y.Z` for a specific one.

```bash title="Terminal"
# Running as a systemd service (installed with --service): replaces the binary, rewrites the unit and restarts the service
curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sudo sh -s -- --service

# Managing the process yourself: only replaces the binary; restart imgyoung afterwards
curl -fsSL https://raw.githubusercontent.com/x-haose/imgyoung/main/install.sh | sh
```

`--service` overwrites `/etc/systemd/system/imgyoung.service` with `deploy/imgyoung.service` from the release archive; keep your own environment variables in a drop-in created with `systemctl edit`, which is left alone.

</Tab>
<Tab value="Docker">

Pull the new image, remove the old container, and create a new one with the **same volume** and your usual options. Removing a container does not remove its named volume. If you pass the master key with `-e IMGYOUNG_MASTER_KEY=…`, do not leave it out when recreating the container: the database records a fingerprint of the master key, and the process refuses to start without it.

```bash title="Terminal"
docker pull ghcr.io/x-haose/imgyoung:latest
docker stop imgyoung && docker rm imgyoung
docker run -d --name imgyoung --restart unless-stopped \
  -p 8721:8721 -v imgyoung-data:/data \
  ghcr.io/x-haose/imgyoung:latest
```

Image tags: `latest` follows the newest stable release; a major.minor tag such as `1.0` follows the newest patch of that series; a full version such as `1.0.3` never moves. Pre-releases only get the full version tag.

</Tab>
<Tab value="Compose">

```bash title="Terminal"
docker compose pull
docker compose up -d
```

If `compose.yaml` pins a major.minor or full version, change the `image:` tag first to move to another series.

</Tab>
<Tab value="Binary">

Download the new archive for your platform from the [releases page](https://github.com/x-haose/imgyoung/releases), verify it against `checksums.txt` from the same release, extract it, replace the old `imgyoung` binary and restart the process. The data directory stays as it is.

```bash title="Terminal"
sha256sum -c checksums.txt --ignore-missing
tar -xzf imgyoung_<version>_linux_amd64.tar.gz
```

</Tab>
<Tab value="Source">

```bash title="Terminal"
git fetch --tags
git checkout vX.Y.Z
task build
```

Replace the running binary with the newly built `./imgyoung` and restart.

</Tab>
</Tabs>

When the old process is stopped, ImgYoung first stops accepting new requests and waits up to 6 seconds for in-flight requests (cutting them off after that), then stops the background loops and in-flight replication jobs, waiting up to 2 more seconds, and finally closes the database and exits. Interrupted replication jobs are queued again by reconciliation on the next start. Docker's default 10-second stop timeout and the `stop_grace_period: 15s` (Compose) and `TimeoutStopSec=15s` (systemd) in the `deploy/` examples are all enough.

## Database migrations [#database-migrations]

Migrations are embedded in the binary and run automatically on every start, right after the database is opened:

- the migration files are first checked against their checksum list, then every migration not yet applied runs in version order; when skipping several versions, all intermediate migrations are applied in turn. Applied versions are recorded in the `schema_revisions` table.
- the replication subsystem's job queue tables are migrated at startup as well.
- runtime settings added by a migration are filled in with their factory defaults; existing values are untouched.
- if the database records a migration version this binary does not know (it was migrated by a newer version), startup is refused; see [Rolling back](#rolling-back) below.
- if a migration fails, the process exits with an error naming the failing migration file. Roll back using your pre-upgrade snapshot (see below).

There is no separate migration command, and none is needed.

## Rolling back [#rolling-back]

Downgrades are not supported: migrations only go forward, and some of them drop columns or rebuild tables. An older binary refuses to start on a database migrated by a newer version; the error names the newest migration version in the database and the newest one the binary knows.

Rolling back therefore takes exactly one thing: **the older binary plus your pre-upgrade snapshot**. Without a pre-upgrade snapshot, you have to stay on the new version.

<Steps>
<Step>

Stop the service.

</Step>
<Step>

Switch back to the older binary or image tag.

</Step>
<Step>

Put your pre-upgrade snapshot (or the database copied while the service was stopped) in place as `<data dir>/imgyoung.db.restore`; do not overwrite `imgyoung.db` with it. At startup it is swapped in, the database migrated by the newer version is kept as `imgyoung.db.pre-restore-<UTC time>`, and the channels' binding times are reset. For Docker the file must be owned by `65532:65532`; use the throwaway-container approach shown in [Backup & restore](./backup.mdx).

</Step>
<Step>

Start the service.

</Step>
</Steps>

Metadata written since the upgrade is lost, with the same effects as restoring a snapshot: images uploaded in the meantime have no record in the metadata, so their objects on the channels are only counted, never deleted, but can no longer be reached through ImgYoung. See [Backup & restore](./backup.mdx).
