# 🧹 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+` (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.