Container & Service Packaging Standard¶
Version: 1.0 Status: Draft Last Updated: 2026-04-03
Purpose¶
Define how Simpaisa Go services are built, packaged, and deployed as containers. Establishes secure defaults, consistent project structure, and operability requirements across all microservices.
Base Images¶
All Go services use distroless runtime images:
- Build stage:
golang:1.24-bookworm(or latest stable). - Runtime stage:
gcr.io/distroless/static-debian12:nonroot.
Distroless provides: no shell, no package manager, no OS utilities — minimal attack surface.
Dockerfile Pattern¶
# Build stage
FROM golang:1.24-bookworm AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /app ./cmd/service
# Runtime stage
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]
Key rules:
- CGO_ENABLED=0 — static binary, no libc dependency.
- -ldflags="-s -w" — strip debug symbols, reduce image size.
- USER nonroot:nonroot — never run as root.
- Read-only filesystem in deployment manifests.
Health Endpoints¶
Every service MUST expose:
| Endpoint | Method | Purpose | Response |
|---|---|---|---|
/health |
GET | Liveness | 200 OK if process is alive. |
/ready |
GET | Readiness | 200 OK when ready to serve traffic. |
/health— returns 200 if the process is running. No dependency checks./ready— returns 200 only when all dependencies (database, NSQ, cache) are reachable. Returns 503 otherwise.
Graceful Shutdown¶
All services MUST handle SIGTERM:
- Stop accepting new requests.
- Drain in-flight connections and NSQ messages.
- Close database connections and flush logs.
- Exit within 30 seconds. Kubernetes sends SIGKILL after
terminationGracePeriodSeconds.
Resource Limits¶
CPU and memory limits are mandatory in all deployment manifests:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Tune per service based on load testing. These are starting defaults — adjust and document deviations.
Security Requirements¶
- Non-root execution — enforced by distroless
nonrootuser. - Read-only root filesystem — set
readOnlyRootFilesystem: truein security context. - No privilege escalation — set
allowPrivilegeEscalation: false. - Image scanning — Trivy or Grype runs in CI. Builds fail on CRITICAL or HIGH CVEs.
- No secrets in images — all secrets via environment variables or mounted volumes.
Image Tagging¶
Format: {service}:{git-sha-short}-{semver}
Examples:
- payin-service:abc1234-v3.1.0
- payout-service:def5678-v2.0.1
- remittance-service:ghi9012-v1.5.0
Rules:
- Never use latest in production.
- Git SHA provides traceability to exact commit.
- Semver communicates change significance.
Container Registry¶
Current: AWS ECR.
Evaluate: Cloudflare R2 + self-hosted registry (aligned with Cloudflare-first infrastructure direction). Decision criteria: cost, latency to PK/BD/NP/IQ regions, integration with CI pipeline.
Go Service Project Layout¶
service-name/
cmd/service/ # Application entrypoint (main.go)
internal/ # Private application code
handler/ # HTTP/gRPC handlers
domain/ # Business logic
repository/ # Data access (SurrealDB, etc.)
pkg/ # Shared libraries (if any, prefer internal/)
api/ # OpenAPI specs, protobuf definitions
Dockerfile
Makefile
go.mod
go.sum
Every service includes:
- Makefile — build, test, lint, docker-build, docker-push targets.
- OpenTelemetry instrumentation — traces, metrics, structured logging from day one.
- Structured logging — JSON format, slog package, correlation via traceId.
Configuration¶
Follow 12-factor app principles:
- All configuration via environment variables.
- No config files baked into container images.
- Use a
.env.examplefile in the repo documenting all required variables. - Sensitive values (API keys, database credentials) via secrets management, never environment variable defaults.
Unikraft Evaluation¶
Consider Unikraft unikernels for: - Security-critical paths — payment authorisation, cryptographic operations. - Performance-critical paths — high-throughput transaction processing.
Evaluation criteria: boot time, memory footprint, compatibility with Go static binaries, operational tooling maturity, debugging capability. Container packaging remains the default until Unikraft is validated.