Files
warmbly/deploy/docker/backend.Dockerfile
T
Matthew Meszaros 734cb5fe08 feat: make self-hosted onboarding survivable by fixing invite_only, which could not onboard anyone (the accept route is JWT-only, so redeeming the invitation that would create your account required already having one, making the self-host default silently identical to fully closed), threading the invitation token through registration so an invited person lands in the inviting organization instead of a stray workspace, gating SSO just-in-time provisioning behind DISABLE_REGISTRATION (it bypassed the gate entirely, so an instance set to true was still open to anyone the IdP would assert) with SSO_AUTO_PROVISION as the opt-out, correcting the OIDC redirect URL that pointed at /api/v1 against a route at /v1 and 404'd every SSO login, scoping the first-launch exemption so it no longer overrides an explicit lockdown, preserving the remaining TTL when restoring a losing setup token so a public endpoint cannot hold the claim window open forever, replacing a generic 403 with typed registration_invite_only, registration_closed, invitation_invalid, setup_token_invalid and setup_already_complete codes that name the next step, logging why no claim link was issued on an already-claimed instance instead of staying silent, adding a warmblyctl operator CLI (status with health checks and a non-zero exit, reissuable setup-link, user create/list/reset-password/grant-admin/revoke-admin/disable-2fa, hash-password) so a locked-out operator no longer needs hand-written psql, adding read-only instance configuration over 104 environment variables with structural secret redaction and fingerprints, 35 health checks, a database-backed settings tier for the three keys no environment variable owns, hiding the signup form when the config already says invite_only rather than failing the whole form with a toast, and documenting first run, accounts and access, configuration, instance health and troubleshooting alongside the root .env.example the README told operators to write but never shipped (#114)
2026-08-16 05:58:11 +02:00

65 lines
2.9 KiB
Docker

# syntax=docker/dockerfile:1.7
#
# Default build is CGO-free (NATS + JSON) — no librdkafka, gcc, or musl-dev, so
# it builds in a fraction of the time and cross-compiles to arm64/amd64 cleanly.
# BuildKit cache mounts keep the module + compile caches warm across builds.
#
# The builder always runs on the build host ($BUILDPLATFORM) and cross-compiles
# to $TARGETARCH, so multi-arch CI builds never run the Go compiler under QEMU.
#
# To include the optional Kafka backend, build with --build-arg GO_TAGS=kafka
# (adds librdkafka + CGO; slower, and CGO cannot cross-compile — build each arch
# on a native runner). Runtime selection is still by env
# (EVENTBUS_PROVIDER / CODEC_PROVIDER).
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
ARG GO_TAGS=""
ARG TARGETOS TARGETARCH
RUN apk add --no-cache git ca-certificates && \
if echo "$GO_TAGS" | grep -qw kafka; then apk add --no-cache gcc musl-dev librdkafka-dev; fi
WORKDIR /app
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
set -eux; \
if echo "$GO_TAGS" | grep -qw kafka; then CGO=1; TAGS="musl kafka"; else CGO=0; TAGS=""; fi; \
CGO_ENABLED=$CGO GOOS=$TARGETOS GOARCH=$TARGETARCH go build -tags "$TAGS" -ldflags="-s -w" -o /out/backend ./cmd/backend; \
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w" -o /out/seed ./cmd/seed; \
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w" -o /out/migrate ./cmd/migrate; \
CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags="-s -w" -o /out/warmblyctl ./cmd/warmblyctl
# Runtime stage
FROM alpine:3.23
ARG GO_TAGS=""
RUN apk add --no-cache ca-certificates tzdata && \
if echo "$GO_TAGS" | grep -qw kafka; then apk add --no-cache librdkafka; fi && \
adduser -D -u 1000 warmbly
COPY --from=builder /out/backend /app/backend
COPY --from=builder /out/seed /app/seed
COPY --from=builder /out/migrate /app/migrate
# The operator CLI goes on PATH, not /app, so the documented recovery command is
# `docker compose exec backend warmblyctl status` and not a path.
COPY --from=builder /out/warmblyctl /usr/local/bin/warmblyctl
# Installer script the worker orchestrator uploads + runs over SSH, and serves
# at GET /worker-install.sh. The mode is explicit because COPY otherwise keeps
# the checkout's: on a filesystem without POSIX permissions that is 0700, and
# the backend runs as uid 1000, so serving the installer fails with a 500.
COPY --chmod=755 scripts/install-worker.sh /app/scripts/install-worker.sh
USER warmbly
EXPOSE 8080
# 127.0.0.1, not localhost: busybox wget tries ::1 first but the server binds IPv4.
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8080/health || exit 1
ENTRYPOINT ["/app/backend"]