Files
anchorage/build/package/Dockerfile
William Gill 12bf35caf8 anchorage v1.0 initial tree
Greenfield Go multi-tenant IPFS Pinning Service wire-compatible with the
IPFS Pinning Services API spec. Paired 1:1 with Kubo over localhost RPC,
clustered via embedded NATS JetStream, Postgres source-of-truth with
RLS-enforced tenancy, Fiber + huma v2 for the HTTP surface, Authentik
OIDC for session login with kid-rotated HS256 JWT API tokens.

Feature-complete against the 22-milestone build plan, including the
ship-it v1.0 gap items:

  * admin CLIs: drain/uncordon, maintenance, mint-token, rotate-key,
    prune-denylist, rebalance --dry-run, cache-stats, cluster-presences
  * TTL leader election via NATS KV, fence tokens, JetStream dedup
  * rebalancer (plan/apply split), reconciler, requeue sweeper
  * ristretto caches with NATS-backed cross-node invalidation
    (placements live-nodes + token denylist)
  * maintenance watchdog for stuck cluster-pause flag
  * Prometheus /metrics with CIDR ACL, HTTP/pin/scheduler/cache gauges
  * rate limiting: session (10/min) + anonymous global (120/min)
  * integration tests: rebalance, refcount multi-org, RLS belt
  * goreleaser (tar + deb/rpm/apk + Alpine Docker) targeting Gitea

Stack: Cobra/Viper, Fiber v2 + huma v2, embedded NATS JetStream,
pgx/sqlc/golang-migrate, ristretto, TypeID, prometheus/client_golang,
testcontainers-go.
2026-04-16 18:13:36 -05:00

69 lines
2.8 KiB
Docker

# 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"]