This episode covers deploying a Fiber application: a multi-stage Dockerfile with a static CGO_ENABLED=0 build, a minimal runtime image, configuration management via environment variables, health checks, and deployment options to Vercel and a regular server.

Writing a Fiber application is only half the journey. Episode 21 covers the rest: deployment and Docker — building a small, secure image, running the app in a container, managing configuration, and production target choices.
Go applications have a unique advantage: they can be compiled into a static binary that runs without any runtime. With the right strategy, a Fiber image can be tens of megabytes and ready to run anywhere — from a VPS to a container orchestrator.
Multi-stage builds the binary in one image and copies the result into a slim runtime image:
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/main .
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/main /app/main
EXPOSE 3000
CMD ["/app/main"]The builder stage downloads modules and compiles the binary with CGO_ENABLED=0 for a static build. The runtime stage contains only the binary plus CA certificates and timezone data — no Go toolchain in the production image.
-ldflags="-s -w" strips symbols and the debug table, shrinking the binary drastically. For an even smaller image, swap the alpine base for scratch — make sure the binary is truly static and CA certificates are added:
FROM scratch
COPY --from=builder /app/main /main
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 3000
ENTRYPOINT ["/main"]scratch produces the smallest image, but you're responsible for everything needed — like CAs for outbound TLS connections. alpine is a balanced choice when you need a shell for debugging.
Don't hardcode configuration values. Read them from environment variables with sensible defaults:
app := fiber.New(fiber.Config{
Prefork: os.Getenv("PREFORK") == "true",
})
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
app.Listen(":" + port)This pattern follows the twelve-factor app: configuration comes from the environment, not from code. Docker passes env with -e or a .env file, and cloud platforms have their own mechanisms. Local defaults make development easy without setup.
Orchestrators need to know when the app is ready to accept traffic:
app.Get("/healthz", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"status": "ok"})
})
app.Get("/readyz", func(c fiber.Ctx) error {
if !databaseConnected() {
return fiber.NewError(fiber.StatusServiceUnavailable, "db belum siap")
}
return c.JSON(fiber.Map{"status": "ready"})
})/healthz signals the process is alive; /readyz signals that dependencies (database, cache) are ready. The Dockerfile references both via HEALTHCHECK:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD wget -q -O - http://localhost:3000/healthz || exit 1For serverless functions on Vercel, the Fiber adaptor can be called from a Go handler:
import "github.com/gofiber/adaptor/v2"
func handler(w http.ResponseWriter, r *http.Request) {
adaptor.FiberApp(app)(w, r)
}
func main() {
vercel.Serverless(handler)
}adaptor.FiberApp(app) converts a Fiber application into an http.Handler, and vercel.Serverless(handler) wraps it for the serverless platform. The same approach works on Netlify and other platforms supporting Go. Note: calls that depend on a single instance (like in-memory sessions) need adjustment because serverless can spin up new instances.
docker build -t fiber-app .
docker run --rm -p 3000:3000 -e PORT=3000 fiber-app
curl http://localhost:3000/healthzdocker build produces the image according to the Dockerfile; docker run runs it with the port and env passed through. If the health check returns {"status":"ok"}, the app is ready for traffic. Check the image size with docker images — compare before and after -ldflags.
Key takeaways:
CGO_ENABLED=0, minimal runtime (alpine or scratch).-ldflags="-s -w" shrinks the binary size./healthz and /readyz for orchestrator health checks.HEALTHCHECK in the Dockerfile keeps the container healthy.github.com/gofiber/adaptor/v2.In the next episode, episode 22 — the final one — we discuss the wrap-up with a complete CRUD project example that ties together routing, middleware, binding, error handling, and deployment.