Files
solopm-server/plans/15-deployment.md
T

3.2 KiB

15 — Build, Deployment & Ops

Two supported deployment paths from day one: bare binary (user supplies Postgres/Meilisearch/SMTP via env) and reference docker-compose. One artifact serves both: a single Go binary with the SPA embedded.

Build pipeline

  • make build: cd web && pnpm install && pnpm buildgo build -o solopm ./cmd/solopm with //go:embed web/dist (embed directive lives in a package that fails the build if web/dist is missing)
  • Version stamped via -ldflags "-X main.version=$(git describe)"; shown in /healthz and a footer
  • make dev: Vite dev server (proxying /api) + go run with a file-watcher (air or watchexec)
  • CI recipe (GitHub Actions or equivalent): lint (golangci-lint, eslint), go test ./..., frontend build, binary artifact
  • Release: multi-arch binaries (linux/amd64, linux/arm64, darwin/arm64) + a container image (distroless or alpine, non-root user, UPLOAD_DIR volume)

Runtime

  • CLI subcommands on the same binary: solopm serve (default), solopm migrate up|down, solopm reindex, solopm cull [--dry-run]
  • serve runs pending migrations at startup by default (--no-migrate to disable)
  • First-run bootstrap: empty users table (beyond seeds) → registration page advertises that the first account becomes admin (05-auth.md)
  • /healthz: 200 when DB reachable; reports meilisearch/smtp as ok/absent (absent is not unhealthy)
  • Full env var reference lives in 01-architecture.md; ship .env.example with every variable, commented

docker-compose (reference)

  • docker-compose.yml services:
    • app — the SoloPM image; ports 4080; env wired to the two services; volumes: uploads:/data/uploads; depends_on healthy postgres + meilisearch
    • postgres — postgres:16-alpine; volume pgdata; healthcheck pg_isready
    • meilisearch — getmeili/meilisearch; volume meilidata; MEILI_MASTER_KEY from env
    • (dev profile) mailhog — SMTP capture for local testing
  • docker compose up from a fresh clone + .env copied from .env.example reaches the login page with zero other steps
  • Compose file pins image versions; README documents upgrade = pull new app image, migrations run on start

Reverse proxy & SSE notes

  • Document (README/ops doc): disable proxy buffering for /api/v1/stream (nginx proxy_buffering off, X-Accel-Buffering: no is already sent); read timeout ≥ 60s; client_max_body_sizeMAX_UPLOAD_MB
  • Cookie Secure requires https at the proxy; set BASE_URL accordingly

Backup & restore

  • Documented procedure: pg_dump + tar of UPLOAD_DIR = complete backup (Meilisearch is derived — rebuild with solopm reindex)
  • Restore drill documented and tested once: restore dump, restore uploads, start app, reindex

Operational acceptance

  • Fresh docker compose up → register → create project → create issue → board drag works, all on first try following only the README
  • Bare-binary path verified against external Postgres + Meilisearch using only .env.example guidance
  • Binary restarts cleanly under systemd example unit (provided in ops doc)