# anchorage runtime image (Alpine). # # Designed to be invoked by GoReleaser's `dockers:` block — GoReleaser # builds the static binary under dist/ and sets that as the build # context, so this file only has to install runtime dependencies and # COPY the binary into a minimal Alpine base. # # For a standalone `docker build` (not via GoReleaser) the binary must # already exist at ./anchorage in the build context; see Makefile's # `docker-local` target for the local-build wrapper. # # Why Alpine and not distroless: # - busybox shell is available for `docker exec -it … sh` triage. # - apk add for ad-hoc debug tools (curl, busybox-extras, strace) at # runtime without rebuilding. # - matches the uid/gid conventions the .deb/.rpm packages use, so an # operator who flips between compose and systemd deploys sees the # same `anchorage` user owning the files. # # Size is ~15 MB final image (5 MB Alpine + 3 MB CA/tz + ~7 MB static # anchorage binary). Small enough; debuggable when we need it. FROM alpine:3.20 # Pin to a specific uid/gid so volume permissions stay stable across # image rebuilds and match the nfpm-generated /etc/passwd on the host. ARG ANCHORAGE_UID=8080 ARG ANCHORAGE_GID=8080 RUN apk add --no-cache \ ca-certificates \ tzdata \ tini \ && addgroup -S -g ${ANCHORAGE_GID} anchorage \ && adduser -S -D -H -u ${ANCHORAGE_UID} -G anchorage -s /sbin/nologin anchorage \ && mkdir -p /etc/anchorage /var/lib/anchorage /var/log/anchorage \ && chown -R anchorage:anchorage /etc/anchorage /var/lib/anchorage /var/log/anchorage \ && chmod 0750 /var/lib/anchorage /var/log/anchorage COPY anchorage /usr/local/bin/anchorage RUN chmod 0755 /usr/local/bin/anchorage # OCI default ports: # 8080 — HTTP + WebSocket # 4222 — NATS client # 6222 — NATS cluster gossip (peer ↔ peer) EXPOSE 8080 4222 6222 # State lives on a volume so the stable node.id and NATS JetStream # files survive container restarts. Compose / k8s mounts a named volume # here; the nfpm-packaged install uses the same /var/lib/anchorage path # so muscle memory transfers. VOLUME ["/var/lib/anchorage"] # Liveness only — /v1/ready returning 503 during drain would flap the # container otherwise. The orchestrator (compose / Swarm / k8s) layers # its own readiness probe on /v1/ready separately. HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 \ CMD wget --quiet --spider http://127.0.0.1:8080/v1/health || exit 1 USER anchorage:anchorage WORKDIR /var/lib/anchorage # tini reaps zombies and forwards SIGTERM so the Go runtime's graceful # shutdown actually runs (the Fiber server drains, consumers drain, # NATS drains — see internal/app/anchorage/app.go:Close). ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/anchorage"] CMD ["serve", "--config", "/etc/anchorage/anchorage.yaml"]