kereru-forms/docs/DEPLOYMENT.md

157 lines
4.5 KiB
Markdown
Raw Normal View History

# Deployment
## Local development
### Backend
```bash
cd backend
go mod download
go run ./cmd/kereru-forms \
--listen-addr 127.0.0.1 \
--port 8080 \
--log-format console \
--db-dsn "file:./kereru-dev.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)"
```
Useful flags:
```bash
kereru-forms --help
```
### Frontend
```bash
cd frontend
npm install
npm run dev
```
Vite serves on <http://localhost:5173> and proxies `/api/*` to `http://localhost:8080`. To point at another API host, set `VITE_API_BASE_URL` at build/dev time:
```bash
VITE_API_BASE_URL=http://localhost:8080 npm run dev
```
## Production build without containers
```bash
# Backend binary
cd backend
CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=1.0.0" -o kereru-forms ./cmd/kereru-forms
# Frontend assets
cd ../frontend
npm ci
npm run build
```
Run the binary with the SPA directory and a persistent database:
```bash
KERERU_STATIC_DIR=../frontend/dist \
KERERU_DB_DSN="file:/var/lib/kereru/kereru.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)" \
KERERU_LOG_FORMAT=json \
KERERU_ENVIRONMENT=production \
./kereru-forms --listen-addr 0.0.0.0 --port 8080
```
The binary serves the API at `/health`, `/forms`, `/submissions` and the SPA for every other path.
## Docker
Build from the repository root so both `backend/` and `frontend/` are in context:
```bash
docker build -f docker/Dockerfile -t kereru-forms:1.0.0 .
docker run -d --name kereru-forms \
-p 8080:8080 \
-v kereru-data:/data \
-e KERERU_CORS_ORIGINS="https://forms.example.com" \
kereru-forms:1.0.0
```
Or with Compose:
```bash
docker compose -f docker/docker-compose.yml up -d --build
```
The image runs as an unprivileged user, stores SQLite data in `/data`, and serves the built frontend from `/app/static`.
## Podman
The image is OCI-compatible. Use rootless Podman:
```bash
podman build -f docker/Dockerfile -t kereru-forms:1.0.0 .
podman run -d --name kereru-forms \
-p 8080:8080 \
-v kereru-data:/data:Z \
-e KERERU_CORS_ORIGINS="*" \
kereru-forms:1.0.0
```
With Compose (either `podman-compose` or `docker compose` using the Podman socket):
```bash
podman-compose -f docker/docker-compose.yml up -d --build
```
`podman generate systemd --new --name kereru-forms` (or a Quadlet unit) can turn the container into a system service.
## Kubernetes
1. Build, tag and push the image to a registry reachable by the cluster:
```bash
docker build -f docker/Dockerfile -t registry.example.com/kereru-forms:1.0.0 .
docker push registry.example.com/kereru-forms:1.0.0
```
2. Update `image:` and the Ingress host in [`deploy/k8s.yaml`](../deploy/k8s.yaml).
3. Apply:
```bash
kubectl apply -f deploy/k8s.yaml
kubectl -n kereru-forms rollout status deployment/kereru-forms
```
The manifest includes a namespace, config map, 1 GiB `ReadWriteOnce` PVC mounted at `/data`, a `Recreate` deployment (SQLite is single-writer), liveness/readiness probes on `/health`, a service on port 80 and an Ingress. A writable `/tmp` emptyDir is mounted because the container root filesystem is read-only.
### Scaling notes
SQLite requires a single writer. Keep `replicas: 1` and use the `Recreate` strategy. To scale horizontally, implement a network database adapter (PostgreSQL/MariaDB) behind `database.Store`, then switch `dbDriver`/`dbDsn` and lift the Deployment restrictions.
Use a `ReadWriteMany` volume only if you replace SQLite; do not share a SQLite file across pods.
## Configuration in production
Prefer environment variables (twelve-factor) or a mounted YAML/JSON file:
```bash
kereru-forms --conf /etc/kereru/config.yaml
```
Typical production settings:
- `KERERU_LOG_FORMAT=json`
- `KERERU_ENVIRONMENT=production`
- `KERERU_CORS_ORIGINS=https://forms.example.com`
- `KERERU_TELEMETRY_ENABLED=true` and `OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318`
- `KERERU_STATIC_DIR=/app/static`
- SQLite DSN on a persistent volume: `file:/data/kereru.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)`
### Backups
For SQLite, back up the database file while the service is stopped, or use the SQLite online backup API/`.backup` command. Keep the volume and file permissions owned by the runtime user (UID used in the container image).
### Security
- Terminate TLS at the ingress/load balancer.
- Management routes (`/forms`, `/submissions`, `/forms/{id}/pdf`) are not authenticated; restrict them with an auth proxy or network policy.
- Set `KERERU_CORS_ORIGINS` to the exact frontend origin in production.
- Do not expose the SQLite file over a network share.