Building a custom binary¶
If you don't want to maintain your own Go module and build pipeline, list
your plugins in the Gateway resource and let the hog-build CLI generate
and compile the binary for you. This page distills the plugin-composition
parts of the delivery guide; read that page for the
full image family and deployment story.
The plugin manifest¶
The Gateway resource's spec.plugins list is the build manifest — there's
no separate plugin file to keep in sync with your config:
kind: Gateway
metadata: { name: my-gateway }
spec:
listen: ":8080"
plugins:
- github.com/acme/hog-geoblock@v1.4.0 # pinned — recommended, reproducible
- github.com/acme/hog-audit # unversioned — resolves to latest at build time
Each entry is <import-path>[@version] — the Go package whose init() calls
hog.Register(...) (see Writing plugins). An empty or
absent plugins: list builds the vanilla binary with only the built-in
modules.
hog-build reads this manifest, generates a throwaway main.go that
blank-imports every listed plugin next to hog.Main() — the same shape as the
framework mode example, generated for you — and a go.mod
pinning the hog module to a specific source tree, then runs go build.
The hog-build CLI¶
Flags:
--config <file-or-dir>— the gateway config file or directory to read the manifest from. Required.-o <path>— output binary path. Defaulthog.--hog-source <path>(or$HOG_SOURCE) — thehogmodule source the build pins against via areplacedirective, so the composed binary always contains thishog, not whatevergo getwould otherwise resolve. Required. Thehog-builderimage sets$HOG_SOURCEfor you.--replace <importpath>=<localdir>— swap a plugin module for a local checkout, skipping module resolution for anything under that import path. Repeatable. See local plugin development below.--tags <tags>— passed through togo build -tags.--go <path>— path to thegobinary to drive the build. Default: thegoonPATH.--keep— keep the generated temp build directory instead of deleting it on exit, so you can inspect the generatedmain.go/go.modafter a failed build.
Local plugin development with --replace¶
While iterating on a plugin you haven't published yet, point hog-build at
your working copy instead of a module version:
hog-build --config gateway.yaml -o ./hog \
--replace github.com/acme/hog-geoblock=./plugins/hog-geoblock
The manifest entry can stay unversioned (github.com/acme/hog-geoblock) —
--replace takes priority over module resolution for anything under that
import path, including subpackages. Inside a Dockerfile, COPY your local
plugin source into the build context before the RUN hog-build ... step so
the replace target exists when the build runs.
The two-stage Dockerfile¶
A custom-plugin image is a two-stage build: hog-builder composes the
binary, hog-runtime serves it. hog-builder (FROM golang:1.26-alpine)
carries the hog source and a compiled hog-build, with $HOG_SOURCE
pre-set; hog-runtime (FROM alpine:3.21) is the non-root, minimal serving
base every deployment ends up on:
# syntax=docker/dockerfile:1
FROM hog-builder AS build
COPY plugins/ ./plugins/
COPY gateway.yaml .
RUN hog-build --config gateway.yaml -o /out/hog
FROM hog-runtime
COPY --from=build /out/hog /usr/local/bin/hog
COPY --chown=hog:hog config/ /etc/hog/
COPY --chown=hog:hog web/ /srv/web/
See examples/Dockerfile.custom in the repo for the complete version,
including the --replace variant for local plugin sources. hog-builder and
hog-runtime are built from the repo root as the build context (docker build
-f build/Dockerfile.builder -t hog-builder ., then the same for
Dockerfile.runtime); the full image family, including the hog-static
convenience image, is covered in the
delivery guide.
Content (/srv/web) and config (/etc/hog) are always plain directories —
COPY them into the final stage or mount them at runtime. There's no codegen
step for your own files; only the binary itself is generated.
Verifying the result¶
Once built, run the binary against your config like any other HOG binary
(./hog --config gateway.yaml) and confirm the routes backed by your plugins
respond as expected. See Testing plugins for how to automate
this as part of your plugin's own test suite.
Rendering config with kustomize¶
The hog-builder image includes kustomize, so if you manage your config
as a kustomize base + per-environment overlays (see
operations: rendering config with kustomize),
you can render the overlay and feed it straight to hog-build in the same
build stage — no extra base image, no separate render step in CI:
FROM ghcr.io/paulopiriquito/hog-builder:v2.0.0 AS build
COPY config/ ./config/
RUN kustomize build config/overlays/prod > /out/gateway.yaml
RUN hog-build --config /out/gateway.yaml -o /out/hog
See operations: rendering config with kustomize for the base/overlay layout and a complete worked example.