Skip to content

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:

  1. Stop accepting new requests.
  2. Drain in-flight connections and NSQ messages.
  3. Close database connections and flush logs.
  4. 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 nonroot user.
  • Read-only root filesystem — set readOnlyRootFilesystem: true in 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: - Makefilebuild, 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.example file 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.

References