Cara Host Newsletter Sendiri dengan Listmonk di VPS
Pasang Listmonk v6.2.0 di Ubuntu 24.04 dengan PostgreSQL, config.toml, systemd, TLS, dan SMTP, lalu pahami biaya serta waktu membangun keterkiriman.
Kebutuhan newsletter yang di-host sendiri di Listmonk
Listmonk adalah pengelola newsletter dan milis yang di-host sendiri: satu biner Go, satu basis data PostgreSQL, satu file konfigurasi, dan satu unit systemd. VPS kecil dapat menjalankannya tanpa kesulitan karena Listmonk menyimpan pelanggan dan mengantrekan kampanye, tetapi tidak pernah mengirimkan email itu sendiri. Listmonk menyerahkan setiap pesan ke server SMTP (simple mail transfer protocol), sehingga tingkat keterkiriman Anda ditentukan oleh reputasi server tersebut, bukan oleh perangkat lunak ini.
Panduan ini memasang Listmonk v6.2.0, rilis terbaru per Juli 2026, di Ubuntu 24.04. Anda memerlukan VPS dengan alamat IP publik, nama domain yang Anda kendalikan, dan PostgreSQL 12 atau yang lebih baru. Instalasi memerlukan waktu sekitar satu jam. Membangun reputasi pengiriman memerlukan waktu berminggu-minggu, dan bagian tersebut dibahas menjelang akhir.
Instal PostgreSQL dan buat database
Ubuntu 24.04 menyediakan PostgreSQL 16 di repositorinya sendiri, jauh lebih baru daripada versi yang diperlukan Listmonk.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlBuat role dan database dalam satu sesi psql. -v ON_ERROR_STOP=1 membuat psql keluar setelah pernyataan pertama yang gagal, sehingga kesalahan ketik tidak menghasilkan konfigurasi setengah jadi yang tampak selesai.
sudo -u postgres psql -v ON_ERROR_STOP=1 <<'SQL'
CREATE USER listmonk WITH PASSWORD 'pick-a-long-random-password';
CREATE DATABASE listmonk OWNER listmonk;
SQLOWNER listmonk bukan sekadar hiasan. Instalasi skema membuat tabel, tipe, indeks, dan fungsi, sehingga role harus memiliki database tersebut. Jika Listmonk diarahkan ke database yang dimiliki role lain, instalasi berhenti dengan permission denied, bahkan setelah Anda menjalankan GRANT CONNECT.
Pastikan database tersebut ada sebelum melanjutkan.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Perintah itu mencetak listmonk. Baris kosong berarti pernyataan CREATE tidak pernah dijalankan, jadi baca kembali keluaran psql.
Instal binary Listmonk
Listmonk merilis binary statis untuk setiap arsitektur. Periksa arsitektur VPS Anda terlebih dahulu, karena binary amd64 pada VPS ARM akan ditolak kernel saat dijalankan.
dpkg --print-architecture
cd /tmp
curl -fsSLO https://github.com/knadh/listmonk/releases/download/v6.2.0/listmonk_6.2.0_linux_amd64.tar.gz
tar -xzf listmonk_6.2.0_linux_amd64.tar.gz
sudo install -m 755 listmonk /usr/bin/listmonk
listmonk --versionPada VPS ARM, ganti amd64 dengan arm64 pada nama file. listmonk --version yang menampilkan string versi merupakan bukti awal bahwa binary tersebut cocok dengan mesin.
Buat config.toml dan batasi aksesnya
--new-config menulis config.toml ke direktori kerja saat ini. Karena itu, cd berada di dalam sh -c, bukan sebelum sudo.
sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'File yang dihasilkan berukuran kecil. Di bawah [app], address = "localhost:9000" mengikat server HTTP hanya ke loopback, sehingga panel admin tidak dapat diakses dari internet sampai Anda memasang reverse proxy di depannya. Jangan ubah baris tersebut. Di bawah [db], Anda mendapatkan host = "localhost", port = 5432, user = "listmonk", database = "listmonk", dan ssl_mode = "disable". Nilai default tersebut sudah sesuai dengan database yang Anda buat, sehingga satu-satunya baris yang harus diubah adalah kata sandi.
ssl_mode = "disable" sudah benar ketika Postgres mendengarkan pada loopback di komputer yang sama, karena traffic tersebut tidak pernah meninggalkan komputer. Jika database dipindahkan ke host lain, ubah nilainya menjadi require. Jika tidak, kata sandi akan melintasi jaringan dalam bentuk plaintext.
Edit baris kata sandi di bawah [db] agar sesuai dengan role, lalu buat akun layanan dan batasi akses file tersebut sehingga login lain tidak dapat mengaksesnya.
sudo useradd --system --home-dir /var/lib/listmonk --create-home --shell /usr/sbin/nologin listmonk
sudo chown -R root:listmonk /etc/listmonk
sudo chmod 640 /etc/listmonk/config.tomlSekarang akun layanan dapat membaca file tersebut dan tidak ada akun lain yang dapat membacanya.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlPerintah pertama menampilkan readable. Perintah kedua menampilkan root:listmonk 640. Akun tanpa hak istimewa lain yang mencoba cat yang sama akan mendapatkan Permission denied. Inilah tujuannya: file ini menyimpan kata sandi database dalam bentuk plaintext, dan server biasanya memiliki lebih dari satu akun login. Alasan yang sama berlaku untuk setiap layanan yang Anda jalankan. Karena itu, baca akun layanan dengan hak akses minimum sekali dan terapkan prinsip tersebut di semua tempat.
Buat skema dengan --install
--install membuat tabel dan mengisi pengaturan default. Tetapkan login admin pertama dengan variabel lingkungan agar akun tersebut sudah ada sebelum panel dapat diakses.
sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
listmonk --config /etc/listmonk/config.toml --install --yes--yes menjawab prompt konfirmasi. Baca prompt tersebut sekali sebelum mengotomatiskannya, karena --install adalah penginstal pertama kali dan akan menghapus skema Listmonk yang sudah ada. Menjalankannya untuk kedua kali pada database aktif akan menghapus subscriber Anda. Dalam skrip apa pun yang mungkin dijalankan dua kali, gunakan --install --idempotent --yes, yang tidak melakukan apa pun jika tabel sudah ada. Perubahan skema yang dirilis dalam release baru diterapkan dengan --upgrade, bukan dengan --install.
Periksa hasilnya dari sisi database, bukan dari browser.
sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"Perintah pertama menampilkan tabel Listmonk, termasuk subscribers, lists, campaigns, templates, dan bounces. Perintah kedua menampilkan admin. Hasil kosong dari perintah kedua berarti variabel lingkungan tidak pernah diteruskan ke proses, sehingga panel akan meminta Anda membuat pengguna pertama melalui browser.
Menjalankan Listmonk dengan systemd
Tulis /etc/systemd/system/listmonk.service.
[Unit]
Description=Listmonk newsletter and mailing list manager
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=listmonk
Group=listmonk
WorkingDirectory=/var/lib/listmonk
ExecStart=/usr/bin/listmonk --config /etc/listmonk/config.toml
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.targetWorkingDirectory penting karena Listmonk menyelesaikan jalur relatif, termasuk jalur unggahan media pada sistem berkas, berdasarkan direktori tersebut. After=postgresql.service hanya mengatur urutan startup; perintah ini tidak menunggu Postgres menerima koneksi. Karena itu, Restart=on-failure menangani kondisi saat Listmonk dimulai terlalu cepat dan tidak dapat terhubung.
sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/ss seharusnya menampilkan 127.0.0.1:9000 dalam status LISTEN. curl yang mengembalikan baris status HTTP apa pun berarti server merespons. Jika curl gagal dengan Connection refused, berarti proses berhenti selama startup, dan journalctl -u listmonk -n 50 --no-pager akan menjelaskan penyebabnya. Perhatikan bahwa enable --now adalah bagian yang tetap berjalan setelah reboot: proses yang dimulai secara manual akan berhenti setelah pemutakhiran kernel berikutnya.
Menempatkan nginx dan TLS di depan
Listmonk menggunakan HTTP biasa pada loopback. Karena itu, nginx mengakhiri TLS (keamanan lapisan transport) dan meneruskan permintaan.
server {
listen 443 ssl;
server_name lists.example.com;
client_max_body_size 25m;
location / {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}client_max_body_size harus dinaikkan karena impor pelanggan dan unggahan media merupakan unggahan file. Secara default, nginx menolak unggahan berukuran lebih dari 1 MB dengan 413 Request Entity Too Large. Terbitkan sertifikat menggunakan certbot. certbot juga menulis baris listen 443 ssl dan pengalihan dari port 80 untuk Anda. Langkah-langkahnya tersedia dalam panduan sertifikat Let's Encrypt untuk nginx. Buka port 80 dan 443, lalu biarkan port 9000 tetap tertutup karena proxy mengaksesnya melalui loopback. Jika firewall belum dikonfigurasi, mulai dengan dasar-dasar firewall ufw.
Kemudian buka panel admin dan tetapkan URL root di bagian Settings ke https://lists.example.com. Instalasi baru menggunakan http://localhost:9000. Listmonk menulis nilai tersebut ke setiap tautan berhenti berlangganan dan URL media yang disertakan dalam email. Jika Anda mengirim kampanye sebelum mengubahnya, setiap penerima akan memperoleh tautan yang mengarah ke komputer mereka sendiri. Tautan tersebut gagal dibuka oleh penerima. Bagi filter spam, kondisi ini terlihat seperti pengirim yang tidak dapat mengonfigurasi domainnya sendiri.
Hubungkan SMTP, yang tidak ada di config.toml
Cari bagian SMTP di config.toml, tetapi Anda tidak akan menemukannya. Pengaturan email tersimpan di database, dalam tabel settings, dan Anda mengeditnya di panel admin melalui Settings dan SMTP. Karena itu, file yang dihasilkan tetap singkat. Perubahan SMTP juga tidak memerlukan restart.
Ada dua pilihan yang tepat untuk server SMTP itu sendiri. Jalankan server Anda sendiri. Dengan demikian, reputasi sepenuhnya berada dalam kendali Anda, tetapi ini merupakan proyek tersendiri: menjalankan server email sendiri dengan Mailcow menjelaskan hal-hal yang diperlukan. Atau arahkan Listmonk ke relay transaksional dan serahkan pengelolaan reputasi IP kepada pihak lain.
Apa pun pilihannya, gunakan port 587 dengan STARTTLS, atau port 465 dengan TLS implisit. Jangan rencanakan penggunaan port keluar 25. Sebagian besar penyedia VPS memblokirnya secara default pada akun baru. Port 25 yang diblokir tampak persis seperti koneksi yang macet karena paket dibuang, bukan ditolak. Akibatnya, klien menunggu hingga waktu tunggu habis, bukan langsung gagal.
Uji sebelum mengandalkannya. Buat sebuah daftar, tambahkan alamat Anda sendiri sebagai pelanggan, lalu kirim kampanye kepada satu penerima. Buka pesan yang diterima dan baca header lengkapnya. Header Authentication-Results yang ditambahkan oleh sisi penerima menunjukkan apakah SPF dan DKIM berhasil.
Deliverabilitas adalah seluruh tugas
Listmonk menyusun pesan, melacak daftar, dan menyerahkan email untuk dikirim. Semua keputusan tentang apakah email tersebut sampai ke kotak masuk dibuat oleh penyedia layanan penerima, berdasarkan alamat IP pengiriman dan domain pengiriman. IP VPS baru sama sekali belum memiliki riwayat, dan setiap penyedia kotak surat besar menganggap ketiadaan riwayat sebagai indikasi yang agak mencurigakan.
Empat hal berikut wajib tersedia:
- Catatan TXT SPF (sender policy framework) yang mencantumkan host yang diizinkan mengirim untuk domain Anda.
- Kunci DKIM (domainkeys identified mail) yang dipublikasikan sebagai catatan TXT, dengan proses penandatanganan dilakukan oleh server email, bukan oleh Listmonk.
- Catatan DMARC (domain based message authentication, reporting and conformance), yang memberi tahu penerima tindakan yang harus dilakukan jika dua pemeriksaan pertama gagal.
- Kotak surat bounce yang dibaca Listmonk, sehingga alamat yang menolak email dikeluarkan dari daftar, bukan terus dicoba tanpa batas.
Setelah itu, kirim email secara perlahan pada tahap awal. Domain yang belum pernah mengirim email lalu tiba-tiba mengirimkan sepuluh ribu pesan dalam satu jam memiliki pola yang persis seperti akun yang telah dibobol, sehingga akan difilter seperti itu. Mulailah dengan pelanggan yang paling aktif, lalu tingkatkan volume selama beberapa hari.
Setiap template juga memerlukan tautan berhenti berlangganan yang berfungsi. Dalam template Listmonk, tautan tersebut adalah {{ UnsubscribeURL }}, dan isi kampanye ditempatkan di lokasi {{ template "content" . }}, yang harus muncul tepat satu kali dalam setiap template. Kampanye tanpa tautan berhenti berlangganan akan menghasilkan keluhan spam, bukan permintaan berhenti berlangganan. Keluhan adalah cara tercepat untuk kehilangan reputasi pengiriman yang telah Anda bangun selama berminggu-minggu.
Cadangan, dan hal yang sebenarnya diperlukan untuk pemulihan
Dua hal harus disimpan di luar server: dump database dan config.toml. Tambahkan direktori media jika Anda mengunggah gambar ke dalam kampanye.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpDump tersebut berisi pelanggan, kampanye, templat, dan semua pengaturan, termasuk kredensial SMTP. Karena itu, enkripsi dump dan simpan di luar server ini. Penjadwalannya sudah tersedia: lihat cadangan restic terenkripsi ke penyimpanan jarak jauh. config.toml hanya terdiri dari beberapa baris, tetapi berisi kata sandi database. Perlakukan file tersebut dengan cara yang sama.
Pemutakhiran harus mengikuti urutan tertentu. Hentikan layanan, buat dump, ganti biner di /usr/bin, jalankan listmonk --config /etc/listmonk/config.toml --upgrade, lalu mulai layanan. Migrasi skema hanya berjalan ke depan, sehingga dump tersebut adalah satu-satunya cara untuk kembali ke kondisi sebelumnya.
Mengapa Listmonk gagal dimulai?
Baca jurnal terlebih dahulu dengan journalctl -u listmonk -n 50 --no-pager. Hampir setiap kegagalan saat dimulai tercatat dalam satu baris di blok [db].
pq: password authentication failed for user "listmonk" berarti kata sandi di [db] tidak cocok dengan role Postgres. Awalan pq menunjukkan bahwa driver Postgres melaporkan penolakan dari server. Ini berarti konfigurasi telah dibaca dengan benar, tetapi kredensialnya salah. Atur ulang role dengan sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", lalu masukkan string yang sama persis ke dalam file tersebut.
pq: database "listmonk" does not exist berarti nilai database di [db] tidak merujuk ke database yang benar-benar ada. sudo -u postgres psql -l menampilkan database yang sebenarnya tersedia di server, termasuk ejaan yang keliru Anda gunakan.
permission denied selama --install berarti role dapat terhubung, tetapi bukan pemilik database tersebut. Karena itu, role tidak dapat membuat tabel di dalamnya. Perbaiki dengan sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;", lalu jalankan kembali instalasi.
Layanan tidak pernah dimulai dan jurnal menyebutkan file konfigurasi. Proses yang berjalan sebagai listmonk tidak dapat membuka config.toml yang dimiliki oleh root:root dengan mode 600. stat -c '%U:%G %a' /etc/listmonk/config.toml seharusnya menampilkan root:listmonk 640, dan direktori di atasnya seharusnya menampilkan root:listmonk 750.
Panel berfungsi, tetapi tidak ada email yang masuk. Ini bukan masalah saat memulai layanan. Periksa Settings dan SMTP terlebih dahulu, lalu periksa log kampanye itu sendiri di panel admin. Log tersebut mencatat kesalahan yang dikembalikan server email untuk setiap percobaan.
FAQ
Apakah saya memerlukan server email sendiri untuk menggunakan Listmonk?
Tidak. Listmonk bukan server email. Listmonk memerlukan kredensial SMTP untuk server yang menerima email Anda dan mengirimkannya. Server tersebut dapat berupa relay transaksional atau server email yang Anda kelola sendiri. Tetapkan kredensial tersebut di bagian Settings dan SMTP pada panel admin, bukan di config.toml, karena pengaturan email disimpan di database. Gunakan port 587 dengan STARTTLS atau port 465 dengan TLS implisit, karena sebagian besar penyedia VPS memblokir port keluar 25 pada akun baru.
Mengapa tautan dalam kampanye saya mengarah ke localhost:9000?
Pengaturan URL root masih menggunakan nilai bawaan saat instalasi, yaitu http://localhost:9000. Listmonk menulis nilai tersebut ke dalam tautan berhenti berlangganan dan URL media saat kampanye dikirim. Buka Settings pada panel admin, tetapkan URL root ke alamat HTTPS yang sebenarnya, lalu simpan. Pesan yang sudah dikirim tidak dapat diperbaiki. Karena itu, kirim kampanye uji kepada diri sendiri dan klik tautan berhenti berlangganan di dalamnya sebelum mengirim email ke daftar yang sebenarnya.
Apakah menjalankan --install lagi akan menghapus subscriber saya?
Ya. --install adalah installer untuk penggunaan pertama dan menghapus skema Listmonk yang ada. --yes menghapus prompt yang seharusnya memperingatkan Anda. Dalam skrip yang mungkin dijalankan dua kali, gunakan --install --idempotent --yes. Perintah tersebut tidak melakukan apa pun jika tabel sudah ada. Untuk menerapkan perubahan skema pada rilis baru, hentikan service, buat pg_dump, lalu jalankan --upgrade.
Mengapa Listmonk menampilkan pesan password authentication failed for user listmonk?
Password dalam blok [db] di /etc/listmonk/config.toml tidak cocok dengan role Postgres yang memiliki nama sama. Baris journal-nya adalah pq: password authentication failed for user "listmonk". pq adalah driver Postgres yang meneruskan penolakan dari server. Ini berarti file konfigurasi ditemukan dan dibaca. Atur ulang password role tersebut dengan sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", tulis string yang sama persis ke dalam file konfigurasi, lalu jalankan sudo systemctl restart listmonk.