Dipublikasikan 7 Agustus 2026
Salah satu masalah paling sering dihadapi developer backend adalah Docker image yang membengkak. Sebuah aplikasi Go yang seharusnya hanya butuh binary kecil malah dibundle dengan image base Ubuntu atau Debian berukuran ratusan megabyte. Hasilnya: build lambat, push ke registry makan waktu, dan deployment jadi tidak efisien. Padahal aplikasi Go itu sendiri dikompilasi jadi binary statis yang ukurannya sangat kecil.
Di artikel ini kita akan membahas teknik Docker multi-stage build yang memungkinkan kita memisahkan proses compilasi dan runtime environment. Dengan pendekatan ini, image production bisa dikecilkan drastis tanpa mengorbankan fungsionalitas. Kita juga akan membahas beberapa best practice tambahan yang sering dilewatkan pemula.
Image Docker yang besar bukan hanya soal penyimpanan di registry. Ada beberapa dampak langsung ke workflow development dan operational:
CI/CD pipeline lambat: Setiap kali push image ke Docker Hub atau private registry, ukuran besar membuat proses upload lebih lama. Di tim yang menerapkan continuous deployment, delay ini terasa setiap kali ada pull request yang merge.
Deployment lambat: Kubernetes node atau VPS harus pull image dari registry sebelum menjalankan container. Semakin besar image, semakin lama startup time dan semakin besar beban bandwidth.
Surface attack lebih luas: Image yang menyimpan compiler, header library, dan tool debugging di production meningkatkan risiko keamanan. Semakin banyak tool yang tersedia di dalam container, semakin banyak celah yang bisa dieksploitasi.
Storage cost: Cloud registry seperti AWS ECR atau Google Artifact Registry mengenakan biaya per GB penyimpanan dan egress. Image kecil berarti penghematan biaya langsung di skala besar.
Mengurangi ukuran image dari 1 GB ke 15 MB bukan hanya optimasi biasa: ini adalah best practice yang wajib diterapkan di production environment, terutama untuk microservices yang di-deploy di cluster.
Multi-stage build adalah fitur Docker yang memungkinkan kita mendefinisikan beberapa FROM dalam satu Dockerfile. Setiap stage bisa punya base image berbeda dan hanya artifact yang kita pilih saja yang disalin ke stage final. Stage yang tidak direferensikan di akhir akan dibuang secara otomatis.
Flow umum untuk aplikasi compiled seperti Go, Rust, atau C++ adalah:
Build stage: Gunakan image lengkap dengan compiler (Golang official image) untuk compile source code jadi binary. Di stage ini kita bisa install dependency build, caching layer, dan tool development.
Production stage: Gunakan image minimalis seperti scratch, alpine, atau distroless dan salin binary hasil compile beserta asset static seperti file HTML atau CSS.
Keuntungannya jelas: stage build dengan toolchain lengkap dibuang, yang tersisa hanya binary executable dan dependensi runtime minimal. Ukuran image turun drastis dan attack surface menyusut signifikan.
Sebelum mulai, pastikan kamu punya project Go sederhana. Berikut contoh HTTP server minimal yang akan kita containerize. Server ini hanya merespons Hello World di root path:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello dari Docker multi-stage build!")
})
fmt.Println("Server berjalan di port 8080")
http.ListenAndServe(":8080", nil)
}Simpan file di main.go dan inisialisasi modul dengan perintah go mod init hello-docker. Pastikan juga kamu sudah install Docker Desktop atau Docker Engine di mesin lokal.
Berikut contoh Dockerfile single stage yang sering dipakai pemula. Image hasilnya akan besar karena menyimpan toolchain Go lengkap di image production:
FROM golang:1.23
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o hello .
EXPOSE 8080
CMD ["./hello"]Kalau kita build image ini dengan docker build -t hello-go:single ., hasilnya bisa mencapai lebih dari 1 GB karena base image golang:1.23 berisi compiler, standard library source, tool debugging, dan package manager. Padahal aplikasi kita hanya butuh satu file binary.
Sekarang kita refactor Dockerfile jadi multi-stage. Tujuannya sederhana: hanya menyalin binary ke image final dan membuang semua tool build.
# Stage 1: Build
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 -a -installsuffix cgo -o hello .
# Stage 2: Production
FROM scratch
WORKDIR /app
COPY --from=builder /app/hello .
EXPOSE 8080
ENTRYPOINT ["./hello"]Mari kita bedah setiap baris penting:
AS builder: Menamai stage pertama supaya bisa direferensi di stage berikutnya dengan flag --from=builder.
CGO_ENABLED=0: Menonaktifkan CGO supaya binary tidak bergantung pada library C system. Ini sangat penting agar binary bisa berjalan di image scratch yang benar-benar kosong.
GOOS=linux: Menargetkan OS Linux untuk binary output agar kompatibel dengan container runtime.
FROM scratch: Image kosong dari Docker. Tidak ada shell, tidak ada package manager, tidak ada sistem file. Hanya runtime environment minimal.
COPY --from=builder: Menyalin file hello dari stage builder ke stage production.
Jalankan perintah build seperti biasa:
docker build -t hello-go:multi .Lalu bandingkan ukuran image dengan perintah:
docker images hello-goHasilnya akan menunjukkan perbedaan signifikan. Image single stage biasanya di atas 1.2 GB, sementara image multi-stage dengan scratch umumnya hanya sekitar 10-20 MB tergantung ukuran binary Go kamu. Itu pengurangan lebih dari 90 persen.
Untuk mengecek apakah aplikasi benar-benar berjalan normal:
docker run -p 8080:8080 hello-go:multiBuka browser atau gunakan curl ke http://localhost:8080 untuk memastikan respons server muncul. Kalau server berjalan tanpa error, berarti setup sudah benar.
Scratch bukan satu-satunya pilihan untuk stage production. Berikut perbandingan beberapa base image populer:
scratch: Ukuran paling kecil, cocok untuk Go atau Rust yang sudah fully static. Tidak ada shell, jadi debugging lebih sulit.
gcr.io/distroless/static: Image minimalis dari Google yang tetap menyertakan CA certificates, timezone data, dan beberapa file esensial. Sangat direkomendasikan untuk production.
alpine:latest: Ukuran sekitar 5-7 MB, menyertakan shell dan package manager. Cocok kalau kamu butuh debugging cepat di production.
debian:slim: Lebih besar (sekitar 20-30 MB) tapi kompatibilitasnya paling tinggi. Dipakai kalau aplikasi butuh library dinamis tertentu.
Untuk aplikasi Go yang dikompilasi static, scratch atau distroless adalah pilihan terbaik. Hindari Alpine kalau tidak perlu karena meskipun kecil, musl libc bisa menyebabkan bug subtle pada beberapa dependency.
Berikut beberapa tips untuk mengoptimalkan multi-stage build lebih jauh:
Gunakan .dockerignore: Hindari menyalin file tidak perlu seperti .git, README.md, atau direktori vendor yang bisa dibuat ulang. File besar yang tidak sengaja tercopy bisa membuat layer cache tidak efisien.
Layer caching: Urutan COPY di Dockerfile sangat penting. Salin go.mod dan go.sum dulu sebelum source code supaya go mod download bisa di-cache selama dependency tidak berubah. Ini menghemat waktu build drastis.
Non-root user: Di stage production, tambahkan instruksi USER 65534:65534 untuk menjalankan container sebagai user non-privileged. Ini mengurangi risiko security breach.
Health check: Tambahkan HEALTHCHECK di Dockerfile supaya Docker atau orchestrator tahu kapan aplikasi siap menerima traffic.
Multi-stage build adalah fitur sederhana tapi sangat powerful di Docker. Dengan memisahkan environment build dan runtime, kita bisa mendapatkan image production yang kecil, aman, dan cepat di-deploy. Konsep ini tidak terbatas untuk Go saja: Rust, C++, dan bahkan Node.js atau Python bisa mendapatkan manfaat serupa dengan memilih base image production yang tepat.
Untuk aplikasi compiled language seperti Go, gunakan scratch atau distroless sebagai base image final. Untuk aplikasi interpreted, Alpine Linux tetap menjadi pilihan solid yang menjaga ukuran tetap compact.
Dokumentasi resmi Docker mengenai multi-stage build bisa kamu baca di Docker Docs: Multi-stage builds. Juga cek referensi Docker Hub Golang image untuk varian tag yang tersedia.
Dapatkan feedback, users, dan eksposur dari komunitas kreator, developer, dan entrepreneur digital Indonesia.
Submit Produk → Pelajari Dulu