Go Knowledge Pack
Applies to: Projects detected with
go.modcontaininggithub.com/gin-gonic/gin,github.com/labstack/echo,github.com/gofiber/fiber, or any Go project using the standard librarynet/httpfor HTTP serving
Quick Reference
| Property | Value |
|---|---|
| Signal files | go.mod (gin/echo/fiber or stdlib net/http) |
| Default port | 8080 |
| Health path | /healthz + /ready |
| Base template | templates/dockerfiles/go.Dockerfile (+ references/base-images.md) |
Build Flags
Two flags are required for a correct production build:
CGO_ENABLED=0produces a fully static binary with no libc dependency — required when targeting the distroless static image. If CGO is needed (e.g., for sqlite3 or cgo bindings), use the distroless cc image instead.-ldflags="-s -w"strips debug symbols and DWARF info, reducing binary size by ~30%.
Health Endpoints
Go does not provide health check endpoints out of the box — you must implement them manually. Example using standard library:
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ok"}`))
})
http.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
if err := db.Ping(); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
w.Write([]byte(`{"status":"not ready"}`))
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ready"}`))
})Probe configuration in Deployment manifest
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3Note: Go binaries start in milliseconds — initialDelaySeconds: 3 is generous. No JVM warmup or interpreter startup to wait for.
Graceful Shutdown
Implement signal.NotifyContext with srv.Shutdown(ctx) to allow in-flight requests to complete before the pod exits during a rolling update. Without this, connections are dropped and callers receive 502 errors.
Database Profiles
Go does not have a built-in profile system. Database configuration is typically driven by environment variables:
| Library | Driver | Connection Env Var |
|---|---|---|
database/sql + pgx |
github.com/jackc/pgx/v5/stdlib |
DATABASE_URL |
| GORM | gorm.io/driver/postgres |
DATABASE_URL |
| sqlx | github.com/jmoiron/sqlx + pgx |
DATABASE_URL |
| pgx direct | github.com/jackc/pgx/v5 |
DATABASE_URL |
Environment variables for PostgreSQL on AKS
env:
- name: DATABASE_URL
value: "host={{PG_SERVER_NAME}}.postgres.database.azure.com port=5432 dbname={{DB_NAME}} user={{IDENTITY_NAME}} sslmode=require"Workload Identity with pgx
Use azidentity to obtain Azure AD tokens and inject them via pgx's BeforeConnect hook — no password stored:
import (
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/jackc/pgx/v5"
)
cred, _ := azidentity.NewDefaultAzureCredential(nil)
config, _ := pgx.ParseConfig(os.Getenv("DATABASE_URL"))
config.BeforeConnect = func(ctx context.Context, cfg *pgx.ConnConfig) error {
token, err := cred.GetToken(ctx, policy.TokenRequestOptions{
Scopes: []string{"https://ossrdbms-aad.database.windows.net/.default"},
})
if err != nil {
return err
}
cfg.Password = token.Token
return nil
}ConfigMap pattern
apiVersion: v1
kind: ConfigMap
metadata:
name: {{APP_NAME}}-config
data:
DATABASE_URL: "host={{PG_SERVER_NAME}}.postgres.database.azure.com port=5432 dbname={{DB_NAME}} user={{IDENTITY_NAME}} sslmode=require"Writable Paths (DS012 Compliance)
When readOnlyRootFilesystem: true is set, Go apps typically need no writable paths:
- Go compiles to a static binary — no temp files, no interpreted bytecode, no session storage
- The distroless static base image has no shell or package manager that writes to disk
Optional /tmp mount
If your application explicitly writes temporary files (e.g., file uploads, report generation):
volumes:
- name: tmp
emptyDir: {}
containers:
- name: app
volumeMounts:
- name: tmp
mountPath: /tmpMost Go web APIs do not need this.
Resource Sizing
Go compiles to a static binary with no runtime — it is the most resource-efficient option.
| Resource | Request | Limit |
|---|---|---|
| CPU | 50m | 200m |
| Memory | 64Mi | 128Mi |
Port Configuration
- Default port: 8080 (Go convention, not enforced by any framework)
- Env var override:
PORT(commonly used pattern) - Bind port >= 1024 — lower ports require elevated privileges; running as non-root (uid 65534) means port 80 or 443 will fail with
permission denied.
Code pattern
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Listening on :%s", port)
log.Fatal(http.ListenAndServe(":"+port, router))All major Go frameworks (Gin, Echo, Fiber) accept the listen address as a string — no special configuration property needed.
Build Commands
| Variant | Command | Notes |
|---|---|---|
| Standard | CGO_ENABLED=0 go build -ldflags="-s -w" -o server ./cmd/server |
Production binary, stripped |
| Race detector (test only) | go build -race -o server ./cmd/server |
Do not use in production — 10x overhead |
| Multiple binaries | CGO_ENABLED=0 go build -ldflags="-s -w" -o migrate ./cmd/migrate |
Build each binary target separately |
The ./cmd/server path is conventional for Go projects using the Standard Go Project Layout. Adjust to match the actual main package location.
Common Issues on AKS
| Issue | Symptom | Fix |
|---|---|---|
| Binary not statically linked | exec format error or not found in distroless |
Ensure CGO_ENABLED=0 is set during build; if CGO is required, use the distroless cc image instead of the distroless static image |
| DNS resolution issues during build | dial tcp: lookup ... no such host |
Add ca-certificates to the build stage or use a Debian-based build image |
| Graceful shutdown not implemented | Connections dropped during rolling update, 502 errors | Implement signal.NotifyContext with srv.Shutdown(ctx) — give in-flight requests time to complete before exit |
| Binary name mismatch | exec /server: no such file or directory |
Verify the -o flag in go build matches the ENTRYPOINT path in the Dockerfile |
| Port < 1024 with non-root user | bind: permission denied |
Use port 8080 (or any port >= 1024); never bind to 80 or 443 inside the container |