footstone

Images

One directory per image, one Dockerfile each. The directory name is the image name: build/ becomes ghcr.io/projecteru2/footstone/build. Adding an image means adding a directory and listing it in the IMAGES variable of the Makefile and in the workflow matrix of .github/workflows/docker-image.yml.

build

ghcr.io/projecteru2/footstone/build — the toolchain image for compiling Eru components and packaging them as .rpm or .deb.

Base: golang:1.27-alpine, matching the go 1.27.0 directive in core, agent and cli.

Installed on top of the base:

Package Why
alpine-sdk, gcc, libc-dev, linux-headers C toolchain for cgo builds and for gem native extensions
git version stamping (git describe) during a build
libgit2, libgit2-dev headers for git bindings that link against libgit2
ruby, ruby-dev runtime for fpm
fpm (gem) builds .rpm and .deb packages from a binary
rpm, dpkg the packagers fpm shells out to

Compile a component by mounting a checkout into the image:

docker run --rm -v "$PWD:/src" -w /src \
  ghcr.io/projecteru2/footstone/build \
  go build -o eru-core .

The Go build cache lives inside the container and is discarded with it. For repeated builds, mount a cache directory as well:

docker run --rm -v "$PWD:/src" -w /src \
  -v "$HOME/.cache/go-build:/root/.cache/go-build" \
  -v "$HOME/go/pkg/mod:/go/pkg/mod" \
  ghcr.io/projecteru2/footstone/build \
  make build

run-lambda

ghcr.io/projecteru2/footstone/run-lambda — a base image for Eru lambda workloads, which run a one-shot command and exit rather than staying up as a service.

Base: alpine:3.22.

It adds sudo and a lambda user with uid and gid 65530, home directory /home/lambda, and a /etc/sudoers entry granting that user passwordless sudo. A workload image derives from it and switches to that user:

FROM ghcr.io/projecteru2/footstone/run-lambda
COPY task.sh /usr/local/bin/task.sh
USER lambda
CMD ["/usr/local/bin/task.sh"]

The passwordless sudo rule means the lambda user can escalate to root inside the container. Use this image only where that is acceptable — that is, where the container itself is the security boundary.

Tags

.github/workflows/docker-image.yml builds and pushes every image in its matrix. Tags come from docker/metadata-action:

Trigger Tags
push to master latest, plus the full commit sha
push of a v* tag the tag name (for example v1.0.0), plus the full commit sha

Both platforms, linux/amd64 and linux/arm64, go into one manifest list per tag. The workflow authenticates to GHCR with the workflow’s own GITHUB_TOKEN, so it needs no configured secrets.

Building locally

make check    # docker build --check on every Dockerfile
make build    # docker build for the local platform only
make push     # docker buildx build --push for linux/amd64,linux/arm64

make push needs a buildx builder that can produce multi-platform output — the default docker driver cannot. Create one once:

docker buildx create --name multi --driver docker-container --use

Pushing also needs a GHCR login with write:packages:

echo "$GITHUB_TOKEN" | docker login ghcr.io -u "$GITHUB_USER" --password-stdin