Listmonk'u VPS'te Kendi Bülteniniz İçin Barındırma
Ubuntu 24.04 üzerinde Listmonk v6.2.0 kurulumunu öğrenin: PostgreSQL, config.toml, systemd, TLS ve SMTP ayarlarıyla teslimat maliyetlerini anlayın.
Listmonk ile kendi sunucunuzda barındırılan bir bülten için gerekenler
Listmonk, kendi sunucunuzda barındırabileceğiniz bir bülten ve posta listesi yöneticisidir: bir Go ikili dosyası, bir PostgreSQL veritabanı, bir yapılandırma dosyası ve bir systemd birimi kullanır. Küçük bir VPS üzerinde sorunsuz çalışır. Bunun nedeni, Listmonk'un aboneleri ve kampanya kuyruklarını saklaması, ancak postayı kendisinin göndermemesidir. Her iletiyi bir SMTP (simple mail transfer protocol) sunucusuna iletir. Bu nedenle teslim oranınızı bu yazılım değil, SMTP sunucusunun itibarı belirler.
Bu kılavuzda, Temmuz 2026 itibarıyla güncel sürüm olan Listmonk v6.2.0, Ubuntu 24.04 üzerine kurulmaktadır. Genel erişime açık bir IP adresine, kontrolünüzde olan bir alan adına ve PostgreSQL 12 veya daha yeni bir sürüme sahip bir VPS gerekir. Kurulum yaklaşık bir saat sürer. Gönderim itibarının oluşması haftalar alır. Bu konuya sonlara doğru değinilmektedir.
PostgreSQL'i kurma ve veritabanını oluşturma
Ubuntu 24.04, Listmonk'un gerektirdiğinden çok daha yeni olan PostgreSQL 16'yı kendi deposunda sunar.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlRolü ve veritabanını tek bir psql oturumunda oluşturun. -v ON_ERROR_STOP=1, ilk başarısız ifadede psql'den çıkılmasını sağlar. Böylece bir yazım hatası, kurulumu tamamlanmış gibi görünen ancak eksik bir durumda bırakmaz.
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 yalnızca biçimsel bir seçenek değildir. Şema kurulumu tablolar, türler, dizinler ve işlevler oluşturur. Bu nedenle rolün veritabanının sahibi olması gerekir. Listmonk'u başka bir role ait veritabanına yönlendirirseniz permission denied ile kurulum durur. Bu durum GRANT CONNECT komutunu çalıştırdıktan sonra bile devam eder.
Devam etmeden önce veritabanının mevcut olduğunu doğrulayın.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Bu işlem listmonk çıktısını verir. Boş bir satır, CREATE ifadesinin hiç çalışmadığı anlamına gelir. Bu durumda psql çıktısını yeniden inceleyin.
Listmonk binary dosyasını kurma
Listmonk, her mimari için statik bir binary yayımlar. Önce mimarinizi kontrol edin. ARM tabanlı bir VPS üzerinde amd64 binary dosyası kullanılırsa kernel bu dosyayı çalıştırmayı reddeder.
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 --versionARM tabanlı bir VPS üzerinde dosya adındaki amd64 ifadesini arm64 ile değiştirin. listmonk --version bir sürüm dizesi yazdırıyorsa binary dosyasının makineyle uyumlu olduğuna dair ilk kanıt elde edilir.
config.toml oluşturma ve erişimi kısıtlama
--new-config, config.toml dosyasını geçerli çalışma dizinine yazar. Bu nedenle cd, sh -c içinde yer alır; sudo öncesinde yer almaz.
sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'Oluşturulan dosya kısadır. [app] altında address = "localhost:9000", HTTP sunucusunu yalnızca loopback adresine bağlar. Bu nedenle yönetim paneline önüne bir reverse proxy yerleştirilene kadar internetten erişilemez. Bu satıra dokunulmamalıdır. [db] altında host = "localhost", port = 5432, user = "listmonk", database = "listmonk" ve ssl_mode = "disable" bulunur. Bu varsayılanlar, oluşturulan veritabanıyla zaten uyumludur. Değiştirilmesi gereken tek satır paroladır.
Postgres aynı makinede loopback üzerinde dinlediği sürece ssl_mode = "disable" doğrudur. Bu trafik makineden çıkmaz. Veritabanı başka bir ana bilgisayara taşınırsa require olarak ayarlanmalıdır. Aksi durumda parola ağ üzerinden açık metin olarak gönderilir.
[db] altındaki parola satırı, rolle eşleşecek şekilde düzenlenmelidir. Ardından hizmet hesabı oluşturulmalı ve dosyanın diğer tüm oturum hesapları tarafından okunması engellenmelidir.
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.tomlArtık hizmet hesabı dosyayı okuyabilir ve başka hiç kimse okuyamaz.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlİlk komut readable çıktısını verir. İkinci komut root:listmonk 640 çıktısını verir. Yetkisiz başka bir hesap aynı cat işlemini denediğinde Permission denied alır. Amaç budur: Bu dosya veritabanı parolanızı açık metin olarak içerir ve bir sunucuda genellikle birden fazla oturum hesabı bulunur. Aynı yaklaşım çalıştırılan her hizmet için geçerlidir. Bu nedenle en az ayrıcalıklı hizmet kullanıcılarını okuyup her yerde uygulayın.
--install ile şemayı oluşturma
--install tabloları oluşturur ve varsayılan ayarları ekler. İlk yönetici oturum açma bilgilerini ortam değişkenleriyle belirleyin. Böylece panel erişilebilir olmadan önce hesap oluşturulur.
sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
listmonk --config /etc/listmonk/config.toml --install --yes--yes onay istemini yanıtlar. Otomatikleştirmeden önce bu istemi bir kez okuyun. Çünkü --install ilk kurulum komutudur ve mevcut bir Listmonk şemasını siler. Canlı bir veritabanında ikinci kez çalıştırılması abonelerinizi siler. İki kez çalıştırılabilecek tüm betiklerde --install --idempotent --yes kullanın. Bu komut, tablolar zaten mevcutsa hiçbir işlem yapmaz. Yeni bir sürümde yayımlanan şema değişiklikleri --upgrade ile uygulanır; --install kullanılmaz.
Sonucu tarayıcıdan değil, veritabanı tarafından kontrol edin.
sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"İlk komut, aralarında subscribers, lists, campaigns, templates ve bounces bulunan Listmonk tablolarını listeler. İkinci komut admin çıktısını yazdırır. İkinci komuttan boş sonuç alınması, ortam değişkenlerinin işleme ulaşmadığını gösterir. Bu durumda panel, ilk kullanıcıyı tarayıcıda oluşturmanızı ister.
Listmonk'i systemd altında çalıştırma
/etc/systemd/system/listmonk.service yazılır.
[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 önemlidir; çünkü Listmonk, dosya sistemi üzerindeki medya yükleme yolu da dahil olmak üzere göreli yolları buna göre çözümler. After=postgresql.service yalnızca başlatma sırasını belirler; Postgres'in bağlantıları kabul etmesini beklemez. Bu nedenle Restart=on-failure, Listmonk'in biraz erken başlaması ve bağlanamaması durumunu ele alır.
sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/ss, LISTEN durumunda 127.0.0.1:9000 göstermelidir. curl herhangi bir HTTP durum satırı döndürüyorsa sunucu yanıt veriyor demektir. curl, Connection refused ile başarısız oluyorsa işlem başlatma sırasında sonlanmıştır; nedeni journalctl -u listmonk -n 50 --no-pager içinde belirtilir. enable --now değerinin yeniden başlatma sonrasında da geçerli kalan bölüm olduğu unutulmamalıdır: elle başlatılan bir işlem, bir sonraki kernel yükseltmesinden sonra sonlanır.
nginx ve TLS'yi öne alın
Listmonk loopback üzerinde düz HTTP kullanır. Bu nedenle nginx, TLS'yi (transport layer security) sonlandırır ve isteği iletir.
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;
}
}Abone içe aktarımları ve medya yüklemeleri dosya gönderimi olduğundan client_max_body_size değerinin artırılması gerekir. nginx, varsayılan olarak 1 MB üzerindeki her şeyi 413 Request Entity Too Large ile reddeder. Sertifikayı certbot ile oluşturun. certbot ayrıca listen 443 ssl satırlarını ve port 80 yönlendirmesini sizin için yazar. Adımlar nginx için Let's Encrypt sertifika kılavuzunda açıklanmıştır. 80 ve 443 portlarını açın. Proxy, 9000 portuna loopback üzerinden eriştiği için bu portu kapalı bırakın. Güvenlik duvarı henüz yapılandırılmadıysa ufw güvenlik duvarı temelleri ile başlayın.
Ardından yönetim panelini açın ve Settings altında root URL değerini https://lists.example.com olarak ayarlayın. Yeni bir kurulumda http://localhost:9000 bulunur. Listmonk, e-postaya eklediği her abonelikten çıkma bağlantısına ve medya URL'sine bu değeri yazar. Bu değeri değiştirmeden önce bir kampanya gönderirseniz her alıcı, kendi makinesine yönlendiren bağlantılar alır. Bu bağlantılar alıcıda çalışmaz. Spam filtresi açısından da gönderenin kendi alan adını yapılandıramadığı izlenimini verir.
config.toml içinde bulunmayan SMTP bağlantısını yapılandırma
config.toml içinde bir SMTP bölümü arandığında böyle bir bölüm bulunmaz. Posta ayarları veritabanındaki settings tablosunda tutulur ve yönetim panelinde Settings ve SMTP altından düzenlenir. Oluşturulan dosyanın bu nedenle kısa kalır. SMTP değişikliği için yeniden başlatma gerekmesinin nedeni de budur.
SMTP sunucusunun kendisi için iki gerçekçi seçenek vardır. Kendi sunucunuzu çalıştırabilirsiniz. Bu durumda itibar tamamen sizin sorumluluğunuzda olur ve bu, başlı başına bir projedir: Mailcow ile kendi posta sunucunuzu çalıştırma bunun neleri kapsadığını açıklar. Diğer seçenek, Listmonk'u işlemsel bir relay'e yönlendirmek ve IP itibarını başka bir hizmet sağlayıcının yönetmesine izin vermektir.
Her iki durumda da STARTTLS ile 587 numaralı portu veya örtük TLS ile 465 numaralı portu kullanın. Giden bağlantılar için 25 numaralı portu planlamayın. Çoğu VPS sağlayıcısı yeni hesaplarda bu portu varsayılan olarak engeller. Engellenen 25 numaralı port, bağlantının takılı kalmasıyla tamamen aynı görünür. Bunun nedeni paketlerin reddedilmek yerine düşürülmesidir. Bu nedenle istemci hızlıca hata vermek yerine zaman aşımını bekler.
Güvenmeden önce test edin. Bir liste oluşturun, kendi adresinizi abone olarak ekleyin ve tek alıcılı bir kampanya gönderin. Alınan iletiyi açın ve tam üstbilgileri okuyun. Alıcı tarafça eklenen Authentication-Results üstbilgisi, SPF ve DKIM denetimlerinin başarılı olup olmadığını gösterir.
Teslim edilebilirlik tüm iştir
Listmonk iletiyi oluşturur, listeyi izler ve postayı teslim eder. Bu postanın gelen kutusuna ulaşıp ulaşmayacağına ilişkin her karar, alıcı sağlayıcı tarafından gönderim IP adresi ve gönderim alan adı kullanılarak verilir. Yeni bir VPS IP adresinin hiç geçmişi yoktur ve her büyük posta kutusu sağlayıcısı geçmişi olmayan adresleri hafif şüpheli kabul eder.
Dört unsur isteğe bağlı değildir:
- Alan adınız için gönderim yapmasına izin verilen ana bilgisayarı belirten bir SPF (sender policy framework) TXT kaydı.
- TXT kaydı olarak yayımlanan bir DKIM (domainkeys identified mail) anahtarı. İmzalama Listmonk tarafından değil, posta sunucusu tarafından yapılmalıdır.
- İlk iki denetim başarısız olduğunda alıcıların ne yapacağını belirten bir DMARC (domain based message authentication, reporting and conformance) kaydı.
- Listmonk'un okuduğu bir geri dönme posta kutusu. Böylece postayı reddeden adresler sürekli yeniden denenmek yerine listeden çıkarılır.
Ardından başlangıçta yavaş gönderim yapılmalıdır. Daha önce hiç posta göndermemiş bir alan adı bir saat içinde aniden on bin ileti gönderirse, güvenliği ihlal edilmiş bir hesabın tam görünümünü sergiler ve bu şekilde filtrelenir. En etkileşimli abonelerle başlanmalı ve hacim günler içinde artırılmalıdır.
Her şablonda çalışan bir abonelikten çıkma bağlantısı da bulunmalıdır. Listmonk şablonunda bu bağlantı {{ UnsubscribeURL }} olur ve kampanya gövdesi, şablon başına tam olarak bir kez görünmesi gereken {{ template "content" . }} konumuna yerleşir. Abonelikten çıkma bağlantısı bulunmayan bir kampanya, abonelikten çıkma işlemleri yerine spam şikayetleri alır. Şikayetler, haftalar boyunca oluşturulan gönderim itibarını kaybetmenin en hızlı yoludur.
Yedekler ve geri yükleme için gerçekten gerekenler
İki öğe sunucudan çıkarılmalıdır: veritabanı dökümü ve config.toml. Kampanyalara görsel yüklüyorsanız medya dizini de yedeklenmelidir.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpBu döküm aboneleri, kampanyaları, şablonları ve SMTP kimlik bilgileri dahil tüm ayarları içerir. Bu nedenle döküm şifrelenmeli ve bu sunucunun dışında saklanmalıdır. Zamanlama sorunu çözülmüştür: uzak depolamaya şifrelenmiş restic yedekleri bölümüne bakılmalıdır. config.toml birkaç satırdan oluşur, ancak veritabanı parolasını içerir. Bu nedenle aynı şekilde ele alınmalıdır.
Yükseltmeler belirli bir sırayla yapılmalıdır. Hizmet durdurulmalı, döküm alınmalı, /usr/bin içindeki ikili dosya değiştirilmeli, listmonk --config /etc/listmonk/config.toml --upgrade çalıştırılmalı ve ardından hizmet başlatılmalıdır. Şema geçişleri yalnızca ileri yönde çalışır. Bu nedenle geri dönüş için tek yol bu dökümdür.
Listmonk neden başlatılamıyor?
Önce günlüğü journalctl -u listmonk -n 50 --no-pager ile okuyun. Başlatma hatalarının neredeyse tamamı [db] bloğunda tek satır olarak yer alır.
pq: password authentication failed for user "listmonk", [db] içindeki parolanın Postgres rolüyle eşleşmediği anlamına gelir. pq öneki, Postgres sürücüsünün sunucunun reddetme yanıtını bildirdiğini gösterir. Bu nedenle yapılandırma doğru okunmuş, ancak kimlik bilgileri hatalıdır. Rolü sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" ile sıfırlayın ve aynı dizeyi dosyaya yazın.
pq: database "listmonk" does not exist, [db] içindeki database değerinin gerçek bir veritabanını belirtmediği anlamına gelir. sudo -u postgres psql -l, yanlışlıkla kullandığınız yazım da dahil olmak üzere sunucuda gerçekten bulunanları listeler.
--install sırasında görülen permission denied, rolün bağlanabildiği ancak veritabanının sahibi olmadığı anlamına gelir. Bu nedenle rol veritabanında tablo oluşturamaz. sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" ile düzeltin ve kurulumu yeniden çalıştırın.
Hizmet hiç başlamıyor ve günlük yapılandırma dosyasını belirtiyor. listmonk olarak çalışan bir işlem, root:root olarak bırakılmış ve modu 600 olan config.toml dosyasını açamaz. stat -c '%U:%G %a' /etc/listmonk/config.toml çıktısı root:listmonk 640 göstermelidir. Dosyanın üzerindeki dizin de root:listmonk 750 olmalıdır.
Panel çalışıyor, ancak posta ulaşmıyor. Bu bir başlatma sorunu değildir. Önce Settings ve SMTP ayarlarını kontrol edin. Ardından yönetim panelindeki kampanyanın kendi günlüğünü inceleyin. Bu günlük, her deneme için posta sunucusunun döndürdüğü hatayı kaydeder.
FAQ
Listmonk kullanmak için kendi mail sunucuma ihtiyacım var mı?
Hayır. Listmonk bir mail sunucusu değildir. E-postalarınızı kabul edip teslim eden bir sunucu için SMTP kimlik bilgilerine ihtiyaç duyar. Bu sunucu, işlemsel bir relay veya kendi yönettiğiniz bir mail sunucusu olabilir. Bu kimlik bilgilerini config.toml içinde değil, yönetim panelindeki Settings ve SMTP bölümlerinde ayarlayın. Mail ayarları veritabanında tutulur. Yeni hesaplarda çoğu VPS sağlayıcısı giden 25 numaralı portu engellediği için, STARTTLS ile 587 numaralı portu veya örtük TLS ile 465 numaralı portu kullanın.
Kampanyamdaki bağlantılar neden localhost:9000 adresini gösteriyor?
Root URL ayarı hâlâ kurulumdaki varsayılan http://localhost:9000 değerindedir. Listmonk, kampanya gönderildiği anda bu değeri abonelikten çıkma bağlantılarına ve medya URL'lerine yazar. Yönetim panelinde Settings bölümünü açın. Root URL değerini gerçek HTTPS adresiniz olarak ayarlayın ve kaydedin. Daha önce teslim edilen iletiler düzeltilemez. Bu nedenle gerçek bir listeye e-posta göndermeden önce kendinize bir test kampanyası gönderin ve bu kampanyadaki abonelikten çıkma bağlantısını tıklayın.
--install komutunu yeniden çalıştırmak abonelerimi siler mi?
Evet. --install ilk kurulum aracıdır ve mevcut Listmonk şemasını siler. --yes ise sizi uyaracak istemi kaldırır. İki kez çalıştırılabilecek herhangi bir betikte --install --idempotent --yes kullanın. Bu seçenek, tablolar zaten mevcutsa hiçbir işlem yapmaz. Yeni bir sürümde şema değişikliklerini uygulamak için servisi durdurun, bir pg_dump alın ve ardından --upgrade komutunu çalıştırın.
Listmonk neden kullanıcı listmonk için password authentication failed mesajını gösteriyor?
/etc/listmonk/config.toml içindeki [db] bloğunda bulunan parola, aynı ada sahip Postgres rolünün parolasıyla eşleşmiyor. Günlük satırı pq: password authentication failed for user "listmonk" şeklindedir. pq, Postgres sürücüsünün sunucu tarafından gönderilen reddetme yanıtını ilettiğini gösterir. Bu da yapılandırma dosyasının bulunduğunu ve okunduğunu belirtir. sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" ile rolün parolasını sıfırlayın. Aynı karakter dizisini yapılandırma dosyasına yazın ve ardından sudo systemctl restart listmonk komutunu çalıştırın.