truss-image 0.24.0

Image toolkit with a shared Rust core across the CLI, HTTP server, and WASM demo.
Documentation
# Deployment Guide

This page covers Docker setup, prebuilt binaries, cloud storage backends, and production deployment considerations for truss.

## Prebuilt Binaries

Download a prebuilt binary from the [GitHub Releases](https://github.com/nao1215/truss/releases) page. Archives and SHA256 checksums are published for each release, along with `release-manifest.json`; see [Downloading release binaries from a program](../README.md#downloading-release-binaries-from-a-program) for reading it from code.

### Homebrew

Homebrew installs are published to [`nao1215/tap`](https://github.com/nao1215/homebrew-tap):

```sh
brew install nao1215/tap/truss
```

| Target | Archive |
|--------|---------|
| Linux x86_64 | `truss-v*-x86_64-unknown-linux-gnu.tar.gz` |
| Linux aarch64 | `truss-v*-aarch64-unknown-linux-gnu.tar.gz` |
| macOS x86_64 | `truss-v*-x86_64-apple-darwin.tar.gz` |
| macOS aarch64 (Apple Silicon) | `truss-v*-aarch64-apple-darwin.tar.gz` |
| Windows x86_64 | `truss-v*-x86_64-pc-windows-msvc.zip` |

Example (Linux x86_64):

```sh
tar xzf truss-v*.tar.gz
sudo mv truss /usr/local/bin/
```

## Docker

### Docker Compose

```sh
docker compose up
```

This starts the server from `compose.yml`. The default configuration mounts `./images` as the storage root.

### Building and Running Directly

```sh
docker build -t truss .
docker run -p 8080:8080 \
  -e TRUSS_BIND_ADDR=0.0.0.0:8080 \
  -e TRUSS_BEARER_TOKEN=changeme \
  -v ./images:/data:ro \
  -e TRUSS_STORAGE_ROOT=/data \
  truss
```

### Prebuilt Container Images

Prebuilt container images are published to GHCR:

```sh
docker pull ghcr.io/nao1215/truss:latest
```

## Storage Backends

truss supports multiple storage backends. The backend is selected via `TRUSS_STORAGE_BACKEND`. Only one backend can be active at a time. See the [Configuration Reference](configuration.md) for all storage-related environment variables.

### Installing with Storage Backend Support

```sh
# S3
cargo install truss-image --features s3

# Google Cloud Storage
cargo install truss-image --features gcs

# Azure Blob Storage
cargo install truss-image --features azure

# All storage backends
cargo install truss-image --features "s3,gcs,azure"
```

### S3

Set `TRUSS_STORAGE_BACKEND=s3` and configure:

| Variable | Description |
|------|------|
| `TRUSS_S3_BUCKET` | Default S3 bucket name (required) |
| `TRUSS_S3_FORCE_PATH_STYLE` | Use path-style addressing (`true`/`1`; required for MinIO, LocalStack, etc.) |
| `AWS_REGION` | AWS region (e.g. `us-east-1`) |
| `AWS_ACCESS_KEY_ID` | AWS access key |
| `AWS_SECRET_ACCESS_KEY` | AWS secret key |
| `AWS_ENDPOINT_URL` | Custom S3-compatible endpoint (e.g. `http://minio:9000`) |

### GCS

Set `TRUSS_STORAGE_BACKEND=gcs` and configure:

| Variable | Description |
|------|------|
| `TRUSS_GCS_BUCKET` | Default GCS bucket name (required) |
| `TRUSS_GCS_ENDPOINT` | Custom endpoint (e.g. `http://fake-gcs:4443`) |
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to service account JSON key file |
| `GOOGLE_APPLICATION_CREDENTIALS_JSON` | Inline service account JSON (alternative to file path) |

### Azure Blob Storage

Set `TRUSS_STORAGE_BACKEND=azure` and configure:

| Variable | Description |
|------|------|
| `TRUSS_AZURE_CONTAINER` | Default container name (required) |
| `TRUSS_AZURE_ENDPOINT` | Custom endpoint (e.g. `http://azurite:10000/devstoreaccount1`) |
| `AZURE_STORAGE_ACCOUNT_NAME` | Storage account name (3-24 lowercase alphanumeric) |

By default, truss uses anonymous access, which works for public containers and Azurite local development. For private containers, append a SAS token to `TRUSS_AZURE_ENDPOINT`. On Azure-hosted compute (App Service, AKS, VMs), managed identity is used automatically when no explicit credentials are provided.

## CDN / Reverse-Proxy Integration

In production, place a CDN such as CloudFront (or a reverse proxy like nginx / Envoy) in front of truss so that transformed images are cached at the edge. See the [API Reference](api-reference.md#cdn--reverse-proxy-integration) for detailed CDN configuration guidance, including cache key setup and public vs. private endpoint visibility.

## Graceful Shutdown

truss supports graceful shutdown for zero-downtime deployments. The `TRUSS_SHUTDOWN_DRAIN_SECS` variable controls the drain period (default: 10 seconds). During this period the server keeps accepting and answering requests, and `/health/ready` returns 503 to every probe so that load balancers stop sending new traffic while in-flight requests complete. The listener is closed as soon as the drain period ends, before in-flight requests are given their remaining time, so a connection attempted after it is refused rather than left waiting.

On Kubernetes, set `terminationGracePeriodSeconds` >= drain + 20 (e.g. `35` for the default 10 s drain).

The drain is started by `SIGTERM` or `SIGINT` on Unix. On Windows it is started by a console control event, which is what the operating system delivers in place of the signals it does not raise: Ctrl+C, Ctrl+Break, the console window being closed, the user logging off, and the system shutting down. Windows still never raises `SIGTERM`, so nothing depends on it. The last three of those five are terminating events, where the operating system kills the process as soon as the handler returns and allows only a few seconds before it stops asking — about five, set by the `HungAppTimeout` and `WaitToKillServiceTimeout` registry values — so truss holds the process open for up to four seconds waiting for the drain and the drain there is best effort. Ctrl+C and Ctrl+Break are not terminating, so the full `TRUSS_SHUTDOWN_DRAIN_SECS` window applies to them as it does on Unix. The published container image is Linux.

## Rate Limiting

Per-client rate limiting is off by default. Set `TRUSS_RATE_LIMIT_RPS` to a positive value to enable it; each client IP then gets a token bucket refilling at that rate, with a capacity of `TRUSS_RATE_LIMIT_BURST` (defaulting to the same value), and requests that find an empty bucket receive 429.

Behind a CDN or reverse proxy every connection arrives from the proxy, so without further configuration all traffic shares one bucket and the limit applies to the whole site at once rather than per client. Set `TRUSS_TRUSTED_PROXIES` to the proxy addresses or CIDR ranges to fix this: when a connection comes from one of them, the client IP is taken from `X-Forwarded-For` (rightmost entry that is not itself a trusted proxy) or `X-Real-IP`. Only list addresses you control, because trusting an address means trusting whatever it puts in those headers.

## Health Checks

| Endpoint | Purpose |
|----------|---------|
| `GET, HEAD /health/live` | Liveness probe (always returns 200) |
| `GET, HEAD /health/ready` | Readiness probe (returns 503 when draining, when the storage or cache root is unreachable, when the cache disk is below its minimum free bytes, or when RSS is over its limit). Transform capacity is reported in the `transformCapacity` check but does not withdraw readiness: a server with every slot busy is answering what it accepted, and a request that finds no free slot is shed on its own with 503 and a `Retry-After` |

Configure health thresholds with:
- `TRUSS_HEALTH_CACHE_MIN_FREE_BYTES` -- minimum free bytes on cache disk
- `TRUSS_HEALTH_MAX_MEMORY_BYTES` -- maximum process RSS (Linux only)