Ringkasan Utama
Cloudflare Tunnel menjalankan daemon kecil bernama cloudflared yang menghubungi Cloudflare secara keluar, jadi Anda mengekspos layanan homelab atau VPS tanpa satu pun port masuk terbuka. Anda memetakan hostname ke layanan lokal di konfigurasi ingress, lalu menempatkan kebijakan Zero Trust Access di depan untuk mewajibkan login berbasis identitas sebelum siapa pun mencapai aplikasi.
Selama bertahun-tahun jawaban standar untuk mengakses server rumah adalah port forwarding: membuka lubang di router, mengekspos port 443, dan berharap konfigurasi reverse proxy serta TLS Anda tidak pernah bermasalah. Di koneksi rumahan Anda sering tidak bisa melakukannya sama sekali, karena ISP berada di belakang carrier-grade NAT dan tidak pernah memberi Anda IP publik yang bisa dirutekan. Di VPS Anda bisa membuka port, tetapi setiap port terbuka adalah layar login yang bisa digempur oleh seluruh internet.
Cloudflare Tunnel membalik arahnya. Alih-alih internet menghubungi Anda, sebuah agen ringan menghubungi edge Cloudflare secara keluar dan menjaga koneksi persisten tetap terbuka. Trafik untuk hostname Anda tiba di Cloudflare, dikirim melalui tunnel keluar yang sudah ada itu, dan baru kemudian mencapai layanan Anda. Firewall Anda tetap tertutup penuh di sisi masuk. Saya menjalankan ini di depan beberapa dashboard internal dan sebuah layanan di VPS saya, dan diam-diam ini menggantikan setiap setup reverse-proxy-plus-open-port yang dulu harus saya awasi.
Cara tunnel bekerja tanpa port terbuka
Daemon-nya, cloudflared, membangun beberapa koneksi keluar ke pusat data Cloudflare terdekat melalui QUIC (UDP 7844) atau HTTPS. Karena ini koneksi keluar yang sudah diizinkan NAT dan firewall Anda, tidak ada yang perlu dibuka. Ketika permintaan mengenai hostname Anda, Cloudflare merutekannya ke dalam tunnel dan cloudflared mem-proxy-kannya ke alamat lokal seperti localhost pada suatu port. Layanan origin tidak pernah melihat internet publik secara langsung, dan port-nya tidak pernah terikat ke antarmuka publik.
- Tanpa aturan firewall masuk — tunnel bersifat keluar saja, jadi ia bekerja di belakang CGNAT maupun firewall VPS yang terkunci.
- TLS diterminasi di edge Cloudflare, jadi Anda tidak mengelola sertifikat di origin untuk hostname publik.
- Filter DDoS dan bot terjadi di edge sebelum trafik masuk ke tunnel Anda.
- Alamat IP origin Anda tidak pernah terekspos di DNS — record menunjuk ke Cloudflare, bukan ke Anda.
Memasang cloudflared dan membuat tunnel
Jalur klasik menggunakan CLI untuk autentikasi, membuat tunnel bernama, dan menghasilkan file kredensial. Autentikasi membuka browser sehingga Anda bisa memilih zona mana yang dimiliki tunnel. Membuat tunnel menulis file kredensial JSON yang namanya adalah UUID tunnel — perlakukan file itu seperti kata sandi.
# Install on Debian/Ubuntu (official Cloudflare repo)
sudo mkdir -p /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
https://pkg.cloudflare.com/cloudflared any main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update && sudo apt-get install -y cloudflared
# Authenticate (opens a browser to pick your zone)
cloudflared tunnel login
# Create a named tunnel -> writes ~/.cloudflared/<UUID>.json
cloudflared tunnel create homelab
# Route a DNS hostname to this tunnel
cloudflared tunnel route dns homelab grafana.example.comLebih baik gunakan metode berbasis token untuk kontainer dan setup cepat. Di dashboard bagian Zero Trust, buat tunnel dan ia memberi Anda satu token konektor — tanpa file kredensial yang harus dipindah-pindah, dan ingress rules bisa diedit di UI. Metode config-file CLI di bawah lebih baik jika Anda ingin aturan routing berada di version control.
File config.yml dan ingress rules
Ketika Anda mengelola tunnel secara lokal, sebuah file konfigurasi memetakan setiap hostname publik ke layanan lokal. Cloudflare mengevaluasi ingress rules dari atas ke bawah dan berhenti pada kecocokan pertama, jadi urutan penting: taruh hostname spesifik lebih dulu dan wildcard luas terakhir. Aturan yang benar-benar sering menjebak orang adalah yang terakhir.
# ~/.cloudflared/config.yml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /home/matthews/.cloudflared/6ff42ae2-...json
ingress:
# Specific hostnames first
- hostname: grafana.example.com
service: http://localhost:3000
- hostname: uptime.example.com
service: http://localhost:3001
# A whole app behind one subdomain tree
- hostname: "*.lab.example.com"
service: http://localhost:8080
# REQUIRED catch-all — must be last, no hostname
- service: http_status:404Aturan ingress terakhir harus berupa catch-all tanpa hostname, misalnya mengembalikan status HTTP 404. Tanpa itu cloudflared menolak untuk memulai dan mencetak error validasi. Ini menjebak hampir semua orang pada konfigurasi pertama mereka — jika daemon tidak mau boot, periksa apakah aturan terakhir tidak punya kunci hostname.
Setelah konfigurasi tervalidasi, jalankan sebagai service agar bertahan saat reboot. Di host systemd, cloudflared memasang unit service untuk Anda; di Docker, Anda mengarahkan kontainer ke routing yang sama tetapi memberikan token konektor alih-alih config yang di-mount.
Menjalankan cloudflared di Docker
Di VPS saya semuanya Docker Compose, jadi tunnel hanyalah satu service tambahan. Metode token paling bersih di sini — token konektor membawa identitas tunnel dan konfigurasi routing-nya, jadi kontainer tidak perlu apa pun yang di-mount. Simpan token di file environment, jangan pernah di-hardcode di file compose yang Anda commit.
# docker-compose.yml
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel run
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
# No ports published — that is the whole point.
networks:
- web
networks:
web:
external: true
# .env (git-ignored)
# TUNNEL_TOKEN=eyJhIjoiTOKEN...Taruh cloudflared di jaringan Docker yang sama dengan aplikasi yang di-proxy-nya, lalu arahkan ingress ke nama kontainer alih-alih localhost — misalnya http://grafana:3000. Kini tidak ada kontainer aplikasi Anda yang perlu port terpublikasi juga; tunnel menjangkaunya lewat jaringan Docker internal dan host tidak punya apa pun yang mendengarkan secara publik.
Menambahkan Zero Trust Access di depan
Tunnel saja tetap mempublikasikan layanan Anda ke siapa pun yang tahu URL-nya. Untuk apa pun yang tidak dimaksudkan sepenuhnya publik, lapisi dengan Cloudflare Access di atasnya. Access berada di edge dan memaksa setiap permintaan melewati pemeriksaan identitas sebelum pernah dirutekan ke tunnel Anda. Semua aplikasi Access bersifat deny by default — pengguna harus mencocokkan kebijakan Allow terlebih dulu — jadi orang asing tanpa autentikasi tidak pernah mencapai origin Anda sama sekali.
- Di dashboard, buka Zero Trust, lalu Access, lalu Applications, dan buat aplikasi Self-hosted baru.
- Atur domain aplikasi ke hostname persis yang dilayani tunnel Anda, misalnya grafana.example.com.
- Tambahkan kebijakan Allow — yang paling sederhana adalah aturan yang mencocokkan alamat email Anda sendiri, atau email yang berakhir dengan domain Anda.
- Hubungkan penyedia identitas seperti Google atau GitHub, atau gunakan one-time PIN bawaan yang dikirim ke email yang diizinkan.
- Simpan. Mengunjungi hostname kini mengarahkan ke halaman login, dan hanya identitas yang cocok yang lolos ke aplikasi.
Di luar allow-list email sederhana, kebijakan Access dapat membatasi berdasarkan keanggotaan grup IdP, negara, postur perangkat, dan durasi sesi. Untuk layanan yang harus menerima panggilan programatik, tambahkan service token agar skrip bisa berautentikasi dengan sepasang header alih-alih login browser. Model mentalnya sederhana: tunnel menentukan bagaimana trafik mencapai mesin Anda, dan Access menentukan siapa yang boleh mengirimnya.
Apa yang layak diekspos begini — dan apa yang tidak
| Layanan | Pendekatan | Alasan |
|---|---|---|
| Dashboard internal (Grafana, uptime, Portainer) | Tunnel plus allow-list Access | Bersifat privat; gerbang identitas menjaganya tetap milik Anda. |
| Blog publik atau situs marketing | Tunnel saja, tanpa Access | Ditujukan untuk semua orang; Access akan memblokir pengunjung asli. |
| SSH ke host | Tunnel plus Access dengan sesi pendek | Menjangkau SSH tanpa port 22 terbuka ke dunia. |
| Port database (Postgres, Redis) | Rute jaringan privat, bukan hostname publik | Jangan pernah mengekspos DB sebagai aplikasi HTTP; gunakan WARP atau routing privat. |
| Apa pun yang menangani pembayaran atau PII | Access plus IdP ketat dan postur perangkat | Auth di edge adalah kontrol, bukan pengganti keamanan tingkat aplikasi. |
Trade-off yang layak disebut: Anda merutekan trafik melalui Cloudflare dan mempercayai edge mereka untuk menterminasi TLS. Untuk homelab dan sebagian besar proyek sampingan itu kesepakatan yang adil demi kenyamanan dan firewall yang tertutup. Jika Anda tidak bisa menerima pihak ketiga menterminasi TLS, alternatif self-hosted seperti VPN WireGuard atau reverse SSH tunnel biasa menjaga semuanya di tangan Anda dengan biaya setup lebih banyak dan tanpa filter edge. Bagi saya, di jalur rumahan di belakang CGNAT dan VPS yang ingin saya kunci, Cloudflare Tunnel plus Access adalah setup yang benar-benar terus saya jalankan.

