## Deploy Runbook (Docker-Only Host)

This runbook is for a minimal production host where **Docker is installed** and **Node.js is not**.

The deployment model is:
- CI builds and publishes a Docker image containing the built static site
- the server updates by pulling that image and restarting the service

Build-time config (examples):
- `PUBLIC_ENABLE_SW=true|false` toggles service worker registration in the generated HTML (defaults to `true` in the Docker build).

### Prerequisites

- Docker + Docker Compose plugin available on the host
- Registry access (e.g., logged in to GHCR if the image is private)
- A `WEB_IMAGE` value pointing at the image to deploy (tag or digest)

Example:

```bash
export WEB_IMAGE=ghcr.io/<owner>/<repo>:latest
```

### First-Time Start

```bash
docker compose -f deploy/docker-compose.prod.yml up -d
```

### Refresh (Pull + Restart)

Pull first (safe; does not affect the running container):

```bash
docker compose -f deploy/docker-compose.prod.yml pull
```

Then restart the service on the newly pulled image:

```bash
docker compose -f deploy/docker-compose.prod.yml up -d --no-build
```

### Verify Deployed Version

1. Check the container's image reference (tag/digest):

```bash
docker compose -f deploy/docker-compose.prod.yml ps
docker inspect --format '{{.Image}} {{.Config.Image}}' <container-id>
```

2. Check build metadata served by the site:

```bash
curl -fsS http://localhost:8080/build.json
```

### Rollback

Re-deploy a known-good version by pinning a previous tag or digest:

```bash
export WEB_IMAGE=ghcr.io/<owner>/<repo>:<known-good-tag>
docker compose -f deploy/docker-compose.prod.yml up -d --no-build
```

Recommended: record the image digest for each release (`docker inspect <image> --format '{{.Id}}'`), and use a digest pin for true immutability.

### Failure Mode Validation (Pull Failure)

If `docker compose pull` fails, **do not run** the restart step. The running site will continue serving the existing container.

To simulate a pull failure safely:

```bash
export WEB_IMAGE=ghcr.io/<owner>/<repo>:this-tag-does-not-exist
docker compose -f deploy/docker-compose.prod.yml pull
```

The pull should fail, but the current service should still be running:

```bash
docker compose -f deploy/docker-compose.prod.yml ps
curl -fsS http://localhost:8080/ > /dev/null
```
