Write the deployment guide and an example Caddy config (closes #89) #182
@@ -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
|
is optional: it is read when present, and an environment variable wins over
|
||||||
the same setting in it.
|
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
|
## Running under upaas
|
||||||
|
|
||||||
What the [upaas](https://git.eeqj.de/sneak/upaas) app for pixa needs:
|
What the [upaas](https://git.eeqj.de/sneak/upaas) app for pixa needs:
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ P2: security: referer blacklist
|
|||||||
|
|
||||||
# Completed Steps
|
# 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
|
- 2026-10-04 the metrics basic auth, CORS preflight, request logging and
|
||||||
metrics recording have tests (closes #79): `MetricsAuth` on its own answers
|
metrics recording have tests (closes #79): `MetricsAuth` on its own answers
|
||||||
401 with a challenge without credentials or with a wrong username or password
|
401 with a challenge without credentials or with a wrong username or password
|
||||||
@@ -485,6 +494,3 @@ P2: security: referer blacklist
|
|||||||
- Prometheus performance metrics
|
- Prometheus performance metrics
|
||||||
- integration tests for the image proxy flow
|
- integration tests for the image proxy flow
|
||||||
- load tests to verify the 1k to 5k req/s target
|
- 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