# 🧹 Upgrading and removing

Version **0.2.0** changes how existing configurations route, bind, compress,
and identify clients. These changes also apply when upgrading from
**0.2.0-rc.N**. The patch releases 0.2.1 and 0.2.2 change no configuration.
Read the complete
[Before you upgrade list](https://github.com/dorianverlaine/pingclair/blob/main/CHANGELOG.md#️-before-you-upgrade)
and its linked entries before replacing a binary. The summary below covers the
changes most likely to affect a deployment.

## ⚠️ What changes in 0.2.0

| Review | What to change or expect |
| --- | --- |
| Routing | Directive order decides the first matching route. A catch-all `redir`, `route`, or `handle` can precede a specific `respond`. Use exclusive `handle` blocks or explicit `route` order. |
| Paths | ASCII case is ignored and escapes are decoded once. `handle_path` and URI stripping also ignore case. Braces are literal; use `path_regexp` for captures and case-sensitive routing. Equal-length sibling paths retain file order. |
| Block matchers | `handle`, `handle_path`, and `route` accept only `*`, `/path`, or `@name`. Replace `handle *.php` with a named `path *.php` matcher. |
| Listeners | `bind` applies even with explicit addresses and ports, including the automatic redirect. `listen` retains its IP address. `bind` and `default_bind` allow one address. Sites on one port share a listener; a bind-restricted site beside a wildcard listener is refused. |
| Addresses | A named site with a port and no scheme uses HTTPS. Write `http://` for plaintext. Scheme-only addresses use global ports. `[::1]` names a site, and `0.0.0.0` with `[::]` on one port folds to IPv6. |
| Client identity | In `servers`, write `trusted_proxies static …` once per scope. List `CF-Connecting-IP` in `client_ip_headers` when needed. Use `client_ip` and `{client_ip}` for the forwarded client; `remote_ip` and `{remote_host}` mean the peer. `{remote_ip}` is refused. |
| Request limits | There is no default body-size ceiling. Set `request_body { max_size … }` if needed. Header completion and pauses between body reads default to 60 seconds; uploads with long pauses may need a longer `limits { body_timeout … }`. |
| Error pages | `handle_errors` now handles gateway, timeout, and `413` errors too. Its `root` and `file_server` serve the page with the error status. Restrict a catch-all block by status if appropriate. |
| Encoding | No compression without `encode`. Blocks, gzip levels (default 5), response matchers, and minimum length take effect. `encode off` cannot have a block. Static responses always vary by encoding; proxy responses do so on encode sites. Re-encoding weakens proxy ETags; static and sidecar validators change. `no-transform` disables encoding. |
| Response cache | Freshness includes upstream age. All caching routes must agree on `max_size`; reload applies budget changes. `flush_interval -1` bypasses admission. Every `Vary` line participates in variants; invalid `Vary` and `Vary: *` prevent storage. |
| Admin API | Missing config reads return `200 null`. Reads mask secrets, so restore them before loading an export. Config writes honor path-qualified `If-Match`; after `412`, read the path and its ETag again. Reads remain available through reload. |
| Metrics | Add global `metrics` to collect. Scrapes are empty without it. Update dashboards to the [renamed `caddy_*` families](/reference/admin-api/#-metrics). |
| CLI | Use `--config`/`-c` and `--adapter caddyfile\|json`; do not also supply a positional path. With no default file, `run` starts admin-only. Give an explicit path if absence must fail. `validate` now parses TLS files and checks key pairs. |
| TLS | The internal CA moves to `pki/authorities/local/` without migration: trust the new root again. Exact names no longer inherit wildcard `client_auth`; add an explicit block if required. Manual TLS requires a named site. |
| Upstreams | Weight 0 drains, weights above 100 and all-zero primary pools fail validation. `lb_try_duration` bounds new attempts, not an active response. Retries after delivery are limited to idempotent methods. |
| Protocols | CONNECT receives `405` and closes H1; a target without a usable port receives `400`. Malformed HTTP/1 targets and chunked bodies receive `400` and close. FastCGI HEAD has no body, oversized parameters receive `431`, and malformed bodies abort the response. |
| Lifecycle | Occupied HTTP, admin, and H3 UDP ports prevent startup. SIGTERM drains within `grace_period`. Restart still has a connection gap; reload is preferable for policy changes. |

The [known release defects](/project/status/#-known-defects-in-022) still apply.
A `502` or `504` generated by the built-in proxy error path carries
`Proxy-Status`; a custom `handle_errors` response does not. A missed keepalive
reuse is logged at `DEBUG` rather than `ERROR`.

## 📦 Preserve the previous installation

Back up the configuration, the current binary, and the entire TLS store before
upgrading. Preserve their ownership and protect backups containing private
keys. The installer keeps `/etc/Pingclair/Pingclairfile`, the certificate store
at `/var/lib/pingclair/.local/share/pingclair`, and the site's files. It replaces
the binary, example configuration, and systemd unit, then restarts the service.
A configured `storage file_system` path takes precedence over the usual store.

Keep the old store backup for rollback: trusting the new internal root does not
make old clients trust it, and rolling back only the binary does not restore the
old root or configuration format.

## 🛡️ Check before switching

Run the **new** binary's `validate` against the production configuration while
it is still staged, before replacing the running binary. It opens no listeners,
but its user must be able to read every certificate and key the file names.
Then test the affected routes, case variants, forwarded client identity, error
pages, uploads, compression, and metrics in a staging deployment.

A source build of `main` reports `v0.0.0-dev+<sha>` (or `v0.0.0-dev` without a
checkout); a release binary reports its tag, such as `v0.2.2`. Check the tag and
published checksum when installing. A dev version string is not evidence that
the stable release is installed.

## ⬆️ Install the stable release

The installer selects the stable channel:

```bash
curl -fsSL https://pingclair.com/install.sh | sudo bash
pingclair version
pc service status
```

Confirm that `pingclair version` reports `v0.2.2`, the service is running, and
your routes respond as expected.

The installer has no version-pinning flag. For containers, pin the intended tag:

```yaml
services:
  pingclair:
    image: ghcr.io/dorianverlaine/pingclair:v0.2.2
```

```bash
docker compose pull
docker compose up -d
docker compose logs pingclair
```

Keep the configuration and TLS store volumes. `latest` follows stable releases;
alpha previews do not advance it. Host ports must be free before startup.

## ⏪ Roll back

Stop the new service, restore the backed-up binary, configuration, and TLS
store with their original ownership, then validate with the restored binary
before starting. A configuration written by 0.2.0 may not load in an earlier
version. Do not overwrite a running certificate store with a partial backup.

## 🧹 Remove the installation

```bash
sudo pc service stop
sudo systemctl disable pingclair
sudo rm /etc/systemd/system/pingclair.service
sudo systemctl daemon-reload
sudo rm /usr/local/bin/pingclair /usr/local/bin/pc
```

These commands preserve `/etc/Pingclair`, `/var/lib/pingclair`, and
`/var/log/pingclair`, including certificates and site files. Delete retained
data only when it is no longer needed for reinstall or rollback.

## 🧭 Next steps

- [Install](/start/install/): installation layout and prerequisites.
- [Run it as a service](/start/service/): reload, restart, and logs.
- [Project status](/project/status/): remaining release limitations.
