Kembali ke Artikel

Docker Port Mapping, Mode Detach, dan Environment Variable

Solusi IT

Container Docker berjalan di jaringan terisolasi, jadi aplikasi di dalamnya tidak otomatis bisa diakses dari browser atau curl di komputermu. Supaya bisa diakses, kamu harus publish port dengan flag -p host:container saat docker run. Itulah inti docker port mapping. Di artikel ini kita praktikkan langsung: menjalankan dua nginx sekaligus di port 8080 dan 8081, menjalankan container di background dengan -d, memberi nama dengan --name, dan menyuntik konfigurasi lewat environment variable -e.

Artikel ini bagian keempat dari seri Belajar Docker dari Nol. Tiga bagian sebelumnya membahas instalasi, konsep image vs container, dan perintah dasar. Mulai bagian ini kita masuk ke skill yang benar-benar dipakai sehari-hari saat menjalankan aplikasi web di container.

Prasyarat Sebelum Mulai

Kamu butuh Docker yang sudah terpasang dan bisa menjalankan docker run hello-world tanpa error. Di artikel ini saya memakai Docker Engine 27 di Ubuntu 24.04, tapi semua perintah berlaku sama di Docker versi 24 ke atas, termasuk di Docker Desktop untuk Windows dan macOS.

Kamu juga sebaiknya sudah nyaman dengan docker ps, docker stop, dan docker rm. Kalau belum, baca dulu bagian sebelumnya: Perintah Dasar Docker yang Wajib Dikuasai Pemula.

Kenapa Container Tidak Bisa Diakses Langsung dari Host

Coba jalankan nginx tanpa flag apa pun:

docker run nginx:1.27-alpine

Log nginx muncul di terminal, artinya web server-nya hidup. Tapi kalau kamu buka http://localhost di browser, hasilnya connection refused. Kenapa?

Secara default Docker menaruh setiap container di jaringan virtual bernama bridge. Container dapat IP internal sendiri, misalnya 172.17.0.2, dan port 80 yang dibuka nginx hanya hidup di IP internal itu. Host alias komputermu tidak otomatis meneruskan traffic ke sana. Ini disengaja. Isolasi jaringan inilah salah satu alasan container aman dijalankan berdampingan tanpa saling ganggu.

Jembatan antara dunia luar dan container adalah publish port lewat flag -p. Formatnya selalu:

-p PORT_HOST:PORT_CONTAINER

Urutannya sering ketukar, jadi hafalkan: host dulu, baru container. -p 8080:80 artinya port 8080 di komputermu diteruskan ke port 80 di dalam container. Port container mengikuti aplikasinya, nginx memang listen di 80. Port host bebas kamu pilih selama belum dipakai proses lain.

Tekan Ctrl+C dulu untuk menghentikan nginx percobaan tadi sebelum lanjut.

Praktik Docker Port Mapping: Dua Nginx di Port 8080 dan 8081

Satu image bisa dijalankan menjadi banyak container sekaligus, asalkan port host-nya berbeda. Kita buktikan dengan dua nginx:

docker run -d --name web-a -p 8080:80 nginx:1.27-alpine
docker run -d --name web-b -p 8081:80 nginx:1.27-alpine

Perhatikan: keduanya sama-sama memakai port 80 di dalam container masing-masing. Tidak bentrok, karena tiap container punya jaringan sendiri. Yang tidak boleh kembar hanya port host, makanya satu dapat 8080 dan satunya 8081.

Cek keduanya jalan:

docker ps

Output yang diharapkan kira-kira seperti ini (ID akan berbeda di komputermu):

CONTAINER ID   IMAGE               COMMAND                  STATUS         PORTS                                     NAMES
f3a1c2d4e5b6   nginx:1.27-alpine   "/docker-entrypoint.…"   Up 10 seconds  0.0.0.0:8081->80/tcp, [::]:8081->80/tcp   web-b
a9b8c7d6e5f4   nginx:1.27-alpine   "/docker-entrypoint.…"   Up 15 seconds  0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   web-a

Verifikasi dengan curl dan docker port

Tes dari host dengan curl:

curl -I http://localhost:8080

Output yang diharapkan:

HTTP/1.1 200 OK
Server: nginx/1.27.5
Content-Type: text/html
...

Ulangi untuk http://localhost:8081, hasilnya harus sama-sama 200 OK. Dua web server hidup berdampingan di satu mesin, dari satu image yang sama, tanpa konfigurasi virtual host apa pun.

Kalau lupa container mana dipetakan ke port berapa, tidak perlu menebak dari output docker ps yang padat. Ada perintah khusus:

docker port web-a

Output yang diharapkan:

80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080

Baris 0.0.0.0 untuk IPv4 dan [::] untuk IPv6. Artinya port 8080 terbuka di semua network interface host. Kalau kamu hanya ingin bisa diakses dari mesin sendiri, misalnya database untuk development, batasi ke localhost: -p 127.0.0.1:8080:80. Kebiasaan kecil ini penting saat nanti kamu deploy ke VPS yang IP-nya publik.

Mode Detach -d dan Memberi Nama dengan –name

Di perintah praktik tadi kita sudah menyelipkan dua flag baru. Sekarang kita bedah satu per satu.

Flag -d (detach) menjalankan container di background. Tanpa -d, terminal kamu tersandera oleh log container, dan menutup terminal atau menekan Ctrl+C ikut mematikan container-nya. Dengan -d, Docker hanya mencetak ID container lalu mengembalikan terminal ke kamu:

$ docker run -d --name web-c -p 8082:80 nginx:1.27-alpine
7c9e2f1a8b3d5e6f4a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f

Container tetap jalan sampai kamu hentikan sendiri dengan docker stop. Hampir semua service seperti web server, database, dan cache dijalankan dengan mode ini.

Flag --name memberi nama yang kamu tentukan sendiri. Tanpa flag ini Docker mengarang nama acak seperti quirky_banzai, lucu tapi menyulitkan. Bandingkan dua perintah ini:

docker stop quirky_banzai
docker stop web-a

Nama yang jelas membuat stop, logs, port, dan perintah lain gampang diketik dan gampang diingat. Satu aturan penting: nama container harus unik. Selama container bernama web-a masih ada, meskipun statusnya sudah exited, kamu tidak bisa membuat container baru dengan nama sama sebelum menghapusnya dengan docker rm web-a.

Menyuntik Konfigurasi Lewat Environment Variable -e

Aplikasi yang baik tidak menulis konfigurasi mati di dalam kode. Password database, mode debug, dan URL API biasanya dibaca dari environment variable. Docker mendukung pola ini lewat flag -e NAMA=nilai saat run.

Bukti paling sederhana, jalankan container Alpine sekali pakai yang tugasnya cuma mencetak environment:

docker run --rm -e APP_ENV=production -e APP_PORT=3000 alpine:3.20 env

Output yang diharapkan:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=b2c3d4e5f6a7
APP_ENV=production
APP_PORT=3000
HOME=/root

Dua variabel yang kita suntik ikut muncul. Aplikasi apa pun di dalam container bisa membacanya seperti environment variable biasa.

Contoh yang lebih nyata adalah image resmi database. MySQL misalnya, wajib diberi tahu password root lewat environment variable, kalau tidak dia menolak start:

docker run -d --name db-latihan \
  -e MYSQL_ROOT_PASSWORD=rahasia123 \
  -e MYSQL_DATABASE=toko_online \
  -p 3306:3306 \
  mysql:8.4

Perintah di atas menjalankan MySQL 8.4 dengan password root rahasia123 dan langsung membuatkan database bernama toko_online. Variabel apa saja yang dikenali sebuah image selalu tercantum di halaman image tersebut di Docker Hub, jadi biasakan membaca bagian environment variable sebelum memakai image baru.

Pola konfigurasi lewat environment variable ini bukan sekadar materi latihan. Di Arrazy, backend Go dan Laravel yang kami bangun untuk sistem aplikasi klien membaca semua kredensial dari environment variable, sehingga image yang sama bisa dipakai di server development maupun production hanya dengan mengganti nilai variabelnya.

Beda EXPOSE di Image dengan -p Saat Run

Ini sumber salah paham paling sering di topik port. Saat kamu melihat isi sebuah Dockerfile atau halaman Docker Hub, sering ada baris EXPOSE 80. Banyak pemula mengira baris itu yang membuka port ke host. Bukan.

EXPOSE hanyalah metadata, semacam catatan dari pembuat image: aplikasi di dalam image ini listen di port 80. Dia tidak membuka apa pun ke host. Tanpa -p, container dengan EXPOSE 80 tetap tidak bisa diakses dari luar.

Aspek EXPOSE (di Dockerfile) -p (saat docker run)
Kapan ditulis Saat image dibuat Saat container dijalankan
Efek ke host Tidak ada, hanya dokumentasi Port host benar-benar diteruskan ke container
Wajib untuk akses dari luar Tidak Ya
Bisa diubah pengguna image Tidak, sudah tertanam di image Ya, bebas pilih port host tiap run

Ada satu titik temu antara keduanya: flag -P (huruf besar). Flag ini menyuruh Docker mem-publish semua port yang tercantum di EXPOSE ke port acak di host:

docker run -d --name web-acak -P nginx:1.27-alpine
docker port web-acak

Output yang diharapkan, angka portnya akan berbeda-beda:

80/tcp -> 0.0.0.0:32768
80/tcp -> [::]:32768

Untuk latihan dan production, -p huruf kecil dengan port eksplisit hampir selalu lebih enak karena portnya bisa ditebak. Anggap -P sebagai trivia yang perlu kamu tahu supaya tidak bingung saat menemuinya di tutorial lain.

Troubleshooting: Error yang Sering Muncul Saat Publish Port

Error bind: address already in use

docker: Error response from daemon: failed to set up container networking:
driver failed programming external connectivity on endpoint web-a:
failed to bind host port 0.0.0.0:8080: address already in use

Artinya port host yang kamu minta sudah dipakai proses lain, bisa container lain, bisa aplikasi biasa seperti Apache atau aplikasi Node yang lupa dimatikan. Cari pelakunya dengan:

sudo ss -ltnp | grep :8080

Output contohnya:

LISTEN 0 4096 *:8080 *:* users:(("docker-proxy",pid=51234,fd=7))

Kalau yang muncul docker-proxy, pelakunya container lain, cek dengan docker ps lalu stop container yang bentrok. Kalau proses biasa, matikan prosesnya atau lebih aman ganti port host-mu, misalnya jadi -p 8090:80. Di Windows dan macOS, gunakan netstat -ano | findstr :8080 (Windows) atau lsof -i :8080 (macOS) untuk mencari proses yang sama.

Error the container name is already in use

docker: Error response from daemon: Conflict. The container name "/web-a" is already
in use by container "a9b8c7d6e5f4". You have to remove (or rename) that container...

Nama container harus unik, termasuk container yang sudah berhenti. Lihat semuanya dengan docker ps -a, lalu hapus yang lama dengan docker rm web-a. Kalau masih jalan, hentikan dulu dengan docker stop web-a.

curl connection refused padahal container jalan

Tiga penyebab paling umum. Pertama, kamu lupa flag -p sama sekali, cek dengan docker port nama-container, kalau outputnya kosong berarti memang tidak ada port yang dipublish, hapus container lalu run ulang dengan -p. Kedua, kamu mengakses port yang salah, misalnya curl localhost:80 padahal mapping-nya 8080:80, yang diakses dari host selalu port sebelah kiri. Ketiga, urutan -p terbalik, -p 80:8080 berbeda makna dengan -p 8080:80.

Port sudah dipublish tapi aplikasi belum siap

Khusus database seperti MySQL, docker ps bisa menunjukkan status Up tapi koneksi masih ditolak beberapa detik pertama. Ini bukan masalah port mapping. Proses inisialisasi database memang butuh waktu. Tunggu sebentar dan cek lognya dengan docker logs db-latihan sampai muncul tulisan ready for connections.

Bersih-bersih dan Lanjut ke Bagian Berikutnya

Sebelum lanjut, rapikan semua container latihan supaya tidak makan resource:

docker stop web-a web-b web-c web-acak db-latihan
docker rm web-a web-b web-c web-acak db-latihan

Sampai sini kamu sudah pegang empat senjata baru: -p untuk membuka akses dari host ke container, -d untuk menjalankan di background, --name untuk penamaan yang waras, dan -e untuk menyuntik konfigurasi. Kombinasi keempatnya adalah pola docker run yang akan terus kamu pakai sampai bagian akhir seri ini.

Bagian berikutnya membahas cara masuk ke dalam container yang sedang berjalan: “Cara Masuk ke Container: docker exec, logs, dan Debugging”. Di sana kamu belajar membuka shell di dalam container, membaca log dengan benar, dan mendiagnosis container yang tiba-tiba mati. Artikelnya terbit menyusul, pantau daftar lengkapnya di halaman hub Belajar Docker dari Nol.

Referensi

BACA JUGA

Artikel Lainnya di Kategori Docker

Ingin Membaca Artikel Lainnya?

Temukan lebih banyak insight dan tips tentang teknologi dan bisnis digital.

Lihat Semua Artikel