Saya tidak ingin mengunduh data geofabrik untuk region Indonesia yang berukuran besar (per-tulisan ini dibuat, 1.6 GB), karena saya hanya memerlukan data untuk tiga sub-region: pulau Sumatra (258 MB), Java (847 MB), dan Kalimantan (138 MB). Oleh karena itu, saya hanya akan mengunduh data untuk ketiga sub-region tersebut dan menggabungkannya menjadi satu file .osm.pbf.
Unduh data untuk ketiga sub-region yang diperlukan dari situs Geofabrik:
Letakkan ketiga file .osm.pbf yang telah diunduh ke dalam satu folder, misal: geofabrik/.
$ cd geofabrik/
$ wget -c https://download.geofabrik.de/asia/indonesia/sumatra-latest.osm.pbf
$ wget -c https://download.geofabrik.de/asia/indonesia/java-latest.osm.pbf
$ wget -c https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf
geofabrik/
│ java-latest.osm.pbf
│ kalimantan-latest.osm.pbf
└ sumatra-latest.osm.pbf
Setelah ketiga file .osm.pbf untuk sub-region yang diperlukan telah diunduh, langkah selanjutnya adalah menggabungkannya menjadi satu file .osm.pbf menggunakan Osmium Tool.
Instalasi Osmium Tool dapat dilakukan dengan mengikuti petunjuk di situs resminya: https://osmcode.org/osmium-tool/.
Setelah Osmium Tool terinstal, jalankan perintah berikut untuk menggabungkan ketiga file .osm.pbf menjadi satu file .osm.pbf baru, misal: indonesia-3pulau-latest.osm.pbf.
$ osmium merge sumatra-latest.osm.pbf java-latest.osm.pbf kalimantan-latest.osm.pbf -o indonesia-3pulau-latest.osm.pbf
Tunggu hingga proses penggabungan selesai.
Dalam catatan ini, saya telah mendemonstrasikan proses penggabungan data untuk pulau Sumatra, Java, dan Kalimantan menjadi satu file .osm.pbf baru yang berukuran lebih kecil dibandingkan dengan data untuk seluruh region Indonesia.
https://download.geofabrik.de/asia/indonesia.html
Diakses tanggal: 2026-02-22
https://osmcode.org/osmium-tool/
Diakses tanggal: 2026-02-22
Overpass API memungkinkan kita untuk melakukan query terhadap data OpenStreetMap (OSM) yang dapat digunakan untuk mencari POI (Point of Interest) pada radius tertentu dari lokasi yang didefinisikan. Overpass API menggunakan bahasa query yang disebut Overpass QL. Dengan Overpass API, kita dapat melakukan query untuk mencari data OSM berdasarkan kriteria tertentu, seperti jenis POI, lokasi, dan lain-lain.
Buat file docker-compose.yml dengan isi seperti ini,
services:
overpass:
image: openzonedev/overpass-api:0.7.57.2
container_name: osm-overpass
restart: unless-stopped
environment:
OVERPASS_META: "yes"
OVERPASS_MODE: "init"
OVERPASS_PLANET_URL: file:///overpass/data/kalimantan-latest.osm.bz2
volumes:
- ./geofabrik/kalimantan-new.latest.bz2:/overpass/data/kalimantan-latest.osm.bz2:ro
- overpass-data:/db
expose:
- "80"
ports:
- "8081:80"
volumes:
overpass-data:
Pada contoh di atas, saya menggunakan data OSM untuk wilayah Kalimantan yang saya unduh dari Geofabrik.
INFO
Overpass API membutuhkan file data OSM dalam format
.osm.bz2untuk proses import data.
Saya akan merubah file.osm.pbfyang kita unduh dari Geofabrik menjadi format.osm.bz2menggunakan toolosmium-tool.
File .osm.pbf ini bisa didapatkan dari situs Geofabrik.
Setelah mengunduh file .osm.pbf, letakkan file tersebut di dalam direktori geofabrik/ yang berada di root project. Buat direktori geofabrik/ jika belum ada.
$ mkdir geofabrik
$ cd geofabrik
$ wget -c https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf
Tunggu proses download selesai, setelah itu pastikan file .osm.pbf sudah berada di dalam direktori geofabrik/.
Berikut adalah struktur direktori yang diharapkan setelah menambahkan file .osm.pbf:
geofabrik/
└── kalimantan-latest.osm.pbf
docker-compose.yml
.osm.pbf ke format .osm.bz2Untuk mengkonversi file .osm.pbf ke format .osm.bz2, bisa menggunakan tool osmium-tool. Pastikan osmium-tool sudah terinstall di sistem.
Jika belum, bisa menginstalnya dengan mengikuti petunjuk di situs resmi osmium-tool.
$ brew install osmium-tool
Setelah osmium-tool terinstall, jalankan perintah berikut untuk mengkonversi file .osm.pbf ke format .osm.bz2:
$ osmium cat geofabrik/kalimantan-latest.osm.pbf -o geofabrik/kalimantan-latest.osm.bz2
Tunggu proses konversi selesai, setelah itu pastikan file .osm.bz2 sudah berada di dalam direktori geofabrik/.
Sip, sekarang file .osm.bz2 sudah siap untuk digunakan dalam proses import data oleh Overpass API.
Setelah file docker-compose.yml sudah siap dan file data OSM dalam format .osm.bz2 sudah berada di dalam direktori geofabrik/, jalankan perintah berikut untuk memulai Docker Compose:
$ docker-compose up -d
PERHATIAN
Untuk pertama kali menjalankan docker compose, Overpass API service akan melakukan proses import data dari file
.osm.bz2ke dalam Overpass Data, proses ini membutuhkan waktu yang cukup lama, tergantung pada ukuran file.osm.bz2yang digunakan. Untuk filekalimantan-latest.osm.bz2yang berukuran 318 MB, proses import data bisa memakan waktu sekitar 30 menit hingga 1 jam.
Untuk melihat log dari container, bisa menggunakan perintah berikut:
$ docker compose logs overpass -f
Tunggu proses import data selesai. Biasanya akan muncul log seperti berikut ketika proses import data sudah selesai:
Overpass container initialization complete. Exiting.
Setelah proses import data selesai, Overpass API service sudah siap untuk digunakan. Overpass API bisa diakses melalui http://localhost:8081/.
Untuk memastikan bahwa Overpass API service sudah berjalan dengan baik, coba akses endpoint /api/status untuk melihat status dari Overpass API service.
$ curl http://localhost:8081/api/status
Outputnya akan seperti ini,
Connected as: 3232252161
Current time: 2026-02-16T10:25:16Z
Rate limit: 0
Setelah Overpass API service sudah siap, coba lakukan query untuk mencari data OSM berdasarkan kriteria tertentu.
Base URL nya seperti ini,
$ curl -X POST http://localhost:8081/api/interpreter
Outputnya akan seperti ini,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" lang="en"/>
<title>OSM3S Response</title>
</head>
<body>
<p>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</p>
<p><strong style="color:#FF0000">Error</strong>: encoding error: Your input contains only whitespace. </p>
</body>
</html>
Nah, tinggal tambahkan query untuk mencari data OSM berdasarkan kriteria tertentu.
Gunakan juga header Content-Type: application/x-www-form-urlencoded untuk mengirim data query dalam format URL encoded.
Misalnya, untuk mencari semua POI dengan tag amenity=cafe pada radius 5000 meter dari titik user berada (-1.2286365, 116.8941987), kita bisa menggunakan query berikut:
$ curl -X POST http://localhost:8081/api/interpreter -H "Content-Type: application/x-www-form-urlencoded" --data "data=[out:json];node[amenity=cafe](around:5000, -1.2286365, 116.8941987);out center;"
Outputnya dalam bentuk JSON akan seperti ini,
{
"version": 0.6,
"generator": "Overpass API 0.7.57.2 48842a1b",
"osm3s": {
"timestamp_osm_base": "2026-01-18T20:51:44Z",
"copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
},
"elements": [
{
"type": "node",
"id": 9898980886,
"lat": -1.2420011,
"lon": 116.8730133,
"tags": {
"amenity": "cafe",
"name": "Excelso - Living Plaza"
}
},
{
"type": "node",
"id": 9899039726,
"lat": -1.2437437,
"lon": 116.8607824,
"tags": {
"amenity": "cafe",
"name": "Excelso Cafe Mall Fantasy"
}
},
{
"type": "node",
"id": 9899398634,
"lat": -1.2449160,
"lon": 116.8603869,
"tags": {
"amenity": "cafe",
"name": "CPM Coffee Shop Balikpapan Baru - Damai"
}
},
{
"type": "node",
"id": 9899400587,
"lat": -1.2452571,
"lon": 116.8597143,
"tags": {
"amenity": "cafe",
"name": "Balgas Cafe - Damai"
}
},
{
"type": "node",
"id": 9899406395,
"lat": -1.2452690,
"lon": 116.8606450,
"tags": {
"amenity": "cafe",
"name": "Kopi Lawe - Damai Baru"
}
},
{
"type": "node",
"id": 9900996432,
"lat": -1.2511856,
"lon": 116.8696109,
"tags": {
"amenity": "cafe",
"branch": "Ruko Jalan MT Haryono",
"brand": "Kopi Kenangan",
"brand:wikidata": "Q97221992",
"cuisine": "coffee_shop",
"diet:halal": "only",
"name": "Kopi Kenangan",
"takeaway": "yes"
}
},
{
"type": "node",
"id": 10036595776,
"lat": -1.2460153,
"lon": 116.8594667,
"tags": {
"amenity": "cafe",
"name": "Dialog Cafe - Balikpapan Baru"
}
}
]
}
Dari data di atas, kita bisa melihat bahwa terdapat beberapa POI dengan tag amenity=cafe pada radius 5000 meter dari latitude dan longitude yang saya definisikan yang berhasil ditemukan oleh Overpass API.
Silahkan sesuaikan query sesuai dengan kebutuhan untuk mencari data OSM berdasarkan kriteria tertentu. Berikut adalah contoh query dalam format Overpass QL yang digunakan di atas:
[out:json];
(
node[amenity=cafe](around:5000, -1.2286365, 116.8941987);
);
out center;
Jika sudah selesai menggunakan Overpass API service, jangan lupa untuk menghentikan Docker Compose dengan perintah berikut:
$ docker-compose download
Saya telah mencatat langkah-langkah untuk menjalankan instance Overpass API menggunakan Docker. Dengan mengikuti langkah-langkah di atas, kita dapat dengan mudah menjalankan Overpass API service di lingkungan lokal kita dan melakukan query terhadap data OpenStreetMap untuk mencari POI berdasarkan kriteria tertentu.
Pada catatan sebelumnya “OpenStreetMap Nominatim (Geocoding Machine) dengan Docker” saya telah membahas cara menjalankan instance Nominatim menggunakan Docker. Pada artikel ini, saya akan mencatat langkah-langkah untuk mengonfigurasi Geocoder gem di aplikasi Ruby on Rails agar menggunakan instance Nominatim self-hosted sebagai geocoding service lookup.
Jika sebelumnya sudah memiliki config file untuk Geocoder di config/initializers/geocoder.rb, buka file tersebut.
Dan tambahkan atau modifikasi konfigurasi seperti ini,
!filename: config/initializers/geocoder.rb
Geocoder.configure(
# Geocoding options
# timeout: 3, # geocoding service timeout (secs)
# lookup: :nominatim, # name of geocoding service (symbol)
# ip_lookup: :ipinfo_io, # name of IP address geocoding service (symbol)
# language: :en, # ISO-639 language code
# use_https: false, # use HTTPS for lookup requests? (if supported)
# http_proxy: nil, # HTTP proxy server (user:pass@host:port)
# https_proxy: nil, # HTTPS proxy server (user:pass@host:port)
# api_key: nil, # API key for geocoding service
# cache: nil, # cache object (must respond to #[], #[]=, and #del)
# Exceptions that should not be rescued by default
# (if you want to implement custom error handling);
# supports SocketError and Timeout::Error
# always_raise: [],
# Calculation options
# units: :km, # :km for kilometers or :mi for miles
# distances: :linear # :spherical or :linear
# Cache configuration
# cache_options: {
# expiration: 2.days,
# prefix: 'geocoder:'
# }
use_https: false,
lookup: :nominatim,
nominatim: {
host: localhost:8080
},
)
Saya menggunakan localhost:8080 karena pada catatan sebelumnya saya menjalankan Nominatim di Docker dengan port mapping 8080:8080 dan karena di localhost sehingga tidak menggunakan https.
Bisa juga disesuaikan dengan host dan port tempat Nominatim dijalankan. Misal, saya sudah punya service yang saya jalankan di https://nominatim.bandithijo.dev, maka konfigurasi nominatim akan seperti ini,
!filename: config/initializers/geocoder.rb
Geocoder.configure(
# ...
# ...
use_https: true,
lookup: :nominatim,
nominatim: {
host: 'nominatim.bandithijo.dev',
},
)
Dengan begini saya sudah berhasil mengonfigurasi Geocoder gem untuk menggunakan Nominatim self-hosted sebagai geocoding service lookup. Selanjutnya, saya bisa menggunakan Geocoder gem untuk melakukan geocoding dan reverse geocoding pada aplikasi Ruby on Rails saya dengan menggunakan instance Nominatim yang saya jalankan sendiri.
Untuk menguji apakah konfigurasi sudah benar, bisa dilakukan di Rails console.
$ rails console
Setup logger agar menampilkan request dan response dari Geocoder gem untuk mempermudah proses debugging,
irb(main):001> Geocoder.configure(logger: Logger.new(STDOUT))
Outputnya,
=>
{:timeout=>3,
:lookup=>:nominatim,
:ip_lookup=>:ipinfo_io,
:language=>:en,
:http_headers=>{},
:use_https=>true,
:http_proxy=>nil,
:https_proxy=>nil,
:api_key=>nil,
:basic_auth=>{},
:logger=>
#<Logger:0x000000012837e6b8
@default_formatter=#<Logger::Formatter:0x0000000124a709e8 @datetime_format=nil>,
@formatter=nil,
@level=0,
@level_override={},
@logdev=
#<Logger::LogDevice:0x0000000128273980
@binmode=false,
@dev=#<IO:<STDOUT>>,
@filename=nil,
@mon_data=#<Monitor:0x0000000124a70970>,
@mon_data_owner_object_id=71700,
@reraise_write_errors=[],
@shift_age=nil,
@shift_period_suffix=nil,
@shift_size=nil,
@skip_header=false>,
@progname=nil>,
:kernel_logger_level=>2,
:always_raise=>[],
:units=>:km,
:distances=>:linear,
:cache=>nil,
:cache_prefix=>nil,
:cache_options=>{:prefix=>"geocoder:", :expiration=>nil},
:nominatim=>{:host=>"osm.bintangdigitalasia.com/nominatim"}}
Dapat dilihat, bahwa konfigurasi nominatim untuk host sudah sesuai dengan yang diinginkan.
Lalu lakukan geocoding pada alamat tertentu,
irb(main):002> Geocoder.search("Jakarta")
Outputnya,
D, [2026-02-09T00:02:12.686252 #30316] DEBUG -- : Geocoder: HTTP request being made for http://localhost:8080/search?accept-language=en&addressdetails=1&format=json&q=Jakarta
=>
[#<Geocoder::Result::Nominatim:0x0000000127219df0
@cache_hit=nil,
@data=
{"place_id"=>3204450,
"licence"=>"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
"osm_type"=>"relation",
"osm_id"=>6362934,
"lat"=>"-6.1754049",
"lon"=>"106.8271680",
"class"=>"boundary",
"type"=>"administrative",
"place_rank"=>8,
"importance"=>0.2933433333333333,
"addresstype"=>"city",
"name"=>"Special Capital Region of Jakarta",
"display_name"=>"Special Capital Region of Jakarta, Java, Indonesia",
"address"=>
{"city"=>"Special Capital Region of Jakarta",
"ISO3166-2-lvl4"=>"ID-JK",
"region"=>"Java",
"ISO3166-2-lvl3"=>"ID-JW",
"country"=>"Indonesia",
"country_code"=>"id"},
"boundingbox"=>["-6.3744575", "-4.9993635", "106.3146732", "106.9739750"]}>]
irb(main):003> Geocoder.search([-1.2379, 116.8529])
Outputnya,
D, [2026-02-09T00:04:49.941967 #30316] DEBUG -- : Geocoder: HTTP request being made for http://locahost:8080/reverse?accept-language=en&addressdetails=1&format=json&lat=-1.2379&lon=116.8529
=>
[#<Geocoder::Result::Nominatim:0x0000000125da17b0
@cache_hit=nil,
@data=
{"place_id"=>119266,
"licence"=>"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
"osm_type"=>"way",
"osm_id"=>109864339,
"lat"=>"-1.2378944",
"lon"=>"116.8529396",
"class"=>"highway",
"type"=>"residential",
"place_rank"=>26,
"importance"=>0.0533433333333333,
"addresstype"=>"road",
"name"=>"Jalan Bubutan",
"display_name"=>"Jalan Bubutan, Gunung Samarinda, Balikpapan Utara, Balikpapan, East Kalimantan, 76125, Indonesia",
"address"=>
{"road"=>"Jalan Bubutan",
"village"=>"Gunung Samarinda",
"city_district"=>"Balikpapan Utara",
"city"=>"Balikpapan",
"state"=>"East Kalimantan",
"ISO3166-2-lvl4"=>"ID-KI",
"postcode"=>"76125",
"country"=>"Indonesia",
"country_code"=>"id"},
"boundingbox"=>["-1.2383814", "-1.2355025", "116.8482510", "116.8530576"]}>]
irb(main):003>
Dengan mengikuti langkah-langkah di atas, saya berhasil mengonfigurasi Geocoder gem untuk menggunakan Nominatim self-hosted sebagai geocoding service lookup. Saya juga berhasil melakukan geocoding dan reverse geocoding menggunakan instance Nominatim yang saya jalankan sendiri. Dengan menggunakan Nominatim self-hosted, saya memiliki kontrol penuh atas data geocoding dan dapat menghindari batasan penggunaan yang mungkin diterapkan oleh layanan geocoding pihak ketiga.
OpenStreetMap Nominatim adalah sebuah layanan geocoding yang memungkinkan pengguna untuk mencari lokasi berdasarkan nama tempat atau alamat (forward search) atau geographic coordinate (reverse search). Dalam artikel ini, saya akan mendokumentasikan cara menjalankan OpenStreetMap Nominatim menggunakan Docker agar proses setup Nominatim menjadi lebih mudah dan cepat, tanpa perlu repot mengatur lingkungan dan dependensi secara manual.
OpenStreetMap Nominatim adalah sebuah layanan geocoding yang memungkinkan pengguna untuk mencari lokasi berdasarkan nama tempat atau alamat (forward search) atau koordinat geografis (reverse search). Nominatim menggunakan data dari OpenStreetMap untuk menyediakan informasi lokasi yang akurat. Tanpa OpenStreetMap, Nominatim tidak memiliki data apapun untuk digunakan dalam proses geocoding.
Forward Geocodig adalah proses mengubah nama tempat atau alamat menjadi koordinat geografis (latitude dan longitude). Misalnya, jika mencari “Balikpapan”, Nominatim akan memberikan koordinat geografis dari Balikpapan.
Reverse Geocoding adalah proses mengubah koordinat geografis (latitude & longitude) menjadi nama tempat atau alamat. Misalnya, jika memiliki koordinat geografis dari Balikpapan (-1.2398711,116.8593379), Nominatim akan memberikan nama tempat atau alamat yang sesuai.
Nominatim menyediakan API yang dapat digunakan untuk melakukan forward, reverse geocoding dan beberapa hal lainnya.
Berikut adalah contoh penggunaan Nominatim API:
/search /reverse /lookup /details /lookup./status Untuk menjalankan OpenStreetMap Nominatim dengan Docker, bisa menggunakan image yang sudah tersedia di Docker Hub. Agar prosesnya lebih mudah, saya prefer untuk membuat docker compose saja. Berikut adalah langkah-langkahnya:
Buat file docker-compose.yml dengan isi sebagai berikut:
!filename: docker-compose.yml
services:
nominatim:
image: mediagis/nominatim:5.2
container_name: osm-nominatim
restart: unless-stopped
environment:
PBF_PATH: /nominatim/data/kalimantan-latest.osm.pbf
volumes:
- ./geofabrik/kalimantan-latest.osm.pbf:/nominatim/data/kalimantan-latest.osm.pbf
- nominatim-data:/var/lib/postgresql
expose:
- "8080"
ports:
- "8080:8080"
volumes:
nominatim-data:
Saya menggunakan docker image mediagis/nominatim:5.2 yang sudah tersedia di Docker Hub.
Pada PBF_PATH diisi dengan file .osm.pbf. PBF adalah format file yang digunakan untuk menyimpan data OpenStreetMap. File ini berisi informasi tentang jalan, bangunan, dan fitur geografis lainnya yang ada di wilayah tertentu. Dalam contoh ini, saya menggunakan file kalimantan-latest.osm.pbf yang berisi data OpenStreetMap untuk wilayah Kalimantan. Untuk mendapatkan file .osm.pbf untuk wilayah lain, bisa mengunduhnya dari situs Geofabrik.
Kisaran size file .osm.pbf untuk wilayah Indonesia dan beberapa wilayah lainnya adalah sebagai berikut:
| Region | Size |
|---|---|
| Indonesia (with East Timor) | 1.6 GB |
| Java | 845 MB |
| Kalimantan | 138 MB |
| Maluku | 25.1 MB |
| Nusa Tenggara | 162 MB |
| Papua | 31.0 MB |
| Sulawesi | 149 MB |
| Sumatra | 255 MB |
Saya menggunakan 2 values untuk volumes,
.geofabrik/kalimantan-latest.osm.pbf:/nominatim/data/kalimantan-latest.osm.pbf .osm.pbf yang ada di host dengan file .osm.pbf yang ada di dalam container. Dengan begitu, saya dapat menggunakannya di PBF_PATH untuk proses import data. Proses persiapan file .osm.pbf ini akan saya jelaskan di bagian selanjutnya.nominatim-data:/var/lib/postgresql nominatim-data ini adalah nama volume yang dibuat dibagian volumes di bawah nominatim-data:. Volume ini akan dihubungkan dengan direktori /var/lib/postgresql di dalam container, yang merupakan direktori tempat Nominatim menyimpan data yang dihasilkan.Secara default nonimatin mengekspose port 8080 di dalam container, sehingga saya juga expose port 8080 di bagian expose. Agar bisa diakses dari luar container, saya juga memetakan port 8080 di host ke port 8080 di dalam container dengan menggunakan ports.
.osm.pbfSebelum menjalankan Docker Compose, pastikan sudah memiliki file .osm.pbf yang akan digunakan untuk proses import data. File .osm.pbf ini bisa didapatkan dari situs Geofabrik.
Setelah mengunduh file .osm.pbf, letakkan file tersebut di dalam direktori geofabrik/ yang berada di root project. Buat direktori geofabrik/ jika belum ada.
$ mkdir geofabrik
$ cd geofabrik
$ wget -c https://download.geofabrik.de/asia/indonesia/kalimantan-latest.osm.pbf
Tunggu proses download selesai, setelah itu pastikan file .osm.pbf sudah berada di dalam direktori geofabrik/.
Berikut adalah struktur direktori yang diharapkan setelah menambahkan file .osm.pbf:
geofabrik/
└── kalimantan-latest.osm.pbf
docker-compose.yml
Sip, sekarang file .osm.pbf sudah siap untuk digunakan dalam proses import data oleh Nominatim.
Setelah membuat file docker-compose.yml, jalankan perintah berikut untuk menjalankan Nominatim service:
$ docker-compose up -d
PERHATIAN
Untuk pertama kali menjalankan docker compose, Nominatim service akan melakukan proses import data dari file
.osm.pbfke dalam Nominatim, proses ini membutuhkan waktu yang cukup lama, tergantung pada ukuran file.osm.pbfyang digunakan. Untuk filekalimantan-latest.osm.pbfyang berukuran 138 MB, proses import data bisa memakan waktu sekitar 30 menit hingga 1 jam.
Untuk melihat log dari container, bisa menggunakan perintah berikut:
$ docker compose logs nominatim -f
Tunggu proses import data selesai. Biasanya akan muncul log seperti berikut ketika proses import data sudah selesai:
--> Nominatim is ready to accept requests
Setelah proses import data selesai, Nominatim service sudah siap untuk digunakan. Nominatim API bisa diakses melalui http://localhost:8080/.
Setelah Nominatim service sudah siap, coba akses Nominatim API untuk melakukan forward geocoding dan reverse geocoding.
Pertama-tama coba cek statusnya dulu.
$ curl http://localhost:8080/status
Outputnya,
OK
Atau kalau mau outputnya dalam JSON,
$ curl http://localhost:8080/status?format=json
Outputnya,
{
"status": 0,
"message": "OK",
"data_updated": "2026-01-21T12:30:08+00:00",
"software_version": "5.2.0",
"database_version": "5.2.0-0"
}
Coba lakukan forward geocoding untuk mencari lokasi berdasarkan nama tempat atau alamat. Misalnya, mencari lokasi “jakarta”.
$ curl "http://localhost:8080/search?q=jakarta&format=json"
Outputnya,
[
{
"place_id": 144282,
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
"osm_type": "way",
"osm_id": 795600162,
"lat": "-0.5354329",
"lon": "117.0914415",
"category": "highway",
"type": "residential",
"place_rank": 26,
"importance": 0.0533433333333333,
"addresstype": "road",
"name": "Jakarta",
"display_name": "Jakarta, Loa Bakung, Sungai Kunjang, Samarinda, Kalimantan Timur, 75391, Indonesia",
"boundingbox": [
"-0.5355636",
"-0.5353021",
"117.0914346",
"117.0914483"
]
}
]
Wah! Ternyata ada daerah beranama “Jakarta” di Kalimantan Timur.
Coba lakukan reverse geocoding untuk mencari nama tempat atau alamat berdasarkan koordinat geografis (latitude & longitude). Misalnya, mencari lokasi berdasarkan koordinat -0.5354329,117.0914415 (koordinat Balikpapan).
$ curl "http://localhost:8080/reverse?lat=-1.2398711&lon=116.8593379&format=json"
Outputnya,
{
"place_id": 144282,
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
"osm_type": "way",
"osm_id": 795600162,
"lat": "-0.5354329",
"lon": "117.0914415",
"class": "highway",
"type": "residential",
"place_rank": 26,
"importance": 0.0533433333333333,
"addresstype": "road",
"name": "Jakarta",
"display_name": "Jakarta, Loa Bakung, Sungai Kunjang, Samarinda, Kalimantan Timur, 75391, Indonesia",
"address": {
"road": "Jakarta",
"village": "Loa Bakung",
"city_district": "Sungai Kunjang",
"city": "Samarinda",
"state": "Kalimantan Timur",
"ISO3166-2-lvl4": "ID-KI",
"postcode": "75391",
"country": "Indonesia",
"country_code": "id"
},
"boundingbox": [
"-0.5355636",
"-0.5353021",
"117.0914346",
"117.0914483"
]
}
Coba lakukan lookup untuk mencari informasi lokasi berdasarkan OSM ID. Misalnya, mencari informasi lokasi berdasarkan OSM ID 795600162. Ini adalah OSM ID dari hasil pencarian sebelumnya untuk lokasi “jakarta” di Kalimantan Timur.
$ curl "http://localhost:8080/lookup?osm_ids=W795600162&format=json"
Outputnya,
[
{
"place_id": 144282,
"licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright",
"osm_type": "way",
"osm_id": 795600162,
"lat": "-0.5354329",
"lon": "117.0914415",
"class": "highway",
"type": "residential",
"place_rank": 26,
"importance": 0.0533433333333333,
"addresstype": "road",
"name": "Jakarta",
"display_name": "Jakarta, Loa Bakung, Sungai Kunjang, Samarinda, Kalimantan Timur, 75391, Indonesia",
"address": {
"road": "Jakarta",
"village": "Loa Bakung",
"city_district": "Sungai Kunjang",
"city": "Samarinda",
"state": "Kalimantan Timur",
"ISO3166-2-lvl4": "ID-KI",
"postcode": "75391",
"country": "Indonesia",
"country_code": "id"
},
"boundingbox": [
"-0.5355636",
"-0.5353021",
"117.0914346",
"117.0914483"
]
}
]
Coba lakukan details untuk mencari informasi lokasi berdasarkan OSM ID, dengan informasi yang lebih detail dibandingkan dengan /lookup. Misalnya, mencari informasi lokasi berdasarkan OSM ID 795600162.
$ curl "http://localhost:8080/details?osmtype=W&osmid=795600162&format=json"
Outputnya,
{
"place_id": 144282,
"parent_place_id": 142133,
"osm_type": "W",
"osm_id": 795600162,
"category": "highway",
"type": "residential",
"admin_level": 15,
"localname": "Jakarta",
"names": {
"name": "Jakarta"
},
"addresstags": {},
"calculated_postcode": "75391",
"country_code": "id",
"indexed_date": "2026-02-08T13:11:46.748639+00:00",
"importance": 0.0533433333333333,
"calculated_importance": 0.0533433333333333,
"extratags": {},
"rank_address": 26,
"rank_search": 26,
"isarea": false,
"centroid": {
"type": "Point",
"coordinates": [
117.0914415,
-0.5354329
]
},
"geometry": {
"type": "Point",
"coordinates": [
117.0914415,
-0.5354329
]
}
}
Jika sudah selesai menggunakan Nominatim service, bisa stop Docker Compose dengan perintah berikut:
$ docker-compose down
Dalam artikel ini, saya telah mendokumentasikan cara menjalankan OpenStreetMap Nominatim menggunakan Docker, mulai dari membuat Docker Compose, mempersiapkan file .osm.pbf, menjalankan Docker Compose, mencoba Nominatim API, hingga menghentikan Docker Compose. Dengan menggunakan Docker, proses setup Nominatim menjadi lebih mudah dan cepat, tanpa perlu repot mengatur lingkungan dan dependensi secara manual.
Pada artikel selanjutnya, saya akan mendokumentasikan cara menggunakan Nominatim self-hosted pada Geocoder gem untuk melakukan geocoding dan reverse geocoding di aplikasi Ruby on Rails, “Menggunakan Nominatim Self-Hosted pada Geocoder Gem”
https://nominatim.openstreetmap.org/ui/about.html
Diakses tanggal: 2026-02-07
https://nominatim.org/release-docs/develop/api/Overview/
Diakses tanggal: 2026-02-07
QnA ini di-request oleh mas Rizky Ramadhan via email. Pertanyaan ini diajukan sekitar tahun 2023. Ia bertanya sebagian besar terkait dengan Ruby on Rails. Saya merasa pertanyaan dan jawaban ini akan memberikan lebih banyak manfaat jika dipublikasikan. Semoga bermanfaat yaa.
PERHATIAN
Saya kesulitan mencari alamat email dari mas Rizky Ramadhan, jika mas Rizky membaca ini dan tidak berkenan untuk mempublikasikan tulisan ini, boleh hubungi saya via email yaa.
Halo, mas. Perkenalkan, saya Rizky, baru lulus tahun ini, sekarang sudah bekerja, sekitar 1 bulan lebih dikit. Kalau di kantor biasa pakai Yii, untuk hobi saya suka Laravel. Mau tanya soal Ruby on Rails.
Kemarin saya sudah menonton tutorial Ruby on Rails dari Padang Tekno di YouTube, lumayan sulit juga nyari tutorial yang up to date, kebanyakan 3 tahun / 5 tahun yang lalu.
Kalau dari yang saya pahami, konsep Rails dan Laravel lumayan mirip. Apa yang bisa dilakukan Rails, bisa juga dilakukan Laravel. Meskipun, kalau soal syntax, Ruby lebih elegan.
Jadi, pertanyaannya:
Saya tertarik buat nyoba Rails, mas. Cuman, belum click di mana “magic” nya, selain syntax yang elegan tadi.
Udah liat-liat comparison nya di internet, tapi sepertinya kebih baik bertanya langsung kepada sepuh. Makasih mas 🙏
Halo halo Rizky.
Selamat atas kelulusan dan sudah keterima bekerja. Mudah-mudahan berkah ilmunya yaa.
Sebelumnya, terima kasih yaa sudah mengajukan pertanyaan terkait Rails. Sebenernya saya belum bisa disebut sepuh Rails, karena baru pakai Rails dari tahun 2019-2021 (FullStack), 2022-2023 (Backend). Wkwkwk.
Mungkin karena masih bekerja pakai Rails dan belum ketemu kesulitan dan jalan buntu ketika digunakan saat bekerja. Masih happy pakai Rails.
Kelebihan-kelebihan yang saya rasakan ketika pakai Rails saat bekerja:
Rails ini framework yang bersifat Opinionated. Artinya sudah ada aturan yang disepakati (secara umum) untuk melakukan suatu pekerjaan yang umum (biasa disebut Best Practice). Kalau bekerja dalam tim, sifat opinionated ini yang mempermudah dan mempercepat menyelesaikan suatu task. Karena kita tidak perlu lagi memperdebatkan mana cara penyelesaian masalah yang baik. Kecuali ada kondisi-kondisi tertentu yang disepakai oleh tim untuk tidak menggunakan “best practice” karena kebutuhan tertentu.
Proses menyelesaikan task menjadi lebih cepat karena beberapa flow bisnis, sudah ada dan sudah dibuatkan best practicenya, jadi tidak perlu lagi memperdebatkan flow bisnis yang sudah umum. Tinggal pakai saja kalau memang tidak ada kebutuhan khusus.
Dengan begini, perdebatan terkait hal-hal teknis, yang umumnya terjadi di tempat kami:
Metaprogramming. Ruby memiliki kemampuan metaprogramming yang diadopsi oleh Rails untuk mempermudah proses pembuatan aplikasi.
Terkait metraprogramming di Rails, ada banyak sekali, tapi yang baru-baru aja (inget) saya pakai yang seperti ini:
Active Model Dirty https://api.rubyonrails.org/classes/ActiveModel/Dirty.html
Saya pakai untuk membuat callback, ketika suatu atribute (misal: name) berubah nilainya.
Active Record Nested Attributes https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Saya pakai untuk membuat data child yang berasosiasi dengan parent. Jadi ketika parentnya dibuat, child nya juga ikut terbut. Misal: membuat admin (parent) baru dengan beberapa admin_permission (child).
2 method di atas, terdengar biasa aja. Tapi kalau dilihat kodenya, hal-hal tersebut dapat dilakukan hanya dengan kode yang simple tanpa perlu banyak effort.
Hal lain seperti migrasi dan relasi tabel juga pakai metaprogramming, sehingga ada method-method yang secara ajaib sudah ada dan tinggal dipakai.
Kebetulan kemarin sempet lihat topik terkait “Powerful Rails Feature You Might Not Know” dari Chris Oliver di Rails World 2023, bisa coba tonton di sini yaa https://www.youtube.com/watch?v=iPCqwZ9Ouc4
Dokumentasi yang dimiliki Rails juga terbilang rapi dan deskriptif. Mulai dari dokumentasi Rails yang ada di https://guides.rubyonrails.org/, sampai dokumentasi Rails API yang ada di https://api.rubyonrails.org/
Rails sebenarnya kumpulan dari beberapa module (library) yang modular yang didesain untuk memiliki intarface yang agnostic. Jadi bisa pakai engine (backend) apa saja. Misal, untuk Active Job kita bisa pakai Sidekiq, Resque, Sneakers, Delayed Job, Good Job, dll. https://edgeguides.rubyonrails.org/active_job_basics.html#starting-the-backend Tapi dengan interface yang sama. Jadi meskipun backend job nya bisa berbeda, tapi interface nya tetep sama (nama pemanggilan method nya sama). Terkait Rails dan modularitynya, bisa tonton dari salah satu Rails Core, Eileen Uchitelle di Keynote RailsConf 2023 https://www.youtube.com/watch?v=TKulocPqV38.
Jujurly saya belum pernah coba framework lain selama Rails, jadi belum bisa kasih jawaban.
Rails ini framework yang sudah lama, sudah mature dalam artian sudah banyak digunakan untuk menyelesaikan beberapa bisnis flow. Yang kemudian flow tersebut dibakukan dalam bentuk method, sehingga tinggal kita pakai dan panggil saja. Tentunya ketersediaan built-in class dan method ini dan hal-hal yang berkaitan dengan jawaban sebelumnya juga yang membuat proses penyelesaian suatu problem menjadi lebih cepat.
Kira-kira ini sedikit yang bisa saya sharing terkait Rails dari pengalaman selama menggunakan Rails untuk kerjaan.
Untuk sekarang, dikerjaan, saya lebih banyak pakai Rails sebagai Backend saja. Jadi tidak menjadikan Rails sebagai FullStack. Maka dari itu, jawaban dan ilustrasi yang kuberikan, terbatas di Rails sebagai Backend (sudut pandang fitur-fitur yang memudahkan pekerjaan di Backend). Tentunya tidak sebanding dengan orang yang menjadikan Rails sebagai Fullstack. Karena di bagian Frontend juga terdapat method-method helper yang banyak memudahkan untuk mempercepat proses pembuatan tampilan web.
Untuk kedepannya, saya juga masih sambil belajar JavaScript. Pengetahuan JavaScript sebagai Rails Developer tentu akan sangat membantu, dibandingkan dengan Rails Developer yang tidak memiliki pengetahuan tentang JavaScript sama sekali.
Mudah-mudahan jawaban saya sudah menjawab 3 pertanyaan yang diajukan yaa.
]]>AsciiDoc adalah bahasa markup teks ringan yang digunakan untuk menulis dokumentasi, artikel, buku, dan konten lainnya. Mirip dengan Markdown, AsciiDoc memungkinkan penulis untuk membuat dokumen yang mudah dibaca dan ditulis dalam format teks biasa, yang kemudian dapat dikonversi ke berbagai format output seperti HTML, PDF, dan ePub.
INFO
“AsciiDoc is a plain text markup language for writing technical content. It’s packed with semantic elements and equipped with features to modularize and reuse content. AsciiDoc content can be composed using a text editor, managed in a version control system, and published to multiple output formats.” - AsciiDoc.org
Awal mula saya bertemu dengan Markup Language ini adalah ketika saya membeli sebuah ebook berjudul “Pemrogram Rp 100 Juta: Panduan Bagi Pemrogram Untuk Menggapai Harga, Tahta, dan Kemasyhuran” oleh Arjuna Sky Kok pada tahun 2020.
Saat itu kami diberikan akses ke private repositori GitHub tempat buku tersebut disimpan, dan saya menemukan bahwa buku tersebut ditulis menggunakan AsciiDoc. Saat itu saya berfikir, “Wah! Keren sekali bisa membuat sebuah dokumen atau buku menggunakan bahasa markup seperti ini.” – Saat itu saya belum mengenal LaTeX. Karena yang saya tahu, untuk membuat dokumen atau buku perlu menggunakan aplikasi pengolah kata seperti Microsoft Word atau LibreOffice Writer atau dengan aplikasi desktop publishing seperti Adobe InDesign atau Scribus.
Dikemudian hari, ternyata saya cukup sering menemukan proyek-proyek open source yang menggunakan AsciiDoc untuk dokumentasi mereka, seperti proyek-proyek yang menggunakan Asciidoctor sebagai tool untuk mengkonversi dokumen AsciiDoc ke format lain. Beberapa diantaranya seperti:
PERTANYAAN
Apa perbedaan AsciiDoc dan Asciidoctor?
AsciiDoc adalah markup language yang digunakan untuk menulis dokumen, sedangkan Asciidoctor adalah tool atau perangkat lunak yang digunakan untuk mengkonversi dokumen AsciiDoc ke berbagai format output seperti HTML, PDF, ePub, dan lainnya.
Markdown memang sudah sangat populer dan banyak digunakan untuk menulis dokumentasi, artikel, dan konten lainnya. Namun, AsciiDoc menawarkan beberapa kelebihan dibandingkan Markdown, terutama untuk dokumentasi teknis yang kompleks. Beberapa kelebihan AsciiDoc dibandingkan Markdown antara lain:
= Title of the document
Rizqi Nur Assyaufi <[email protected]>
:source-highlighter: rouge
:icons: font
:toc: macro
:toc-title:
== Table of contents
toc::[]

Gambar 1. Table of Contents dan Judul Dokumen
== Section
Hi, my name is {author}. I'm a Backend Software Engineer at Small Startup footnote:[Startup is a new company, often technology-focused, designed for rapid growth by validating a scalable business model, seeking external funding (like venture capital), and aiming to disrupt existing markets with innovative products or services — Wikipedia] in Jakarta that are working remotely from Balikpapan. Feel free to ask me anything about Software Develop-thing or Linux-thing on {email}.
*This is bold text*
_This is cursive text_
`This is mono text`

Gambar 2. Paragraf dengan format teks
== List
Docker allows to package the full stack in a container:
* OS
** Windows
** Ubuntu
* JVM,
* App server
* Application with its configuration

Gambar 3. Nested list
== Block quotes
[quote, Ben Parker, Spiderman Movie]
____
With great power comes great responsibility.
____

Gambar 4. Block quote
== Defining cross references
You can assign meta data to a block. Here’s an example of a quote block that includes all types of metadata:
[source, ruby, linenums]
----
class Cat <1>
def meow <2>
puts "Meow!" <3>
end
end
cat = Cat.new <4>
cat.meow <5>
----
<1> class definition
<2> method definition
<3> output statement
<4> creating an instance of the class
<5> calling the method

Gambar 5. Cross reference in code block
== Admonition paragraphs and blocks for warnings, notes and tips
An admonition paragraph draws the reader’s attention to certain information. It can be defined by a predefined label at the beginning of the paragraph or as a block.
Here are the other built-in admonition types:
NOTE: Some additional info...
TIP: Pro tip...
IMPORTANT: Don't forget...
WARNING: Watch out for...
CAUTION: Ensure that...

Gambar 6. Admonition blocks
Hi, my name is {author}. I'm a Backend Software Engineer at Small Startup footnote:[Startup is a new company, often technology-focused, designed for rapid growth by validating a scalable business model, seeking external funding (like venture capital), and aiming to disrupt existing markets with innovative products or services — Wikipedia] in Jakarta that are working remotely from Balikpapan. Feel free to ask me anything about Software Develop-thing or Linux-thing on {email}.

Gambar 7. Footnote example
Lalu dengan menggunakan tool Asciidoctor, dokumen tersebut dapat dieksport ke dalam format PDF.
$ asciidoctor-pdf sample_document.adoc
Maka akan menghasilkan file sample_document.pdf yang berisi dokumen yang sudah terformat dengan baik.

Gambar 8. Hasil eksport dokumen ke format PDF halaman 1

Gambar 9. Hasil eksport dokumen ke format PDF halaman 2
Dapat dilihat dari contoh-contoh di atas, dokumen yang dihasilkan memiliki format yang rapi dan terlihat profesional.
Sintaks AsciiDoc juga mendukung berbagai elemen seperti judul, paragraf, list, blok kode, quote, cross reference, footnote, table of contents, dan blok peringatan (admonition blocks) untuk menyoroti informasi penting.
Dokumen di atas hanyalah contoh sederhana dari apa yang dapat dilakukan dengan AsciiDoc. Untuk membuat menjadi layout buku juga tinggal menggunakan dokumen type book.
Bukan saya alergi dengan WYSIWYG editor seperti Ms.Word atau LO Writer, tapi “menurut keyakinan saya”, menulis dokumen teknis lebih efisien dengan AsciiDoc. Kita tinggal mengisi konten saja dengan notasi-notasi (sintaks) yang sesuai, processor yang akan merender menjadi format yang sesuai. Tidak perlu formatting text manual.
Kalau membuat dokumen teknis itu 50% nya menulis konten dan 50% nya lagi formatting text. Maka dengan AsciiDoc, kita bisa 90% fokus menulis kontent dan 10% nya konfigurasi template awal.
Bahkan jika konfigurasi templatenya sudah jadi, “menurut keyakinan saya” malah bisa 100% tinggal menulis konten saja.
Dengan fitur-fitur yang lengkap dan fleksibilitasnya, AsciiDoc menjadi pilihan yang menarik untuk menulis dokumentasi teknis.
Untuk informasi lebih lanjut tentang AsciiDoc, Anda dapat mengunjungi situs resminya atau dokumentasi Asciidoctor yang saya sertakan pada bagian referensi di bawah.
https://asciidoc.org/
Tanggal diakses: 2026-01-18
https://docs.asciidoctor.org/asciidoc/latest/
Tanggal diakses: 2026-01-18
https://asciidoctor.org/
Tanggal diakses: 2026-01-18
Di pertengahan tahun 2025, saya memutuskan untuk membuat perubahan besar pada cara saya menulis artikel di blog ini. Setelah beberapa tahun menggunakan berbagai notasi syntax, saya merasa perlu untuk menyederhanakan proses penulisan agar tetap mempertahankan mood dalam menulis. Oleh karena itu, pada BanditHijo Versi 3, saya berfokus pada penggunaan CommonMark specification sebagai standar penulisan artikel.
Sedikit kilas balik, pada versi pertama blog ini [1], saya menggunakan modifikasi tag/notasi markdown yang cukup kompleks untuk menulis artikel. Hal ini membuat proses penulisan menjadi lebih rumit dan terkadang mengganggu alur kreatif saya.

Gambar 1. BanditHijo’s Blog versi pertama
Sebagai gambaran, seperti ini Jekyll custom tag yang saya gunakan pada versi pertama:
{% shell_user %}
sudo pacman -Syu
{% endshell_user %}
$ sudo pacman -Syu
{% shell_term postgres> %}
CONNECT my_database;
{% endshell_term %}
postgres> CONNECT my_database;
{% image https://image.url/123/image_01.png | 1 %}
{% image https://image.url/123/image_01.png | 1 | Caption di sini %}
Dan masih banyak lagi notasi khusus yang saya pergunakan pada versi pertama. Artikel lengkapnya bisa di baca di sini: Membuat Jekyll Custom Tags dengan Liquid Tags.
Pada mulanya, saya merasa bahwa dengan menggunakan custom tag/notasi markdown tersebut, saya dapat menulis artikel dengan lebih ekspresif. Namun, seiring berjalannya waktu, saya menyadari bahwa kompleksitas tersebut justru menghambat proses penulisan saya.
Saya juga sempat mencoba merubah format artikel di versi kedua [2], yang tadinya menggunakan Markdown pada versi pertama, saya ubah menjadi AsciiDoc. Pertimbangan saya menggunakan AsciiDoc, karena blog saya sebagian besar berisi hal-hal teknis. Sehingga cocok menggunakan notasi AsciiDoc yang luwes untuk dokumen teknis.

Gambar 2. BanditHijo’s Blog versi kedua
Namun, setelah mencoba beberapa waktu, dan memigrasikan beberapa artikel saya, proses build menjadi lebih lambat dan proses menulis menggunakan AsciiDoc ternyata tidak semudah yang saya bayangkan. Akhirnya, saya memutuskan untuk kembali lagi menggunakan versi pertama.
AsciiDoc memang memiliki kelebihan dalam hal penulisan dokumen teknis, namun untuk blog pribadi yang lebih banyak berisi artikel ringan dan tutorial, saya merasa bahwa Markdown lebih sesuai dengan kebutuhan saya. Tapi, saya masih tetap menggunakan AsciiDoc untuk membuat dokumentasi teknis yang saya berikan untuk pihak lain. Karena membuat dokumentasi teknis dengan AsciiDoc sangat efisien. Karena hanya cukup menyediakan template dokumen, kemudian tinggal menulis kontennya saya, lalu build menjadi dokumen.
Saya menggunakan AsciiDoctor sebagai tool untuk formatingnya, dan AsciiDoctor PDF untuk merubah menjadi format PDF. Keduanya adalah tool yang sangat powerful untuk membuat dokumentasi teknis.
AsciiDoc menurut saya levelnya berada di antara Markdown dan LaTeX. Jika Markdown terlalu sederhana, dan LaTeX terlalu kompleks, maka AsciiDoc adalah solusi di tengah-tengahnya.
Seiring berjalannya waktu, saya menyadari bahwa semakin sederhana proses penulisan, semakin bersemangat saya untuk menulis. Karena custom tag/notasi markdown yang saya gunakan pada versi pertama justru membuat proses menulis menjadi lebih rumit dan mengganggu alur kreatif saya. Meskipun saya sudah menggunakan snippet code di Neovim untuk mempercepat penulisan, tetap saja prosesnya terasa kurang praktis buat saya.
Maka dari itu, di pertengahan tahun 2025, saya memutuskan untuk membuat perubahan besar pada cara saya menulis artikel di blog ini. Setelah beberapa tahun menggunakan berbagai notasi syntax, saya merasa perlu untuk menyederhanakan proses penulisan agar tetap mempertahankan mood dalam menulis. Oleh karena itu, pada BanditHijo Versi 3, saya berfokus pada penggunaan CommonMark specification sebagai standar penulisan artikel.
Ada beberapa keuntungan yang saya dapatkan dengan mengikuti CommonMark specification:
Untuk menjaga konsistensi format penulisan, saya mendokumentasikan rules dalam menulis di sini “Writing Rules” yang berisi panduan menulis artikel di blog ini.
Saya berusaha semaksimal mungkin mengikuti CommonMark specification dan seminimal mungkin menggunakan modifikasi tag/notasi markdown dalam menulis artikel.
Saya menggunakan thema baru yang lebih minimalis dan fokus pada konten artikel.
Kali ini saya tidak lagi menggunakan plain CSS yang dibuat sendiri seperti pada versi sebelumnya, melainkan menggunakan framework CSS bernama Tailwind CSS. Framework ini memungkinkan saya untuk membuat desain yang responsif dan modern dengan lebih mudah.

Gambar 3. BanditHijo’s Blog versi ketiga
Sebelumnya, pada versi pertama, saya menyimpan semua gambar di layanan pihak ketiga, seperti postimages.org. Pada mulanya, saya pikir gambar akan membuat repo blog ini menjadi gemuk dan sulit di-manage. Namun, setelah mempertimbangkan berbagai aspek, saya memutuskan untuk menyimpan semua gambar langsung di dalam repository blog ini. Ceritanya bisa dibaca di sini, Preferensi Saya dalam Menyimpan Assets Gambar di Jekyll.
Untuk manajemen assets seperti gambar, saya membuat struktur direktori yang lebih terorganisir di dalam repository blog ini. Setiap artikel memiliki direktori khusus untuk menyimpan gambar dan file terkait lainnya. Hal ini memudahkan saya dalam mengelola assets baik berupa gambar maupun file lainnya.
📂 _posts/
└ 📂 blog/
└ 📂 2025/
└ 📄 2025-12-29-ruby-programmers-best-friend.md 👈 artikel
📂 assets/
│ 📁 css/
│ 📂 images/
└ 📂 posts/
└ 📂 blog/
└ 📂 2025/
└ 📂 2025-12-29-ruby-programmers-best-friend/ 👈 direktori assets
│ 📄 file-01.pdf
│ 📄 gambar-01.png
└ 📄 gambar-02.png
⚙️ _config.yml
📄 index.markdown
📄 README.md
Kemudian pada front matter artikel, saya menambahkan variabel assets yang berisi path ke direktori assets artikel tersebut. Sehingga saat menulis artikel, saya hanya perlu merujuk ke variabel assets untuk menyisipkan gambar atau file lainnya.
---
layout: "post"
title: "Ruby, Programmer's Best Friend"
date: "2025-12-29 08:00"
permalink: "/blog/:title"
assets: "/assets/posts/blog/2025/2025-12-29-ruby-programmers-best-friend" # 👈 direktori assets
author: "BanditHijo"
category: "blog"
tags: ["ruby"]
description: "Ruby adalah bahasa pemrograman yang dirancang untuk kemudahan dan produktivitas."
---
Kemudian pada artikel, saya dapat mengakses variabel assets dengan {{ page.assets }}.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.


Lorem ipsum dolor sit amet, consectetur adipiscing elit, [Download File 1]({{ page.assets }}/file-01.pdf).
Dengan pendekatan ini, saya mendapatkan beberapa keuntungan, di antaranya:
Tak terasa sudah sejak 2018 saya menulis di blog ini dengan berbagai format penulisan. Dari mulai menggunakan custom tag/notasi markdown di versi pertama, mencoba AsciiDoc di versi kedua, hingga akhirnya kembali ke Markdown dengan mengikuti CommonMark specification di versi ketiga ini.
Dengan perubahan ini, saya berharap dapat lebih fokus pada konten artikel tanpa terganggu oleh aspek teknis penulisan.
Terima kasih saya ucapkan untuk teman-teman yang telah mengikuti perjalanan blog ini hingga versi ketiga. Terima kasih karena telah memberikan testimonial dan feedback dalam beberapa kesempatan ketika berinteraksi dengan saya, baik secara langsung maupun tidak langsung melalui media sosial dan juga email. Saya senang jika blog ini dapat memberikan manfaat dan inspirasi buat teman-teman yang membacanya.
Mumpung masih awal tahun, saya mengajak teman-teman, “Ayo nulis blog juga yaa!”
https://github.com/bandithijo/bandithijo.github.io_v1
Tanggal diakses: 2026-01-16
https://github.com/bandithijo/bandithijo.github.io_v2
Tanggal diakses: 2026-01-16
https://github.com/bandithijo/bandithijo.github.io_v3
Tanggal diakses: 2026-01-16
wordpress 6.x
WordPress Multisite adalah fitur bawaan WordPress yang memungkinkan Anda untuk membuat dan mengelola beberapa situs web dari satu instalasi WordPress. Fitur ini sangat berguna bagi mereka yang ingin mengelola jaringan situs web, seperti blog jaringan, situs klien, atau situs dengan subdomain atau subdirektori. Dalam catatan ini, saya akan mendokumentasikan langkah-langkah untuk mengatur WordPress Multisite.
Ada beberapa tahapan proses instalasi WordPress Multisite.
Edit file wp-config.php pada instalasi WordPress, dan cari bagian /* That's all, stop editing! Happy publishing. */. Tambakan kode berikut tepat sebelum baris tersebut.
!filename: wp-config.php
/* Multisite enable */
define( 'WP_ALLOW_MULTISITE', true );
/* That's all, stop editing! Happy publishing. */
Setelah menambahkan kode tersebut, simpan perubahan pada file wp-config.php.
Pada bagian WordPress Admin Dashboard, Anda akan melihat pada menu Tools terdapat submenu baru bernama Network Setup.

Gambar 1. Sebelum mengaktifkan fitur Multisite

Gambar 2. Setelah mengaktifkan fitur Multisite
Kemudian, coba refresh halaman admin dashboard WordPress Anda untuk memastikan bahwa fitur Multisite telah diaktifkan dengan benar.

Gambar 3. Halaman Network Setup
Karena saya membuat di localhost, maka yang direkomendasikan adalah menggunakan subdirektori.
Selain subdirektori sebenarnya ada pilihan subdomain, namun untuk menggunakan subdomain di localhost perlu konfigurasi tambahan pada server lokal. Jadi saya pakai subdirektori saja.
INFO
Jika bukan di localhost, di halaman Network Setup, sebenarnya akan diminta untuk mengkonfigurasi jaringan multisite dengan pilihan apakah ingin menggunakan subdomain (site1.example.com) atau subdirektori (example.com/site1).
Setelah menentukan Network Title dan Network Admin Email, klik tombol Install untuk melanjutkan proses instalasi.

Gambar 4. Konfigurasi tambahan setelah instalasi Multisite untuk dipasang di wp-config.php dan .htaccess
Setelah mengkonfigurasi jaringan, WordPress akan memberikan beberapa kode yang perlu ditambahkan ke file wp-config.php dan .htaccess. Tambahkan kode tersebut sesuai petunjuk yang diberikan.
!filename: wp-config.php
/* Multisite enable */
define( 'WP_ALLOW_MULTISITE', true );
/* Multisite settings */
define( 'MULTISITE', true );
define( 'SUBDOMAIN_INSTALL', false );
define( 'DOMAIN_CURRENT_SITE', 'localhost' );
define( 'PATH_CURRENT_SITE', '/' );
define( 'SITE_ID_CURRENT_SITE', 1 );
define( 'BLOG_ID_CURRENT_SITE', 1 );
/* That's all, stop editing! Happy publishing. */
!filename: .htaccess
# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]
</IfModule>
# END WordPress
Setelah ditambahkan, simpan perubahan pada kedua file tersebut.
Kemudian, tekan link “Log in” di pojok kiri bawah halaman tersebut untuk masuk kembali ke dashboard admin WordPress.
Setelah login kembali, akan terdapat menu My Sites di bagian atas dashboard admin WordPress, yang menunjukkan bahwa jaringan multisite telah berhasil dikonfigurasi.

Gambar 5. Menu My Sites di dashboard admin WordPress

Gambar 6. WordPress Network Admin Dashboard

Gambar 7. Daftar situs di WordPress Multisite

Gambar 8. Plugin Install di WordPress Multisite. Bisa mengaktifkan untuk semua situs dengan “Network Activate”
Selesai!
WordPress: WordPress Multisite / Network
Tanggal diakses: 2026-01-13
YouTube/WordPress: Setting up a WordPress multisite network
Tanggal diakses: 2026-01-13
neovim 0.10.x
Language Server Protocol sangat membantu developer saat proses developement. Ruby LSP adalah LSP untuk Ruby yang terbilang masih cukup baru, yang mulai populer digunakan dikalangan Ruby programmer dan Rails developer. Catatan ini akan mendokumentasikan cara saya melakukan setup terhadap Ruby LSP di Neovim.

Gambar 1. Ruby LSP icon
The Ruby LSP is an implementation of the language server protocol for Ruby, used to improve rich features in editors. It is a part of a wider goal to provide a state-of-the-art experience to Ruby developers using modern standards for cross-editor features, documentation and debugging.
Install ruby-lsp gem menggunakan perintah berikut:
$ gem install ruby-lsp
Karena saya menggunakan standard sebagai code formatter dan linter, maka saya juga menginstall standard gem.
$ gem install standard
Saya menggunakan plugin nvim-lspconfig untuk mengatur LSP di Neovim.
Berikut adalah konfigurasi yang saya gunakan untuk mengaktifkan Ruby LSP di Neovim.
!filename: lua/lsp/init.lua
local lspconfig = require('lspconfig')
lspconfig.ruby_lsp.setup({
init_options = {
formatter = 'standard',
linters = { 'standard' },
addonSettings = {
rails = true,
rspec = true,
},
},
})
Just it! Ruby LSP sudah aktif di Neovim.
Hanya sesederhana itu saja instalasi dan konfigurasi Ruby LSP di Neovim. Selamat mencoba!
Dan untuk Rails, sudah otomatis terdeteksi jika berada di dalam project Rails.
Ruby LSP detects Rails projects and installs the Rails add-on for you.
Saya sangat merekomendasikan Ruby LSP ini untuk dicoba, terutama bagi Anda yang sering menggunakan Neovim sebagai editor utama. Fitur-fitur yang ditawarkan sangat membantu dalam meningkatkan produktivitas saat coding Ruby atau Rails.
Sejak Maret 2025, saya sudah mulai menggunakan Ruby LSP ini di Neovim, setelah sebelumnya sejak 2019 menggunakan Solargraph.

Gambar 2. Post terkait Ruby LSP di Threads saya.
Pengalaman saya sejauh ini sangat positif. Saya bahkan lupa kalau saya menggunakan Ruby LSP dan bukan lagi Solargraph.
https://shopify.github.io/ruby-lsp/
Tanggal diakses: 2026-01-10
https://shopify.github.io/ruby-lsp/editors#neovim
Tanggal diakses: 2026-01-10
https://shopify.github.io/ruby-lsp/rails-add-on.html
Tanggal diakses: 2026-01-10
GitHub: Shopify/ruby-lsp
Tanggal diakses: 2026-01-10
https://rubygems.org/gems/ruby-lsp
Tanggal diakses: 2026-01-10
https://rubygems.org/gems/standard
Tanggal diakses: 2026-01-10
Tahun 2019 adalah tahun ketika saya memasuki dunia Software Development secara profesional untuk pertama kalinya.
Sebagai seorang Software Developer yang masih baru, saya memiliki banyak sekali tenaga dan curiosity untuk belajar hal-hal baru. Saya sering menghabiskan waktu berjam-jam di depan komputer untuk menulis kode, bereksperimen dengan teknologi baru, mengutak-atik sistem operasi Linux, dan menyelesaikan berbagai proyek percobaan.
Sampai suatu malam yang telah larut, saya melihat ada tetesan darah di atas meja kerja yang berasal dari hidung saya. Saya terkejut dan mulai menyadari bahwa mungkin saja saya telah menghabiskan terlalu banyak waktu di depan layar komputer tanpa istirahat yang cukup.
Kebetulan, pada saat itu di kantor tempat saya bekerja, sudah di-encourage untuk menggunakan WakaTime sebagai alat untuk melacak waktu (time tracker) yang dihabiskan di depan komputer. Saya coba melihat dashboard WakaTime. Ternyata, saya telah menghabiskan lebih dari 12 jam sehari di depan komputer selama beberapa hari berturut-turut.
WakaTime adalah alat pelacak waktu (time tracker) yang dirancang khusus untuk programmer. Dengan WakaTime, saya dapat melacak berapa lama waktu yang saya habiskan untuk menulis kode, serta mendapatkan wawasan tentang produktivitas saya. Beberapa alasan mengapa saya memilih WakaTime adalah:
WakaTime memungkinkan saya untuk membagikan statistik produktivitas saya dengan mudah melalui fitur Shareables https://wakatime.com/share (Login dengan akun WakaTime untuk membuat Shareables).
Berikut ini adalah beberapa contoh Shareables status WakaTime saya.
Di akhir tahun, WakaTime menyediakan ringkasan statistik tahunan yang menarik. Saya dapat melihat berapa banyak waktu yang saya habiskan untuk menulis kode setiap harinya, bahasa pemrograman yang paling sering saya gunakan, dan proyek-proyek yang paling banyak saya kerjakan.
Berikut ini adalah ringkasan statistik kode saya dari WakaTime setiap tahunnya sejak 2019 hingga 2025.

Gambar 1. Code Stats for 2019, https://wakatime.com/a-look-back-at-2019 (Login to see your stats)

Gambar 2. Code Stats for 2020, https://wakatime.com/a-look-back-at-2020 (Login to see your stats)

Gambar 3. Code Stats for 2021, https://wakatime.com/a-look-back-at-2021 (Login to see your stats)

Gambar 4. Code Stats for 2022, https://wakatime.com/a-look-back-at-2022 (Login to see your stats)

Gambar 5. Code Stats for 2023, https://wakatime.com/a-look-back-at-2023 (Login to see your stats)

Gambar 6. Code Stats for 2024, https://wakatime.com/a-look-back-at-2024 (Login to see your stats)

Gambar 7. Code Stats for 2025, https://wakatime.com/a-look-back-at-2025 (Login to see your stats)
Kalau di dunia olahraga ada Strava yang digunakan untuk melacak aktivitas bersepeda atau lari, maka WakaTime bisa dianggap sebagai Strava-nya para programmer. Dengan WakaTime, saya bisa melihat “rute” coding saya, “kecepatan” menulis kode, dan “jarak” yang telah saya tempuh dalam dunia pemrograman.

Gambar 8. Saya memodifikasi Gambar 7 dengan memberikan background foto laptop saya dengan menggunakan GIMP (GNI Image Manipulation Program)
Untuk membantu memahami WakaTime lebih jauh, bisa dibaca di halaman FAQ resmi WakaTime di https://wakatime.com/faq.
Halaman ini meliputi berbagai pertanyaan umum seperti:
Melacak waktu yang dihabiskan untuk coding dengan WakaTime telah membantu saya menjadi lebih sadar akan produktivitas dan pentingnya menjaga keseimbangan antara kerja dan istirahat.
Saya merekomendasikan WakaTime kepada sesama teman programmer untuk membantu mereka memahami kebiasaan kerja mereka dan meningkatkan produktivitas secara keseluruhan.
Terima kasih telah membaca cerita saya tentang pengalaman menggunakan WakaTime. Semoga bermanfaat!