Dalam dunia pengembangan, kontainer menjadi cara standar untuk membungkus aplikasi beserta seluruh keperluannya agar dapat berjalan identik di berbagai lingkungan. Sebuah kontainer adalah unit isolasi yang memanfaatkan dua fitur kernel Linux: namespaces untuk membatasi pandangan proses terhadap sistem berkas, jaringan, dan pengenal proses, serta cgroups untuk mengatur jatah CPU, memori, dan I/O. Tidak seperti mesin virtual yang meniru perangkat keras lengkap dan harus membawa kernel sendiri, kontainer berbagi kernel dengan sistem operasi induk sehingga ukuran image-nya sangat kecil—umumnya puluhan megabita—dan mampu menyala dalam hitungan detik. Tiga komponen utama dalam ekosistem ini adalah image, kontainer, dan registry. Image merupakan cetak biru statis yang tersusun dari tumpukan lapisan read-only; setiap lapisan merekam satu instruksi spesifik seperti menyalin berkas atau memasang paket. Ketika dijalankan, satu lapisan tulis (read-write) ditambahkan di atas tumpukan tersebut; semua perubahan yang terjadi selama kontainer hidup hanya tersimpan di lapisan ini dan akan hilang begitu kontainer dihapus. Registry berfungsi sebagai gudang penyimpanan dan distribusi image, contohnya Docker Hub dan Quay. Di antara berbagai mesin kontainer, Podman (Pod Manager) menonjol karena sepenuhnya mematuhi standar OCI, sangat kompatibel dengan perintah Docker (hingga banyak pengguna cukup membuat alias alias docker=podman), dan memiliki dua keunggulan utama: arsitektur daemonless tanpa proses latar belakang istimewa seperti dockerd, serta dukungan mode rootless yang memungkinkan pengguna biasa menjalankan kontainer secara aman—bila terjadi kebocoran, penyerang hanya mendapat hak akses pengguna biasa di host, bukan kendali penuh root. Inilah yang menjadikan Podman pilihan tepat untuk memulai.

Untuk memulai perjalanan dengan Podman, pada distribusi Debian atau Ubuntu cukup jalankan sudo apt update && sudo apt install podman -y. Setelah selesai, verifikasi instalasi dengan podman version; semua komponen kini siap digunakan. Alur kerja paling dasar dimulai dari mencari dan menjalankan image yang sudah tersedia. Gunakan podman search docker.io/busybox untuk menemukan image kecil bernama BusyBox, lalu tarik dengan podman pull docker.io/library/busybox. Setelah terunduh, Anda dapat langsung menjalankan kontainer interaktif melalui podman run -it docker.io/library/busybox; Anda akan masuk ke sesi terminal di dalam kontainer. Untuk memantau kontainer yang sedang berjalan, gunakan podman ps. Kontainer dapat dihentikan dengan podman stop <id_container> dan dihapus dengan podman rm <id_container>. Jika image sudah tidak diperlukan, bersihkan dengan podman rmi <id_image>. Langkah-langkah ini sudah cukup untuk mulai bereksperimen.

Ketika image publik belum memenuhi kebutuhan, Podman memungkinkan Anda membangun image kustom menggunakan berkas Containerfile. Strategi multi-stage build sangat dianjurkan agar image akhir hanya berisi biner aplikasi tanpa sistem operasi tambahan, sehingga ukurannya sekecil mungkin. Sebagai contoh, kita akan membangun aplikasi REST API kecil dengan bahasa Go yang menangani endpoint /tasks. Pertama, siapkan direktori proyek baru dan inisialisasi modul Go: mkdir tasks-api && cd tasks-api, lalu go mod init tasks-api. Selanjutnya tulis kode aplikasi di main.go seperti berikut:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type Task struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

var (
    tasks  []Task
    nextID = 1
)

func getTasks(w http.ResponseWriter, _ *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(tasks)
}

func createTask(w http.ResponseWriter, r *http.Request) {
    var task Task
    if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    task.ID = nextID
    nextID++
    tasks = append(tasks, task)
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(task)
}

func taskHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        getTasks(w, r)
    case http.MethodPost:
        createTask(w, r)
    default:
        http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
    }
}

func main() {
    http.HandleFunc("/tasks", taskHandler)
    log.Println("http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Kode ini mendefinisikan struktur Task, dua handler untuk membaca dan menambah tugas, serta router sederhana yang membedakan metode GET dan POST. Setelah kode siap, buat Containerfile di direktori yang sama:

FROM golang:1.26-alpine AS builder
WORKDIR /app
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o tasks .

FROM scratch
WORKDIR /app
COPY --from=builder /app/tasks .
EXPOSE 8080
CMD ["./tasks"]

Pertama (builder) menggunakan image golang:1.26-alpine untuk mengunduh dependensi dan mengkompilasi kode menjadi biner statis bernama tasks. Dengan menyalin go.mod lebih dulu sebelum seluruh kode, Podman dapat men-cache lapisan unduhan modul sehingga proses build berikutnya lebih cepat jika tidak ada perubahan di go.mod. Tahap kedua dimulai dari image kosong scratch yang sama sekali tidak memiliki shell atau utilitas; biner hasil kompilasi disalin ke direktori /app, port 8080 dideklarasikan sebagai metadata, dan perintah CMD akan menjalankan aplikasi begitu kontainer dimulai. Bangun image dengan podman build -t tasks .; setelah selesai, image lokal localhost/tasks siap digunakan. Jalankan kontainer di latar belakang dengan podman run -d -p 8080:8080 localhost/tasks, lalu uji API: curl http://localhost:8080/tasks. Aplikasi kini berjalan di dalam kontainer yang ukurannya sangat kecil.

Setelah image kustom tercipta, Anda dapat membagikannya ke registry agar dapat diakses dari mana saja. Masuk ke Docker Hub dengan podman login docker.io, beri tag baru yang mengarah ke repositori Anda (contoh podman tag localhost/tasks docker.io/username/tasks), kemudian unggah dengan podman push docker.io/username/tasks. Pengguna lain kini dapat menarik image tersebut menggunakan podman pull docker.io/username/tasks.

Dalam praktik nyata, satu aplikasi sering terdiri dari beberapa kontainer—misalnya backend dan reverse proxy—yang harus berjalan bersama. Podman menyediakan dukungan orchestration sederhana melalui perintah podman compose yang memanfaatkan penyedia compose seperti podman-compose atau docker-compose. Pastikan salah satunya terpasang, jika belum bisa diinstal dengan sudo apt install podman-compose. Mari kita satukan backend Go yang telah dibangun dengan Caddy sebagai reverse proxy. Buat berkas Caddyfile dengan isi :80 { reverse_proxy backend:8080 }—ini akan meneruskan semua permintaan yang masuk ke kontainer bernama backend pada port 8080. Kemudian buat docker-compose.yml:

services:
  backend:
    build: .
    expose:
      - "8080"

  caddy:
    image: caddy:alpine
    ports:
      - "8081:80"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
    depends_on:
      - backend

Berkas ini mendefinisikan dua layanan. Layanan backend dibangun dari Containerfile di direktori proyek dan hanya membuka port 8080 di dalam jaringan virtual yang dibuat otomatis oleh Compose, sehingga tidak dapat diakses langsung dari host. Layanan caddy menggunakan image caddy:alpine, memetakan port 8081 di host ke port 80 di dalam kontainer—pemetaan di atas port 1024 diperlukan karena mode rootless Podman tidak mengizinkan pengikatan ke port sistem. Konfigurasi Caddy dipasang sebagai volume read-only (:ro) demi keamanan, dan depends_on memastikan backend dinyalakan terlebih dahulu. Jalankan seluruh tumpukan dengan podman compose up -d. Caddy kini mendengarkan di localhost:8081 dan meneruskan permintaan ke backend; Anda bisa mengakses aplikasi melalui http://localhost:8081/tasks. Untuk menghentikan dan membersihkan semua layanan beserta jaringan virtualnya, gunakan podman compose down.

Selama pengoperasian, masalah bisa saja muncul. Jika kontainer gagal berjalan, langkah pertama selalu memeriksa log dengan podman logs <nama_container>; hampir semua kegagalan terekam di sana. Bila belum jelas, lakukan inspeksi mendetail menggunakan podman inspect <nama_container> dan perhatikan exit code serta variabel lingkungan yang diterapkan. Untuk penyelidikan langsung, Anda bisa masuk ke dalam kontainer yang bermasalah dari image yang sama dengan mengganti entrypoint: podman run -it --entrypoint /bin/sh <nama_image>. Dari dalam, Anda dapat memeriksa berkas, menjalankan perintah, dan mendiagnosis lebih lanjut. Agar lingkungan tetap bersih, lakukan pembersihan berkala dengan podman system prune -a; perintah ini akan menghapus semua image, kontainer, dan volume yang sudah tidak terpakai, menjaga sistem tetap ringan.