Back to Blog
Tutorial9 min read2026-06-28

How to Deploy a Go Gin API to the Cloud

Go's static binaries make for tiny, fast container images. Here's a Gin REST API deployed with a scratch-based multi-stage build, graceful shutdown, health checks, and an auto-wired Postgres.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Why Go is a joy to deploy

Go compiles to a single static binary. That means your production image can be a few megabytes — no runtime, no interpreter, no node_modules. A Gin API on a modern container platform starts in milliseconds and sips memory. Let's build a production-grade deploy.

Step 1: A Gin app that binds to $PORT

// main.go
package main

import (
    "net/http"
    "os"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/health", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"status": "ok"})
    })
    r.GET("/api/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "pong"})
    })

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    r.Run("0.0.0.0:" + port)
}

Step 2: Graceful shutdown

Gin's r.Run doesn't handle SIGTERM. For zero-dropped-requests on redeploy, run an http.Server and shut it down cleanly:

srv := &http.Server{Addr: "0.0.0.0:" + port, Handler: r}
go func() { srv.ListenAndServe() }()

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
<-quit

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
srv.Shutdown(ctx) // drains in-flight requests

Step 3: A tiny multi-stage Dockerfile

Build with the Go toolchain, ship on scratch (or distroless if you want a CA bundle and shell-free base):

FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app .

FROM gcr.io/distroless/static-debian12
COPY --from=build /app /app
EXPOSE 8080
ENTRYPOINT ["/app"]

CGO_ENABLED=0 produces a fully static binary; -ldflags="-s -w" strips debug info to shrink it. Distroless gives you CA certs (needed for HTTPS calls) without a full OS.

Step 4: Wire a managed Postgres

Use pgx and read the connection string from the environment. On PandaStack, attaching a managed PostgreSQL injects DATABASE_URL:

import "github.com/jackc/pgx/v5/pgxpool"

pool, err := pgxpool.New(context.Background(), os.Getenv("DATABASE_URL"))
if err != nil {
    log.Fatalf("db connect: %v", err)
}
defer pool.Close()

pgxpool manages a connection pool — cap its max connections below your tier's limit (50 on free, 300 on Pro, 1000 on Premium).

Step 5: Deploy

Connect your repo. PandaStack auto-detects Go, or uses your Dockerfile. On push it builds with rootless BuildKit in a K8s Job pod, ships to Artifact Registry, and Helm-deploys:

git push origin main

Live build and app logs stream in real time. Add a custom domain for automatic SSL. Because the image is tiny, builds and cold starts are fast — handy on the free tier where apps scale to zero on spot nodes.

Step 6: Right-size compute

Go APIs are CPU-efficient and memory-light. The free tier (0.25 CPU / 512MB) handles a surprising amount of traffic for a Go service. Scale up to compute-optimized c1/c2 tiers for high-throughput APIs, or add replicas for horizontal scale.

Performance notes

  • Use gin.SetMode(gin.ReleaseMode) in production to drop debug logging overhead.
  • Reuse the pgxpool across handlers — never open a connection per request.
  • A static-binary image cold-starts dramatically faster than interpreted runtimes, so scale-to-zero is far less painful for Go than for Node or Python.

References

  • [Gin documentation](https://gin-gonic.com/docs/)
  • [Go — Graceful shutdown (net/http)](https://pkg.go.dev/net/http#Server.Shutdown)
  • [Distroless container images](https://github.com/GoogleContainerTools/distroless)
  • [pgx — PostgreSQL driver](https://github.com/jackc/pgx)

---

A Go Gin API is about as lean as cloud deploys get — a few-MB image, millisecond starts, low memory. Push one to PandaStack's [free tier](https://dashboard.pandastack.io) and attach a managed Postgres; DATABASE_URL wires itself in and you're live.

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also