Write the deployment guide and an example Caddy config (closes #89)
check / check (push) Failing after 2s
check / check (push) Failing after 2s
README.md gains a "Deployment" section: what the reverse proxy in front of pixa must do (terminate TLS, pass Host, Origin and Referer on unchanged, set X-Forwarded-For with trusted_proxies to match, wait at least downstream_timeout, optionally refuse /metrics) and what pixa does itself; that the state directory needs a persistent volume, what cache_max_bytes counts and why to set it; the health check for a load balancer; what SIGTERM does and the exit codes; and what running outside Docker needs. configs/Caddyfile is the example, as Caddy needs no settings beyond the host name and pixa's address. Model: opus-5-5
This commit was merged in pull request #182.
This commit is contained in:
@@ -34,6 +34,65 @@ else has a built-in default. A config file mounted at `/etc/pixa/config.yml`
|
||||
is optional: it is read when present, and an environment variable wins over
|
||||
the same setting in it.
|
||||
|
||||
## Deployment
|
||||
|
||||
pixa listens on plain HTTP and runs behind a reverse proxy that terminates TLS.
|
||||
[`configs/Caddyfile`](configs/Caddyfile) is an example for Caddy, chosen because
|
||||
it is the smallest correct one: Caddy gets the TLS certificate itself and does
|
||||
everything in this list without further settings. The reverse proxy must:
|
||||
|
||||
- terminate TLS, as the login and generator pages work only over HTTPS (see
|
||||
Routes);
|
||||
- pass the `Host`, `Origin` and `Referer` headers on unchanged, as pixa refuses
|
||||
a form from those pages unless `Origin` or `Referer` names the host in `Host`,
|
||||
and builds encrypted URLs from `Host`;
|
||||
- set `X-Forwarded-For` to the client's address, with `trusted_proxies` set to
|
||||
the address pixa sees the proxy's requests come from, so the login limit
|
||||
counts each client by its own address (see `trusted_proxies` under
|
||||
Configuration);
|
||||
- wait for pixa's answer for at least `downstream_timeout` (default `60s`), the
|
||||
longest pixa takes to fetch, convert and send an image.
|
||||
|
||||
It may also refuse `/metrics`, as the example does, so that only a scraper that
|
||||
reaches pixa directly can read it; pixa itself asks for the metrics username and
|
||||
password there.
|
||||
|
||||
pixa does the rest itself: it checks signatures and encrypted URLs, applies the
|
||||
allowlist, refuses upstream hosts with private or local addresses, limits login
|
||||
attempts, upstream response size and image dimensions, and sends the security
|
||||
headers, `Strict-Transport-Security` included, with every response.
|
||||
|
||||
The state directory (`state_dir`, `/var/lib/pixa` in the container) holds the
|
||||
database and the disk cache:
|
||||
|
||||
- It needs a persistent volume: without one, every restart starts with an empty
|
||||
cache. In the container, the startup script gives the directory to the user
|
||||
pixa runs as (uid 65532) and sets its mode to `750`; outside it, that user
|
||||
must be able to write the directory.
|
||||
- `cache_max_bytes` limits the source and transformed images together. The
|
||||
database, the metadata files, the `.meta` file beside each transformed image
|
||||
and files still being written come on top, and eviction runs in the
|
||||
background, so the cache can pass the limit for a while: leave room on the
|
||||
volume beyond it.
|
||||
- Set `cache_max_bytes` for a lasting deployment. Its default is 75% of the
|
||||
space free when pixa starts, which the cache's own files reduce, so a fuller
|
||||
cache gives a smaller limit after a restart.
|
||||
|
||||
A load balancer's health check can request `/.well-known/healthcheck.json`,
|
||||
which answers 200 whenever pixa is running, in maintenance mode too (see
|
||||
`maintenance_mode`).
|
||||
|
||||
On SIGTERM or SIGINT pixa stops accepting connections, gives the requests in
|
||||
progress and the images being processed 5 seconds to finish, and exits: with 0,
|
||||
or with 1 when images were still being processed after those 5 seconds or
|
||||
another part of pixa failed to stop. A request not finished by then is cut off.
|
||||
`docker stop` waits 10 seconds before it kills the container.
|
||||
|
||||
Outside Docker, pixa needs libvips (the image has 8.15) and libheif to run, as
|
||||
it uses libvips through CGO; building it also needs their development files,
|
||||
`pkg-config` and a C compiler. `script/bootstrap` installs all of these with
|
||||
nix, apt, brew or apk.
|
||||
|
||||
## Running under upaas
|
||||
|
||||
What the [upaas](https://git.eeqj.de/sneak/upaas) app for pixa needs:
|
||||
|
||||
@@ -29,6 +29,15 @@ P2: security: referer blacklist
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-10-04 deployment guide and example Caddy config (closes #89):
|
||||
"Deployment" in `README.md` says what the reverse proxy in front of pixa must
|
||||
do (terminate TLS; pass `Host`, `Origin` and `Referer` on unchanged; set
|
||||
`X-Forwarded-For`, with `trusted_proxies` to match; wait at least
|
||||
`downstream_timeout`; optionally refuse `/metrics`) and what pixa does itself,
|
||||
that the state directory needs a persistent volume and what `cache_max_bytes`
|
||||
counts, the health check for a load balancer, what a stop does and its exit
|
||||
codes, and what running outside Docker needs; `configs/Caddyfile` is the
|
||||
example, checked with `caddy validate`.
|
||||
- 2026-10-04 the metrics basic auth, CORS preflight, request logging and
|
||||
metrics recording have tests (closes #79): `MetricsAuth` on its own answers
|
||||
401 with a challenge without credentials or with a wrong username or password
|
||||
@@ -485,6 +494,3 @@ P2: security: referer blacklist
|
||||
- Prometheus performance metrics
|
||||
- integration tests for the image proxy flow
|
||||
- load tests to verify the 1k to 5k req/s target
|
||||
- P2: documentation
|
||||
- deployment guide
|
||||
- example nginx or caddy reverse proxy config
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Example Caddy config for running pixa behind Caddy; see "Deployment" in
|
||||
# README.md. Replace images.example.com with pixa's public host name, and
|
||||
# 127.0.0.1:8080 with the address Caddy reaches pixa on.
|
||||
#
|
||||
# Caddy gets and renews the TLS certificate for the host name, passes the
|
||||
# Host, Origin and Referer headers on unchanged, sets X-Forwarded-For to the
|
||||
# client's address, and waits for pixa's answer with no time limit of its
|
||||
# own, so pixa's downstream_timeout is what ends a slow request.
|
||||
|
||||
images.example.com
|
||||
|
||||
# pixa asks for metrics.username and metrics.password on /metrics. This
|
||||
# line also keeps it off the public address, for a scraper that reaches
|
||||
# pixa directly; remove it to read /metrics through Caddy.
|
||||
respond /metrics 404
|
||||
|
||||
reverse_proxy 127.0.0.1:8080
|
||||
Reference in New Issue
Block a user