Files
ngit-grasp/docs/how-to/deploy-docker.md
T
DanConwayDev 37d81f3713 feat(deploy): add portable container contract
Production deployment previously depended on an illustrative Docker snippet and did not define which state or identity must survive replacement.

Add a non-root runtime image, loopback-only Compose service, optional Caddy TLS overlay, shared /data layout, bounded public verifier, and an identity-persistence container test. Document backup, proxy, single-writer, upgrade, and rollback requirements as the contract for every environment.

This assumes one ngit-grasp writer per state directory and a reverse proxy or platform edge for public TLS. Image publication and provider-specific control-plane setup are deliberately left to separate changes.

Validated with sh -n, ShellCheck 0.11.0, locked Cargo metadata, YAML parsing, Docker Hub tag lookups, local input-failure checks, and git diff --check. Docker/Podman is unavailable in this VM, so the included end-to-end container test was not run here.
2026-08-20 19:38:04 +00:00

137 lines
3.9 KiB
Markdown

# Deploy with Docker or Podman
This is the portable default for a Linux host. The repository ships a
multi-stage `Dockerfile`, a loopback-only Compose service, and an optional
Caddy overlay for automatic HTTPS.
The image contains ngit-grasp, Git, CA certificates, and a small init process.
It prepares `/data` and then runs ngit-grasp as UID/GID `10001`.
## Prerequisites
- Docker Engine with Compose v2, or a compatible Podman Compose setup
- durable local storage for the named volume
- a domain whose DNS points to the host
- ports 80 and 443 available when using bundled Caddy
Review the [deployment contract](../reference/deployment-contract.md) before
placing the state volume on remote or managed storage.
## Fresh VPS with automatic HTTPS
Create the small deployment environment file:
```bash
cp deploy.env.example .env
```
Set `NGIT_DOMAIN` in `.env`, then start the relay and Caddy:
```bash
docker compose -f compose.yaml -f compose.caddy.yaml up --build -d
docker compose -f compose.yaml -f compose.caddy.yaml ps
scripts/verify-deployment.sh https://ngit.example.com
```
Caddy obtains and renews the certificate. The relay is also published on
`127.0.0.1:7334` for local diagnostics, but it is not directly reachable from
the network.
The bundled Caddy configuration serves ngit-grasp at the domain root. To share
a hostname using `NGIT_BASE_PATH`, use an existing reverse proxy and configure
its path routing explicitly.
## Existing reverse proxy
Start only the relay service:
```bash
docker compose up --build -d
curl -H 'Accept: application/nostr+json' http://127.0.0.1:7334
```
Proxy the public HTTPS hostname to `http://127.0.0.1:7334`. Preserve WebSocket
upgrades, request methods, bodies, and query strings. Forwarded client IP
headers are ignored by default; configure `NGIT_TRUSTED_PROXY_CIDRS` only after
identifying the exact container-visible proxy address and keeping the backend
private.
## State and identity
The `ngit-grasp-data` named volume is mounted at `/data` and contains all
durable state. The first start creates `/data/.relay-owner.nsec`; retaining the
volume retains the public identity advertised in NIP-11.
Inspect the resolved volume without guessing its Compose prefix:
```bash
docker volume inspect ngit-grasp_ngit-grasp-data
```
Do not use `docker compose down --volumes` in production. It deletes the relay
identity, events, and Git repositories.
## Operations
```bash
# Logs
docker compose logs --follow ngit-grasp
# Controlled restart
docker compose restart ngit-grasp
# Stop while retaining state
docker compose stop
# Start again
docker compose start
```
When the Caddy overlay is active, include both `-f` arguments for commands that
must operate on the complete project.
## Backup
Stop the writer, archive the complete volume, and start it again:
```bash
docker compose stop ngit-grasp
docker run --rm \
--volume ngit-grasp_ngit-grasp-data:/data:ro \
--volume "$PWD:/backup" \
alpine:3.22 \
tar -czf /backup/ngit-grasp-data.tar.gz -C /data .
docker compose start ngit-grasp
```
Store the archive away from the Docker host. Test restoration into a separate,
non-public deployment before relying on it.
## Upgrade
Pin or check out the intended tag, read `CHANGELOG.md`, take a backup, and
rebuild without overlapping the old and new writers:
```bash
docker compose stop ngit-grasp
docker compose build --pull ngit-grasp
docker compose up -d ngit-grasp
scripts/verify-deployment.sh https://ngit.example.com
```
For storage-changing releases, follow the linked migration guide and restore
the pre-upgrade volume snapshot before attempting a binary rollback.
## Validate the image locally
The repository includes a destructive-to-test-resources-only integration
check. It builds the image, creates uniquely named temporary container and
volume resources, replaces the container, and confirms the NIP-11 pubkey did
not change:
```bash
scripts/test-container-deployment.sh
```
Set `CONTAINER_ENGINE=podman` to exercise a compatible Podman CLI.