Skip to content
Back to Deployment

Deploying Go Gin Apps

High-performance Go HTTP framework, compile to single binary, deploy to scratch/distroless container or VPS.

PlatformsIntermediate

Why

Go compiles to a single static binary with no runtime dependencies, producing tiny containers (scratch/distroless) with instant startup. Gin adds routing and middleware on top of net/http with 10x performance of Node/Python.

Platform-as-a-Service adoption grew 24% in 2024 as teams shift from VM management to git-push deploys. the cloud infrastructure market reached $330B in 2024, growing 22% year-over-year (Synergy Research), and PaaS layers capture the majority of new workloads.

When

Use Go/Gin for high-performance APIs, microservices, and CLI-served web apps. Choose over Node when binary distribution and low memory matter.

How

1. Build — go build -o server -ldflags="-s -w" main.go (stripped binary) 2. Run — ./server, listens on :8080 by default 3. Docker — multi-stage: golang build, scratch/distroless runtime 4. Binary size — -ldflags="-s -w" strips debug info, UPX for more compression 5. Env — os.Getenv, or godotenv for .env in dev 6. Graceful shutdown — signal.NotifyContext for SIGTERM 7. Cross-compile — GOOS=linux GOARCH=amd64 go build

Example

// main.go — Gin with graceful shutdown package main import ("github.com/gin-gonic/gin"; "context"; "os"; "os/signal"; "syscall") func main() { r := gin.Default() r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"ok": true}) }) r.GET("/api", func(c *gin.Context) { c.JSON(200, gin.H{"data": "hello"}) }) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) defer stop() go r.Run(":8080") <-ctx.Done() } # Dockerfile — multi-stage, distroless FROM golang:1.22-alpine AS build WORKDIR /app COPY go.* ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o server -ldflags="-s -w" main.go FROM gcr.io/distroless/static-debian12 COPY --from=build /app/server /server EXPOSE 8080 ENTRYPOINT ["/server"]

Common Problems

  • ⚠Binary too large — forgot -ldflags=-s -w, includes debug symbols doubling size
  • ⚠CGO_ENABLED=1 in container — needs glibc/musl, use CGO_ENABLED=0 for static binary
  • ⚠Container has no shell — distroless/scratch, can't exec into for debugging, use :debug variant

Best Practices

  • ✓Use distroless or scratch base image — Go static binary needs no OS, smallest possible container
  • ✓Strip binary with -ldflags='-s -w' and consider UPX — reduces 20MB binary to 5-8MB
  • ✓Set up health checks and graceful shutdown — handle SIGTERM to drain in-flight requests before exit

FAQ