Belajar Axum - Deployment & Docker
Series/Belajar Axum/Episode 24
Episode 24 of 28

Belajar Axum - Deployment & Docker

Membawa aplikasi Axum ke produksi: multi-stage Dockerfile dengan image minimal (scratch/distroless), pengaturan worker & env, lalu deploy ke VPS atau platform managed seperti Fly.io dan Render.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Semua fitur sudah dibangun — sekarang saatnya aplikasi hidup di luar laptop kalian. Episode ini membahas deployment: containerization dengan Docker (image sekecil mungkin), pengaturan worker/runtime, dan pilihan platform — VPS mandiri atau managed seperti Fly.io dan Render.

Mengapa deployment dibahas sebagai episode tersendiri? Karena aplikasi Axum memiliki kebutuhan khas: binary Rust yang statis, konfigurasi via env vars (episode 16), graceful shutdown (episode 16), dan arsitektur reverse proxy (episode 20). Memahami cara mem-packaging binary ini dengan benar menentukan apakah deploy berjalan mulus atau jadi siklus "kenapa gagal di server padahal jalan di laptop".

Multi-Stage Dockerfile

Keunggulan Rust: binary yang dihasilkan statis dan mandiri — tidak butuh runtime interpreter. Kita bisa menghasilkan image sekecil puluhan MB, bahkan beberapa MB jika di-strip. Ini jauh lebih kecil dari image Node/Python (500MB+).

Dockerfile
# ---------- Stage 1: build ----------
FROM rust:1.80 AS builder
WORKDIR /app
 
# Dependency caching: salin Cargo.toml dulu, build deps,
# lalu salin source — perubahan source tidak mengulang build deps
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release
 
# ---------- Stage 2: runtime ----------
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/target/release/axum-api /usr/local/bin/app
 
ENV RUST_LOG=info
EXPOSE 3000
CMD ["app"]

Dua stage memisahkan build (berat, penuh toolchain) dari runtime (ringan). Lapisan yang dibawa ke produksi hanya yang ada di stage kedua.

Note

Binary Rust Rust terhubung ke libc (glibc pada rust:1.80). Karena itu stage runtime memakai debian:bookworm-slim + ca-certificates (untuk TLS — wajib agar koneksi HTTPS keluar bekerja). Target x86_64-unknown-linux-musl bisa menghasilkan binary fully static untuk scratch — kita bahas di bawah.

Scratch dan Distroless

Untuk image yang lebih minimal, dua opsi:

Distroless

Image tanpa package manager, shell, atau tool apa pun — hanya binary:

Dockerfile.distroless
FROM gcr.io/distroless/cc-debian12
WORKDIR /app
COPY --from=builder /app/target/release/axum-api /usr/local/bin/app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 3000
CMD ["app"]

Scratch (butuh musl)

Jika binary di-build dengan target musl (fully static), bisa jalan di image kosong total:

Dockerfile.scratch
# Stage 1: build dengan musl
FROM rust:1.80 AS builder
RUN rustup target add x86_64-unknown-linux-musl
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release --target x86_64-unknown-linux-musl
 
# Stage 2: image kosong
FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/axum-api /app
# jalankan sebagai user non-root
USER 1000:1000
EXPOSE 3000
ENTRYPOINT ["/app"]

Kelebihan scratch: image beberapa MB. Kekurangan: tidak ada shell — debugging masuk container jadi terbatas (docker exec tidak berguna tanpa /bin/sh). Untuk produksi modern, distroless adalah titik manis: kecil namun tetap bisa di-debug.

Warning

Di image scratch/distroless, tidak ada shell dan tidak ada user root konvensional. Jangan pasang HEALTHCHECK yang butuh curl/wget — gunakan check TCP via Docker/K8s, atau sertakan binary healthcheck statis. Dan jalankan sebagai USER 1000:1000 — jangan root di production.

Pengaturan Worker dan Runtime

Rust/tokio tidak butuh tuning worker seperti Node (cluster) atau Gunicorn — tokio sudah multi-thread out of the box. Hal yang perlu diatur:

AspekPengaturan
Thread countTOKIO_WORKER_THREADS (default = core CPU)
Worker threads--threads flag saat runtime (jika perlu)
RUST_LOGDikontrol env var (episode 17)
File descriptorBatas ulimit container harus cukup (untuk banyak koneksi WebSocket)
MemoryBatasi di orchestrator, bukan di aplikasi

Contoh pengaturan di platform managed:

Runtime env (platform managed)
RUST_LOG=axum_api=info
TOKIO_WORKER_THREADS=4
BIND_ADDR=0.0.0.0:8080

Catatan penting: BIND_ADDR harus 0.0.0.0:8080 di dalam container, bukan 127.0.0.1 — port di-expose container, dan proxy/ingress platform mengarahkan ke sana. Port 8080 sering menjadi konvensi platform (Render, Cloud Run).

Deploy ke VPS (Self-Managed)

Arsitektur VPS yang benar memakai reverse proxy dari episode 20:

100%

Langkah-langkah di VPS:

Deploy manual di VPS
# 1. Build image
docker build -t axum-api:1.0 .
 
# 2. Jalankan dengan env yang benar
docker run -d --name axum-api \
  -p 127.0.0.1:3000:3000 \
  --env-file .env.prod \
  --restart unless-stopped \
  axum-api:1.0
 
# 3. nginx di depan (konfigurasi dari episode 20)
systemctl reload nginx
 
# 4. Verifikasi
curl -sI https://api.kita.id/api/health

Poin penting: -p 127.0.0.1:3000:3000 — port hanya diekspos ke lokal, bukan ke publik; nginx yang menghadap dunia. --env-file memuat secret tanpa menaruhnya di command line history.

Untuk update: build image baru, stop, start. Untuk otomasi lengkap (CI/CD), episode 26 membahas pipeline; di sini fokus pada kebenaran langkah manual.

Deploy ke Platform Managed

Platform managed menghilangkan manajemen VPS — kalian hanya menyediakan image dan env.

Render

Render mengenali Rust/Axum; deploy via service web dengan image container atau build dari repo:

render.yaml (opsional)
services:
  - type: web
    name: axum-api
    runtime: image
    image:
      url: registry.hub.docker.com/user/axum-api:latest
    envVars:
      - key: DATABASE_URL
        sync: false   # diisi lewat dashboard
      - key: RUST_LOG
        value: info
    healthCheckPath: /api/health
    numInstances: 1

Render menangani restart, log, dan HTTPS untuk domain .onrender.com.

Fly.io

Fly membangun dari Dockerfile langsung — flyctl launch mendeteksi project:

Deploy ke Fly.io
fly launch          # deteksi Dockerfile, buat konfigurasi
fly secrets set DATABASE_URL=postgres://... JWT_SECRET=...
fly deploy

File fly.toml yang relevan:

fly.toml
[env]
  BIND_ADDR = "0.0.0.0:8080"
  RUST_LOG = "info"
 
[[services]]
  internal_port = 8080
  protocol = "tcp"
 
  [[services.ports]]
    http_port = 80
  [[services.ports]]
    http_port = 443

Fly mengirim SIGTERM saat scale-down — dan aplikasi kita (episode 16) sudah menanganinya dengan graceful shutdown. Inilah alasan kita membangun itu lebih dulu.

Tip

Platform managed mengekspos health check. Pastikan route /api/health (yang kita bangun di episode 9) mengembalikan 200 dan tidak butuh auth. Platform menandai instance "unhealthy" jika check gagal — endpoint ini menentukan keandalan restart otomatis.

Memilih Platform

KriteriaVPSManaged (Fly/Render)
KontrolPenuhTerbatas pada abstraksi
BiayaMurah, tapi kelola sendiriLebih mahal, tapi hemat waktu
DevOps effortTinggi (server, TLS, update)Rendah
ScalingManualSemi-otomatis
Cocok untukEngineer berpengalamanTim kecil / cepat rilis

Tidak ada jawaban benar universal — kalian bisa juga kombinasi: VPS untuk kontrol, managed untuk kecepatan. Episode ini membekali keduanya.

Verifikasi Deployment

Checklist sebelum menganggap deploy sukses:

Checklist post-deploy
# 1. Health check publik
curl -s -o /dev/null -w "%{http_code}\n" https://api.kita.id/api/health
 
# 2. TLS berfungsi (episode 20)
curl -sI https://api.kita.id/api/health | head -1
 
# 3. Endpoint ber-auth masih bekerja
curl -s -o /dev/null -w "%{http_code}\n" \
  https://api.kita.id/me -H "Authorization: Bearer $TOKEN"
 
# 4. Restart mulus (SIGTERM)
# deploy versi baru, amati: koneksi tidak terputus
 
# 5. Log observability masuk ke aggregator (episode 17)

Penutup

Pada episode 24 ini aplikasi kalian siap hidup di produksi:

  • Multi-stage Dockerfile: build stage + runtime debian-slim / distroless / scratch.
  • Binary statis Rust = image kecil; ca-certificates wajib untuk TLS keluar.
  • Runtime: BIND_ADDR=0.0.0.0:8080, RUST_LOG, worker threads via env.
  • VPS: container hanya diekspos ke lokal, nginx di depan.
  • Managed: Render & Fly.io dengan env + health check.
  • Checklist post-deploy lengkap.

Di episode 25 selanjutnya kita tingkatkan performa: performance & benchmarking — zero-copy, allocation tuning, biaya middleware, dan benchmark dengan criterion + load test. Sampai jumpa di episode 25!

Belajar Axum - Deployment & Docker | Belajar Axum