DNS Internal #

Salah satu fitur Docker yang sering dianggap sepele tapi sangat krusial dalam arsitektur container adalah DNS internal. Berkat DNS ini, container bisa saling menemukan satu sama lain hanya dengan nama service atau container — tidak perlu tahu IP address, tidak perlu hardcode, tidak perlu konfigurasi tambahan.

Tanpa DNS internal, membangun sistem multi-container akan jauh lebih rapuh. Setiap kali container restart, IP-nya bisa berubah. Setiap kali ada scaling, IP baru muncul. Tanpa service discovery yang stabil, satu perubahan kecil bisa meruntuhkan seluruh arsitektur.

Docker menyediakan DNS internal yang bekerja otomatis pada user-defined network. Ia embedded di Docker Engine, super cepat, dan tidak butuh konfigurasi manual. Artikel ini membahas DNS internal secara mendalam: cara kerjanya, kapan aktif, bagaimana ia berinteraksi dengan Docker Compose, dan cara troubleshoot saat resolusi nama gagal.

Apa Itu DNS Internal Docker? #

DNS internal Docker adalah DNS server bawaan yang berjalan di dalam Docker Engine. Saat container menjalankan query DNS, query tersebut secara otomatis diarahkan ke server ini lewat resolver 127.0.0.11.

flowchart LR
    A[Container: app] -->|nslookup db| B[127.0.0.11]
    B -->|Cek: db container di network?| C{Docker DNS}
    C -- Ya, ketemu --> D[Return: 172.18.0.3]
    C -- Tidak --> E[Forward ke external DNS]
    E --> F[Google / Cloudflare DNS]
    F --> E
    E --> A

Karakteristik utama:

  • Resolver otomatis — container tidak perlu konfigurasi /etc/resolv.conf manual.
  • Resolve container namedb, api, web bisa dipakai sebagai hostname.
  • Resolve service name — di Compose, db merujuk ke service db.
  • Round-robin — untuk multi-container dengan nama yang sama, return semua IP.
  • Forward external — query untuk domain publik diteruskan ke DNS eksternal.
Resolver-nya adalah 127.0.0.11. Ini adalah IP internal yang hanya bisa diakses dari dalam container. Saat container menjalankan cat /etc/resolv.conf, biasanya akan muncul nameserver 127.0.0.11. Jangan bingung — 127.0.0.11 itu bukan host’s localhost, tapi embedded DNS Docker di dalam container namespace.

Alamat DNS Internal: 127.0.0.11 #

Setiap container yang dibuat dengan network modern (user-defined) akan memiliki 127.0.0.11 sebagai resolver. Cara melihatnya:

docker run --rm --network app-net alpine cat /etc/resolv.conf

Output:

nameserver 127.0.0.11
options ndots:0
  • nameserver 127.0.0.11 — semua query DNS diarahkan ke sini.
  • options ndots:0 — jumlah dot minimum sebelum query dianggap FQDN.
ndots:0 itu penting: artinya Docker DNS akan mencoba resolve nama pendek (seperti db) langsung, tanpa menambah domain suffix. Ini yang membuat service discovery via nama pendek bekerja. Kalau ndots lebih tinggi, db mungkin akan dianggap relatif dan ditambahkan suffix domain dulu.

Kapan DNS Internal Aktif? #

DNS internal Docker hanya bekerja optimal di user-defined network. Ini adalah salah satu perbedaan terbesar antara default bridge dan user-defined bridge.

Network Type DNS Internal? Container name resolve?
Default bridge (bridge) ❌ Tidak ada ❌ Tidak (harus pakai IP atau --link)
User-defined bridge ✅ Ya ✅ Ya
Host network ❌ Tidak ❌ Tidak (pakai host’s resolv.conf)
None network ❌ Tidak ❌ Tidak
Overlay (Swarm) ✅ Ya ✅ Ya (di Swarm scope)
Custom network plugin Tergantung plugin Tergantung plugin
Jebakan paling umum: Developer menjalankan container di default bridge, lalu heran kenapa ping db gagal atau curl http://api:8080 dapat error. Ini bukan bug — ini by design. Default bridge tidak punya DNS internal. Solusinya: docker network create my-net lalu jalankan container di network itu.

Eksperimen: Default vs User-Defined #

# Default bridge - DNS tidak aktif
docker run -d --name db --network bridge postgres
docker run -it --rm --network bridge alpine sh
/ # nslookup db
# nslookup: can't resolve 'db'
# (gagal)

# User-defined network - DNS aktif
docker network create app-net
docker run -d --name db --network app-net postgres
docker run -it --rm --network app-net alpine sh
/ # nslookup db
# Server:    127.0.0.11
# Address:   127.0.0.11:53
# Non-authoritative answer:
# Name: db
# Address: 172.18.0.2
# (berhasil!)

Alur Resolusi DNS: Step by Step #

Apa yang terjadi saat container melakukan query ke db:

sequenceDiagram
    participant App as Aplikasi (container)
    participant R as /etc/resolv.conf
    participant DNS as 127.0.0.11
    participant E as External DNS

    App->>R: gethostbyname("db")
    R->>DNS: Query UDP 127.0.0.11:53
    DNS->>DNS: Cek: "db" container di network ini?
    alt Container ketemu
        DNS-->>App: Return 172.18.0.3
    else Container tidak ketemu
        DNS->>E: Forward ke 8.8.8.8
        E-->>DNS: Response (atau NXDOMAIN)
        DNS-->>App: Forward response
    end

Yang menarik: selama container dan service ada di network yang sama, resolve berhasil dengan latency sangat rendah (sub-millisecond). Tidak ada hop ke external DNS, tidak ada round-trip internet.

flowchart TB
    subgraph Internal["Resolve Lokal (instant)"]
        A[app nslookup db] --> B[127.0.0.11]
        B --> C[Return IP container]
    end

    subgraph External["Resolve Eksternal (forward)"]
        D[app nslookup google.com] --> E[127.0.0.11]
        E --> F[Forward ke 8.8.8.8]
        F --> G[Return IP google]
    end

Kapan Docker DNS gagal?

  • Container di network berbeda (tidak ada connectivity).
  • Container dengan nama yang sama di network berbeda (ambigu).
  • Typo di nama service.
  • aliases tidak di-set dengan benar di Compose.
  • links (legacy) yang sudah deprecated — jangan dipakai.

Round-Robin untuk Multi-Container #

Saat satu service dijalankan sebagai multiple container (misal api-1, api-2, api-3), Docker DNS bisa resolve semua IP sekaligus untuk load balancing round-robin.

# Start 3 container dengan nama yang sama (alias)
docker network create --driver bridge scale-net

docker run -d --name api-1 --network scale-net --network-alias api my-api
docker run -d --name api-2 --network scale-net --network-alias api my-api
docker run -d --name api-3 --network scale-net --network-alias api my-api

# Query dari container lain
docker run -it --rm --network scale-net alpine sh
/ # nslookup api
# Name: api
# Address: 172.18.0.2
# Address: 172.18.0.3
# Address: 172.18.0.4
# Docker Compose - scale
services:
  api:
    image: my-api
    networks:
      - app-net
    deploy:
      replicas: 3
flowchart LR
    Q[Container: client] -->|gethostbyname api| D[127.0.0.11]
    D --> R1[172.18.0.2]
    D --> R2[172.18.0.3]
    D --> R3[172.18.0.4]
    Q --> R1
    Q --> R2
    Q --> R3

Round-robin di sini adalah DNS-level — klien menerima list IP dan biasanya implementasikan load balancing mereka sendiri. Ini berbeda dengan load balancing di level proxy (Nginx, HAProxy) yang lebih advanced.

Penting: round-robin DNS bukan load balancing yang sempurna. Klien mungkin cache hasil dan tidak benar-benar round-robin. Untuk production, lebih baik pakai reverse proxy (Traefik, Nginx) atau orchestrator (K8s, Swarm) yang punya load balancing lebih pintar.

DNS Internal di Docker Compose #

Docker Compose secara otomatis mengaktifkan DNS internal berbasis service name. Setiap service otomatis ter-resolve.

version: "3.9"
services:
  web:
    image: nginx
  api:
    image: my-api
    environment:
      DATABASE_URL: postgres://user:pass@db:5432/mydb
      REDIS_URL: redis://cache:6379
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: pass
  cache:
    image: redis

api bisa akses db dengan db:5432, dan cache dengan cache:6379tanpa konfigurasi tambahan. Compose otomatis:

  • Membuat satu network default untuk project.
  • Meng-connect-kan semua service.
  • Mengaktifkan DNS internal.

Alias Tambahan dengan aliases #

services:
  db:
    image: postgres
    networks:
      default:
        aliases:
          - database
          - postgres-db
          - primary-db

Sekarang db bisa dipanggil dengan: db, database, postgres-db, atau primary-db. Berguna untuk:

  • Migrasi — service lama pakai nama database, service baru pakai db. Pakai alias agar kompatibel.
  • Abstraksi — kode aplikasi pakai db (generic), tapi sebenarnya underlying service bisa diganti.
  • Multiple environmentdev-db di development, prod-db di production.

External DNS Resolution #

DNS internal Docker juga meneruskan query untuk domain eksternal (public) ke upstream DNS. Kamu bisa set custom DNS server untuk semua container.

services:
  app:
    image: my-app
    dns:
      - 1.1.1.1
      - 8.8.8.8
    dns_search:
      - example.com
      - internal.local

Atau set di level daemon (semua container):

// /etc/docker/daemon.json
{
  "dns": ["1.1.1.1", "8.8.8.8"],
  "dns-search": ["example.com"]
}
sudo systemctl restart docker
Hati-hati: kalau kamu set custom DNS di daemon dan lupa restart Docker, container baru akan tetap pakai setting lama. Restart daemon setiap kali ubah daemon.json.

Verifikasi DNS Internal #

Cara paling efektif memastikan DNS internal aktif:

# 1. Cek resolv.conf
docker exec <container> cat /etc/resolv.conf
# Harus ada nameserver 127.0.0.11

# 2. Cek hostname resolution
docker exec <container> nslookup <target-container>
# atau
docker exec <container> getent hosts <target-container>

# 3. Test connectivity via hostname
docker exec <container> ping -c 1 <target-container>
docker exec <container> wget -qO- http://<target-service>:8080

# 4. Lihat network container
docker inspect <container> --format '{{json .NetworkSettings.Networks}}'

Troubleshooting DNS Gagal #

1. “Could not resolve host” #

Penyebab umum:

  • Container di default bridge.
  • Container di network berbeda.
  • Typo di nama.
# Debug: cek network container
docker inspect <container> | grep -A 5 NetworkSettings

# Pastikan ada di network yang sama
docker network connect app-net <container>

2. DNS Sangat Lambat #

Penyebab umum:

  • Forward ke external DNS yang lambat.
  • ndots setting yang membuat query berulang.
# Cek query time
docker exec <container> dig +stats <hostname>
# Lihat "Query time"

# Set DNS yang lebih cepat
docker run --dns 1.1.1.1 --dns 8.8.8.8 my-app

3. Resolve间歇性 Gagal #

Penyebab umum:

  • Container restart dengan IP baru.
  • Network overlay (Swarm) dengan quorum issue.
  • DNS cache di aplikasi (Java DNS cache terkenal stubborn).
# Test resolve berkali-kali
docker exec <container> sh -c 'for i in 1 2 3 4 5; do nslookup db; done'

DNS Cache di Aplikasi #

Beberapa aplikasi dan runtime punya DNS cache internal yang menyimpan hasil query terlalu lama. Ini bisa menyebabkan masalah saat container restart dengan IP baru.

Runtime Default TTL Catatan
Go 30 detik (default) Cukup cepat
Java 30+ detik (cache forever until JVM restart) ⚠️ Masalah umum
Node.js 0 (no cache by default) Bagus
Python Library-dependent Bervariasi
PHP-FPM 60 detik (PHP bawaan) Cukup
Java DNS cache adalah jebakan terkenal: JVM menyimpan DNS result selamanya (atau sampai TTL setting diubah). Untuk containerized Java apps, set -Dsun.net.inetaddr.ttl=30 atau pakai library DNS resolver lain (dnsjava). Tanpa ini, restart container bisa membuat app stuck ke IP lama.

Best Practice DNS Internal #

1. Selalu Pakai User-Defined Network #

docker network create app-net
docker run --network app-net my-service

Tanpa user-defined network, DNS internal tidak aktif.

2. Gunakan Service Name (Compose), Bukan Container Name #

# ✓ Solusi: service name stabil
services:
  api:
    image: my-api
    environment:
      DATABASE_HOST: db  # service name, stabil

# ✗ Anti-pattern: container name rapuh
services:
  db:
    container_name: my-database  # tidak bisa di-scale
    image: postgres

3. Jangan Hardcode IP #

# ✗ Anti-pattern
DATABASE_HOST=172.18.0.3  # bisa berubah

# ✓ Solusi
DATABASE_HOST=db  # DNS resolve

4. Set DNS yang Konsisten #

// /etc/docker/daemon.json
{
  "dns": ["1.1.1.1", "8.8.8.8"]
}

DNS eksternal yang konsisten membantu reproducibility.

5. Test DNS Resolution di CI/CD #

# Health check yang verify DNS
docker run --rm --network app-net my-app \
  sh -c "getent hosts db && getent hosts cache"

DNS Internal di Swarm dan Kubernetes #

Docker Swarm memiliki DNS service sendiri yang lebih canggih — Swarm Mode DNS. Service yang dideploy di Swarm otomatis ter-resolve dan di-load-balance.

Kubernetes punya CoreDNS yang berjalan sebagai pod di cluster. Service name ter-resolve via Service object. Lebih advanced dari Docker DNS — ada health check, weighted load balancing, dan integration dengan service mesh.

flowchart TB
    subgraph Docker["Docker / Compose"]
        D1[127.0.0.11]
    end
    subgraph Swarm["Docker Swarm"]
        S1[Swarm DNS<br/>tasks.service_name]
    end
    subgraph K8s["Kubernetes"]
        K1[CoreDNS<br/>service.namespace.svc.cluster.local]
    end
    Docker --> Swarm --> K8s

Konsep yang sama, implementasi yang berbeda. Untuk development, Docker DNS cukup. Untuk production multi-host, Swarm atau K8s memberikan fitur lebih.


DNS Internal vs Service Discovery Tools #

Aspek Docker DNS Consul etcd K8s CoreDNS
Built-in ✅ (default)
Multi-host ❌ (kecuali Swarm)
Health check ❌ (butuh library)
Load balancing Round-robin basic
Setup complexity Nol Tinggi Tinggi Sedang
Cocok untuk Single-host Compose Multi-host tradisional Multi-host K8s pra-era Kubernetes
Untuk kebanyakan kasus, Docker DNS cukup. Ia gratis, otomatis, dan zero-config. Baru pertimbangkan Consul/etcd/CoreDNS saat kamu butuh health check based routing, multi-host, atau service mesh integration.

Ringkasan #

  • DNS internal Docker adalah server DNS embedded di Docker Engine, diakses lewat 127.0.0.11. Resolve container name dan service name di user-defined network.
  • Resolver otomatis — container punya 127.0.0.11 di /etc/resolv.conf tanpa konfigurasi manual.
  • Hanya aktif di user-defined network. Default bridge TIDAK punya DNS internal — ini jebakan paling umum.
  • Round-robin — multi-container dengan alias yang sama akan return semua IP, klien implementasi load balancing.
  • External query diteruskan ke upstream DNS (default: dari host). Bisa di-override per-container atau di level daemon.
  • Docker Compose otomatis mengaktifkan DNS internal berbasis service name. Tiap service bisa diakses via nama service-nya.
  • Aliases di Compose memungkinkan satu service punya banyak nama (untuk kompatibilitas, abstraksi, multiple environment).
  • Anti-pattern: pakai default bridge, hardcode IP, pakai container_name di Compose, pakai --link (deprecated).
  • Troubleshooting: cek /etc/resolv.conf, nslookup, getent hosts, dan pastikan container di network yang sama.
  • Java DNS cache adalah jebakan terkenal — set TTL rendah untuk containerized Java apps.
  • Best practice: selalu user-defined network, pakai service name (bukan container_name), jangan hardcode IP, test DNS resolution di CI/CD.
  • Untuk multi-host production, pertimbangkan Swarm (built-in) atau K8s (CoreDNS). Docker DNS cukup untuk single-host Compose.
  • DNS internal bukan service discovery global. Untuk health check based routing, multi-DC, dan advanced use case, butuh Consul, etcd, atau service mesh.

← Sebelumnya: Port Mapping & Exposure   Berikutnya: Container-to-Container →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact