SSD Nodes Learn 🎉 VPS $5.50/aydan başlayan
Rehberler Matt ConnorYazan Matt Connor

VPS Uzerinde Headless Tarayici Calistirma Rehberi

VPS uzerinde headless Chromium calistirirken karsilasilan dev/shm hatalari, eksik fontlar ve sandbox sorunlarini cozmek icin gereken sistem ayarlari bu rehberde anlatiliyor.

Çalıştırdığınız yapı

VPS üzerinde başsız (headless) bir tarayıcı, bir kişi yerine kodunuz tarafından yönetilen, penceresiz bir Chromium sürümüdür. Sunucu tarafında bu, temsilcinizin yerel bir soket üzerinden iletişim kurduğu uzun ömürlü bir süreç ağacıdır. Kurulumu tek bir komutla tamamlanır. Asıl iş bundan sonra başlar. Tarayıcının makineden alabileceği kaynakları sınırlandırmalı ve kontrol uç noktasını genel internet erişimine kapatmalısınız.

Bu kılavuz, araç seçiminin yapıldığını ve artık işletim aşamasına geçtiğinizi varsayar. Eğer hala tarayıcılar ve veri çekme araçları arasında karşılaştırma yapıyorsanız, kendi sunucunuzda barındırabileceğiniz Firecrawl alternatifleri ile başlayıp buraya geri dönün. Aşağıdaki tüm örneklerde Playwright'ın Chromium sürümü kullanılmaktadır; çünkü Playwright kendi tarayıcı yapısını ve bağımlılık yükleyicisini beraberinde getirir. Bu sayede aynı komutlar hem yalın bir Ubuntu VPS üzerinde hem de bir container içerisinde çalışır. Sürümler Ağustos 2026 itibarıyla günceldir.

Bağımlılıkları tahmin etmeden Chromium kurulumu

npm i -D playwright@1.62.0
npx playwright install --with-deps chromium

--with-deps, Chromium'un ihtiyaç duyduğu paylaşımlı kütüphaneler ve yazı tipleri için apt komutunu çalıştırır ve bu aşamada root yetkisi ister. Tarayıcı derlemesinin kendisi, komutu çalıştıran kullanıcı için ~/.cache/ms-playwright dizinine indirilir. Bir sunucuda bu durum önemlidir; çünkü servis kullanıcısı genellikle giriş yaptığınız kullanıcıdan farklıdır. Sistem paketlerini bir yönetici olarak sudo npx playwright install-deps chromium ile bir kez kurun, ardından hem kurulum komutunda hem de servis biriminde PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers değişkenini ayarlayarak tek bir kopyanın paylaşılmasını sağlayın. Tarayıcısını göremeyen bir servis, aradığı yolu belirten bir hata mesajıyla başlatma sırasında başarısız olur.

Playwright sürümünü sabitleyin. Her sürüm tek bir tarayıcı derlemesine bağlıdır, bu nedenle sabitlenmemiş bir npm update, çalışan bir servisin altındaki tarayıcıyı değiştirebilir. Ağustos 2026 itibarıyla güncel sürüm Playwright 1.62'dir.

İki farklı Chromium derlemesi mevcuttur ve bunlar aynı program değildir. Varsayılan indirme işlemi, yalnızca headless modda çalışan daha küçük bir ikili dosya olan headless shell'dir ve npx playwright install --with-deps --only-shell yalnızca bunu kurar. Tam tarayıcı sürümü ise, Playwright tarayıcı belgelerinde "gerçek Chrome tarayıcısı, dolayısıyla daha özgün, güvenilir ve daha fazla özellik sunan" olarak tanımlanan chromium kanalı ile elde edilir. Toplu veri çekme işlemleri için shell sürümünü kullanın. Bir site farklı davrandığında ve nedenini bulmanız gerektiğinde ise tam tarayıcı sürümünü kullanın.

Headless bir tarayıcı konteyner içinde neden çöker

Docker, her konteynere 64 MB boyutunda bir /dev/shm alanı tahsis eder. Docker belgeleri bu konuda nettir: "Boyutu tamamen atlarsanız, sistem 64m değerini kullanır". Chromium, işlenmiş içeriği süreçleri arasında bu paylaşımlı bellek alanı üzerinden aktarır; bu nedenle ağır bir sayfa bu alanı hızla doldurabilir. Ardından oluşturucu (renderer) süreci sonlanır ve istemciniz, dizüstü bilgisayarınızda sorunsuz çalışan bir sayfa için hedef çöktü hatası verir. Herhangi bir değişiklik yapmadan önce konteynerin içinden boyutu doğrulayın.

df -h /dev/shm

İki gerçek çözüm yolu mevcuttur ve bunlar birbirinin tamamlayıcısı değil, alternatifidir. --ipc=host, konteyneri ana makinenin IPC ad alanına dahil eder; böylece konteyner, normalde RAM'in yarısı kadar olan ana makine /dev/shm alanını kullanır. Playwright Docker kılavuzu, "Chromium bellek yetersizliğinden çökebilir" uyarısıyla bu yöntemi önerir. Bunun bedeli, konteyner ile ana makine arasındaki IPC izolasyonunu kaldırmaktır. --shm-size=1g ise özel ad alanını korur ve sadece mount edilen alanı büyütür.

docker run --rm -it --init --ipc=host --user pwuser mcr.microsoft.com/playwright:v1.62.0-noble /bin/bash

--disable-dev-shm-usage bayrağı, arama sonuçlarında en sık karşılaşacağınız cevaptır ancak farklı bir işlem yapar: ilgili dosyaları /dev/shm içerisinden alıp geçici bir dizine taşır. Eğer /tmp disk üzerinde bulunuyorsa, çökmeyi daha yavaş bir işleme ve disk yazma işlemlerine değişmiş olursunuz. Eğer /tmp bir tmpfs ise, veriler herhangi bir boyut sınırı olmaksızın tekrar RAM'e döner; bu da bir tarayıcının küçük bir VPS'i tüketmesinin bir yoludur. Bunun yerine /dev/shm boyutunu uygun şekilde ayarlayın.

--no-sandbox bayrağının gerçek maliyeti

Chromium, her bir oluşturucuyu (renderer) Linux kullanıcı isim alanları (user namespaces) üzerine inşa edilmiş bir korumalı alanda (sandbox) izole eder. Bu korumalı alan, kötü niyetli bir sayfa ile sunucunuz arasındaki sınırdır. Başlatılamadığında Chromium çalışmayı reddeder ve günlük kayıtlarında şu satır yer alır:

Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted

Genel tavsiye --no-sandbox kullanmaktır. Chromium'un kendi güvenlik belgeleri bu bedel konusunda açıktır: bu bayrak "Chromium'un kritik güvenlik özelliklerini devre dışı bırakır ve açık web'de gezinirken asla kullanılmamalıdır". Bağlantıları takip eden bir aracı, tanımı gereği açık web'de gezinmektedir. Gerçek nedeni bulun.

İki neden neredeyse her durumu açıklar. Tarayıcıyı root olarak çalıştırmak korumalı alanı devre dışı bırakır; çünkü zaten sahip olduğu ayrıcalıkları bırakamaz. Playwright imajının pwuser adında sıradan bir kullanıcı ile gelmesinin nedeni budur. Ubuntu 24.04 ve sonraki sürümlerde AppArmor, ayrıcalıksız kullanıcı isim alanlarını kısıtlar ve tanımlı bir profili olmayan bir yoldaki Chromium ikili dosyası engellenir. Playwright'ın ~/.cache/ms-playwright altındaki indirme dizini tam olarak böyle bir yoldur. Her ikisini de kontrol edin:

id -u
sysctl kernel.apparmor_restrict_unprivileged_userns
sudo dmesg | grep -i userns_create

sysctl üzerinden gelen bir 1 ve apparmor="DENIED" operation="userns_create" içeren bir çekirdek satırı, ikinci nedeni doğrular. Sunucudaki diğer her şey için kısıtlamayı yerinde tutarak, yalnızca ilgili ikili dosyaya /etc/apparmor.d/pw-chromium içinde izin verin:

abi <abi/4.0>,
include <tunables/global>

profile pw-chromium /home/*/.cache/ms-playwright/*/chrome-linux/{chrome,headless_shell} flags=(unconfined) {
  userns,
}

Bunu sudo apparmor_parser -r /etc/apparmor.d/pw-chromium ile yükleyin. Yol, tarayıcı sürümünü içerdiğinden her Playwright güncellemesinde değişir. Yukarıdaki glob ifadeleri bu değişikliğe uyum sağlar. Tam bir yola göre yazılmış bir profil, sessizce eşleşmeyi durdurur ve tarayıcı, ilgisiz görünen bir güncellemeden sonra tekrar başarısız olmaya başlar.

Ekran görüntülerinin boş veya kutucuklarla dolu gelmesinin nedenleri

Boş veya içi boş dikdörtgenlerle dolu bir ekran görüntüsü, genellikle bir işleme hatasından ziyade bir yazı tipi sorunudur. install-deps çalışan bir temel yapı çeker: fonts-liberation, fonts-freefont-ttf, fonts-noto-color-emoji, fonts-unifont, Japonca için fonts-ipafont-gothic, Çince için fonts-wqy-zenhei, Tayca için fonts-tlwg-loma-otf. Bu kümede Noto CJK bulunmadığından, Korece ve diğer bazı alfabeler fontconfig'in bulabildiği herhangi bir yazı tipine geri döner. Tahmin etmek yerine fontconfig'e sorun:

fc-match "sans-serif:lang=ko"
fc-match "sans-serif:lang=ar"
fc-list | wc -l

Önem verdiğiniz bir dil unifont değerine veya gerçek glifleri olmayan bir yedeğe çözümleniyorsa, fonts-noto-core ve fonts-noto-cjk paketlerini kurun ve kontrolü tekrar çalıştırın. Fontconfig sonuçları önbelleğe aldığından, yazı tiplerini yükledikten sonra tarayıcıyı yeniden başlatın. Hiç yazı tipi içermeyen kırpılmış bir görüntü, başlangıçta Fontconfig error: Cannot load default config file günlüğü tutar ve her sayfayı boş işler.

Yerel ayar (locale) ve saat dilimi, yazı tiplerinden bağımsızdır ve yalnızca görünümü değil, sayfanın içeriğini de değiştirir. Bir container genellikle LANG ayarlanmamış ve TZ değeri UTC olarak gelir; bu nedenle siteler İngilizce içerik sunar ve UTC zaman damgaları basar, tarayıcınız ise o ülkedeki bir kullanıcının gördüğünden farklı zamanlar bildirir. Bunları makine bazında değil, tarayıcı bağlamında ayarlayın; böylece tek bir tarayıcı farklı bölgeler için görevleri yerine getirebilir.

const context = await browser.newContext({
  locale: 'en-GB',
  timezoneId: 'Europe/Paris',
});

Sızıntı yapan tarayıcı süreçleri neden sunucuyu swap kullanımına zorlar

"Zombie" ismi iki farklı sorunu tanımlamak için kullanılır. Gerçek bir zombie, üst süreci hiçbir zaman wait() çağrısı yapmamış, tamamlanmış bir süreçtir. Sadece bir PID girdisi tutar ve başka hiçbir şey yapmaz, bu nedenle bellek tüketmez. Tarayıcı bir container içinde PID 1 olarak çalıştığında bu süreçlerle karşılaşırsınız, çünkü PID 1'in varsayılan bir "reaper" (süreç temizleyici) mekanizması yoktur. Docker'ın --init bayrağı, "sinyalleri ileten ve süreçleri temizleyen" küçük bir init süreci çalıştırarak tam olarak bu sorunu çözer. Compose içinde aynı işlev init: true ile sağlanır.

Sunucunuzu swap kullanımına zorlayan sızıntı ise farklıdır: kimsenin kapatmadığı canlı Chromium süreçleri. Bu durum, bir görev newContext() ile close() arasında hata verdiğinde veya kontrol eden betik öldürülüp tarayıcı ağacını yetim bıraktığında meydana gelir. En kötü senaryo, her istek için yeni bir tarayıcı başlatan koddur. Bunları sayın:

pgrep -c -f 'headless_shell|chrome'
ps -eo pid,ppid,rss,etime,comm --sort=-rss | head -20

Bu sayı, görevler arasında boşta kalma değerine geri dönmelidir. Eğer gün boyunca artış gösteriyorsa, çözüm başlatma bayraklarında değil kodunuzdadır: bağlamı bir finally bloğu içinde kapatın, tarayıcıyı SIGTERM üzerinde kapatın ve bir ay boyunca çalıştırmak yerine belirli sayıda görevden sonra tarayıcıyı geri dönüştürün. systemd altında, bir durdurma veya yeniden başlatma işlemi, birimin cgroup'undaki her şeyi öldürür, bu nedenle sudo systemctl restart browser.service güvenilir bir sıfırlama yöntemidir. Bir terminal çoklayıcısı içinde elle başlatılan bir tarayıcının böyle bir garantisi yoktur ve yetim süreçleri oturumdan daha uzun süre yaşar.

Bir tarayıcı bağlamı ne kadar RAM gerektirir

Soruyu hassas bir şekilde sorun, çünkü "bir tarayıcı" tek bir süreçten ibaret değildir. Chromium bir tarayıcı süreci, bir GPU süreci, yardımcı süreçler ve her site için bir render süreci çalıştırır; site izolasyonu ise siteler arası iframe'lere kendi render süreçlerini atar. Bir BrowserContext, aynı ağaç yapısı içinde ayrı bir çerez deposu ve depolama alanıdır, bu nedenle ikinci bir bağlamın maliyeti düşüktür. İkinci bir sayfa ise render süreçlerini başlattığı için maliyetlidir; reklam yoğunluklu bir sayfa ise birden fazla süreç başlatır.

Bu nedenle ölçülmesi gereken değer, kendi iş yükünüz altında tüm ağaç yapısının ulaştığı tepe bellek kullanımıdır. Başka birinin blogundaki bir rakam burada değersizdir, çünkü cevabı açtığınız sayfalar belirler. Ölçümü, kullanacağınız makine üzerinde ve ziyaret edeceğiniz siteleri baz alarak yapın:

sudo systemd-run --unit=browser-probe -p MemoryMax=2G -p MemorySwapMax=0 -p WorkingDirectory=/srv/agent /usr/bin/node worker.js
systemctl status browser-probe

Ubuntu 24.04 üzerinde, bu çıktıda yer alan Memory: satırı, birim için hem mevcut hem de tepe bellek kullanımını raporlar. Çalışan birimi her seferinde tek bir sayfa ile başlatın, tepe değerini not edin, ardından ikinci bir sayfa açarak ikinci sayfanın gerçek maliyetini görmek için işlemi tekrarlayın. Eşzamanlılık hesabı aritmetiktir: toplam RAM miktarını alın, sunucunun geri kalanının ihtiyaç duyduğu miktarı çıkarın, birkaç yüz MB'lık bir pay bırakın ve sonucu ölçülen tepe değerine bölün. Bunun altındaki makineyi boyutlandırmak için bir aracı VPS'nin ne kadar RAM ve CPU'ya ihtiyaç duyduğu bölümüne bakın.

Bu sayıyı iki yerde zorunlu kılın. Kodunuzda sabit bir çalışan havuzu veya semafor kullanın; böylece aracı isteklerindeki ani artışlar tarayıcı başlatmak yerine kuyruğa alınır. İşletim sistemi seviyesinde ise bir cgroup limiti kullanın; böylece kuyruktaki bir hata tüm makineyi çökertemez:

[Service]
MemoryMax=2G
MemorySwapMax=0
TasksMax=512
Restart=always

MemorySwapMax=0 göründüğünden daha önemlidir. Bu ayar olmadan, cgroup limite ulaştığında sayfaları swap alanına iter; bu durumda sunucu ayakta kalır ancak her istek yavaşlar, bu da temiz bir hata durumundan daha zor teşhis edilir. Bu ayar ile çekirdek, cgroup içindeki tarayıcı ağacını sonlandırır, systemd birimi yeniden başlatır ve sshd hayatta kalır. Compose içindeki aynı kontroller mem_limit, shm_size ve init olup, Docker Compose içinde bellek limitlerini ayarlama bölümünde ele alınmıştır.

Tarayıcı uç noktasını genel internete kapatın

Playwright, tarayıcıyı bir sunucu olarak çalıştırabilir ve aracınıza bir WebSocket URL'si sağlayabilir:

const { chromium } = require('playwright');
const server = await chromium.launchServer({ port: 3000 });
console.log(server.wsEndpoint());

Bu uç noktanın herhangi bir oturum açma mekanizması yoktur. Playwright API belgeleri bunu doğrudan belirtir: "wsPath bilgisine sahip herhangi bir işlem veya web sayfası (Playwright içinde çalışanlar dahil), işletim sistemi kullanıcısının kontrolünü ele geçirebilir." Varsayılan ana bilgisayar localhost'dir ve "yalnızca loopback arayüzünden gelen bağlantıları kabul eder"; belgeler, 0.0.0.0 gibi açık bir adres belirtmenin "tarayıcı RPC'sini, dinleme portuna erişebilen her şeye açık hale getireceğini" konusunda uyarır. Chrome'un kendi --remote-debugging-port seçeneği daha da kötüdür. DevTools protokolünün hiçbir kimlik doğrulama mekanizması yoktur ve tamamen loopback arayüzüne bağlı olmasına dayanır.

Gerçekte neyi dış dünyaya açtığınızı kontrol edin; bunu hem VPS üzerinden hem de ikinci bir makineden test edin:

ss -ltnp

0.0.0.0 adresine bağlı bir tarayıcı portunda görünen her şey bir güvenlik açığıdır. Çoğu sağlayıcının kontrol panellerinde, ufw kurallarınızın haberdar olmadığı ayrı bir ağ güvenlik duvarı çalıştırdığını unutmayın. Uç noktaya başka bir makineden erişmek için SSH tüneli veya özel bir VPN kullanın:

ssh -N -L 3000:127.0.0.1:3000 you@your-vps

Buradaki risk, birinin tarayıcı sürenizi çalmasından çok daha büyüktür. Kontrol edebildiğiniz bir tarayıcı, ağınızın içinde yer alan bir istek sahteciliği (request-forgery) makinesidir. O sokete erişen kişi, tarayıcıya http://127.0.0.1:8080 adresini, veritabanı yönetici sayfanızı veya 169.254.169.254 adresindeki bulut meta veri adresini getirtebilir ve ardından yanıtı sayfadan okuyabilir. Güvenlik duvarınız, isteğin VPS'in kendisinden geldiğini görür ve buna izin verir. Kontrol uç noktasını, o sunucuda shell erişimine sahip olmakla eşdeğer tutun.

MCP sunucuları da aynı yapıdadır. npx @playwright/mcp@latest --headless --port 8931, localhost üzerinde HTTP üzerinden hizmet verir ve --host 0.0.0.0, yerel bir aracı genel bir araca dönüştüren bayraktır. Projenin README dosyası, Playwright MCP'nin "bir güvenlik sınırı olmadığını" açıkça belirtir. Portu loopback üzerinde tutun ve aracın ona aynı tünel üzerinden erişmesini sağlayın.

Ajanınızın okuduğu sayfalar güvenilmeyen girdilerdir

Açık web'de gezinen bir ajan, yabancılar tarafından yazılmış metinleri, sizin talimatlarınızı da içeren bir modele besler. Bir sayfa, modele hitap eden ve ona görevi terk etmesini, bir araç çağırmasını veya bir URL'ye veri göndermesini söyleyen metinler içerebilir. Model, her ikisini de metin olarak aldığı için sayfanın sözcükleri ile sizin talimatlarınızı birbirinden ayırt etmenin güvenilir bir yoluna sahip değildir. Kurulumu, kötü niyetli bir sayfanın elinde az malzeme kalacak şekilde tasarlayın.

  • Tarayıcıyı kendi işletim sistemi kullanıcısı altında çalıştırın; ortamında SSH anahtarları veya bulut kimlik bilgileri bulundurmayın.
  • Her görev için yeni bir bağlam kullanın ve --isolated ile Playwright MCP'den yararlanın; böylece bir sitedeki oturum bir sonraki sayfaya aktarılmaz.
  • İşin izin verdiği durumlarda bir kaynak izin listesi (allowlist) tutun. Playwright MCP, --allowed-origins ve --blocked-origins parametrelerini noktalı virgülle ayrılmış listeler olarak kabul eder.
  • E-posta göndermek veya harcama yapmak gibi durumu değiştiren her işlemden önce bir insan onayı adımı zorunlu tutun.

Daha da iyisi, tarayıcının tamamını atılabilir ve yeniden oluşturulabilir bir makinede tutun; bu, kodlama ajanlarını geçici bir VM içinde çalıştırmak ile aynı mantıktır. Ajanın asıl işi ucu açık gezinmek yerine arama yapmaksa, tam teşekküllü bir tarayıcıdan ziyade daha kısıtlı bir araç daha güvenlidir: kendi SearXNG'niz tarafından desteklenen bir arama yeteneği, kötü niyetli sayfayı asla yüklemeden sonuçları döndürür.

FAQ

Chromium, aynı VPS üzerinde doğrudan çalışırken neden Docker içinde çöküyor?

Çünkü container varsayılan olarak 64 MB boyutunda bir /dev/shm ile gelir, ancak ana makinenin çok daha büyük bir alanı vardır. Chromium, işlenmiş içeriği bu paylaşımlı bellek alanı üzerinden aktarır; bu nedenle ağır bir sayfa bu alanı doldurur ve oluşturucu (renderer) süreci sonlanır. Durumu doğrulamak için container içinde df -h /dev/shm komutunu çalıştırın, ardından container'ı ana makinenin paylaşımlı belleğini kullanan --ipc=host parametresiyle veya container'ın kendi belleğini genişleten --shm-size=1g parametresiyle başlatın. --disable-dev-shm-usage kullanımı sorunu yalnızca /tmp dizinine taşır.

VPS üzerinde başka hiçbir şey çalışmıyorsa --no-sandbox güvenli midir?

Hayır. Sandbox, kötü niyetli bir sayfanın makinenin geri kalanına erişmesini engelleyen yapıdır. Chromium dokümantasyonu, bu bayrağın "Chromium'un kritik güvenlik özelliklerini devre dışı bıraktığını ve internette gezinirken asla kullanılmaması gerektiğini" belirtir. Bağlantıları takip eden bir aracı, internette geziniyor demektir. Bunun yerine sorunu kaynağında çözün: tarayıcıyı root kullanıcısı olarak çalıştırmayın ve Ubuntu 24.04 üzerinde, tarayıcı ikili dosya yolu için userns, içeren bir AppArmor profili ekleyin; böylece ayrıcalıksız kullanıcı isim alanlarına (user namespaces) yalnızca o program için izin verilmiş olur.

Küçük bir VPS üzerinde kaç tane tarayıcı çalıştırabilirim?

Bir sayıya güvenmeyin, ölçüm yapın. Chromium her site için bir oluşturucu süreci başlatır, bu nedenle cevap açtığınız sayfalara bağlıdır. MemoryMax ayarlanmış şekilde systemd-run altında bir işçi (worker) süreci çalıştırın, systemctl status içindeki Memory: satırından tepe değerini okuyun, ardından boş RAM miktarınızı bu tepe değerine bölün ve bir miktar pay bırakın. Sonucu kodunuzda bir kuyruk yapısı ve birim dosyasında MemoryMax ile iki kez zorunlu kılın; böylece ani istek artışları makineyi swap'e düşürmek yerine kuyrukta bekler.

Aracım başka bir makineden tarayıcıya bağlanabilir mi?

Evet, ancak portu asla 0.0.0.0 adresine bağlamayın. Playwright sunucu uç noktası ve Chrome DevTools portu, kendilerine ulaşabilen her istemciyi parola sormadan kabul eder. Dinleyiciyi 127.0.0.1 üzerinde tutun ve bağlantıyı bir SSH tüneli veya özel bir VPN üzerinden gerçekleştirin. Sunucuda ss -ltnp ile doğrulama yapın, dışarıdan bir port kontrolü gerçekleştirin ve ayrıca servis sağlayıcınızın ağ güvenlik duvarını da kontrol edin.

Sayfa açıkça yüklendiği halde ekran görüntülerim neden boş?

Eksik yazı tipleri (fonts). Sayfanın betiğini kapsayan bir yazı tipi olmadığında, metinler boş kutular olarak veya hiç görünmeden işlenir; bu yüzden görsel ağırlıklı bir sayfa boş görünebilir. Kazıma yaptığınız her dil için fc-match "sans-serif:lang=ko" komutunu çalıştırın, cevap genel bir yedek (fallback) olduğunda fonts-noto-core ve fonts-noto-cjk paketlerini yükleyin ve fontconfig önbelleğinin yeniden yüklenmesi için tarayıcıyı yeniden başlatın. Hiç yazı tipi içermeyen bir container, başlangıçta Fontconfig error: Cannot load default config file hatasını günlüğe kaydeder.

#headless-browser#playwright#chromium#ai-agents#automation