[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-posts":3},{"data":4,"meta":2143},[5,616,1432,1842],{"id":6,"documentId":7,"title":8,"slug":9,"description":10,"content":11,"date":552,"admin":553,"createdAt":554,"updatedAt":555,"publishedAt":556,"mediumUrl":557,"tags":558,"featured":559,"seoTitle":560,"seoDescription":561,"category":562,"image":563},20,"cg6xc48kudkcc1re7vbmgiiy","Strapi’yi Güncelledim, Deploy Belleğe Takıldı: Hetzner ve Coolify Deneyimim","strapi-guncelleme-nuxt-deploy-bellek-sorunu","Strapi güncellemesinin ardından Nuxt deploy’u bellek hatasıyla durdu. Hetzner sunucumda kaynakları nasıl kontrol ettiğimi ve ek swap sonrası sonucu komutlarla anlattım.",[12,18,22,26,29,33,36,42,45,54,57,61,67,70,78,81,93,96,100,103,107,110,114,117,121,125,128,132,135,139,142,146,149,153,157,160,164,167,171,175,178,182,186,189,193,197,200,212,215,219,223,226,230,233,237,240,244,247,251,255,258,262,265,273,277,280,284,287,295,302,309,316,319,323,326,330,334,337,345,348,352,355,359,362,366,369,373,376,380,383,387,390,399,402,406,410,413,417,421,424,428,431,435,438,442,445,449,452,456,459,468,471,475,478,482,485,510,513,517,520,524,527,531,534,538,541,545,548],{"type":13,"children":14},"paragraph",[15],{"text":16,"type":17},"Portföy sitemin içerik yönetiminde Strapi kullanıyorum. Yeni sürüme geçmek için başladığım güncelleme, bir süre sonra kendimi sunucunun RAM ve swap kullanımını incelerken bulduğum bir sürece dönüştü.","text",{"type":13,"children":19},[20],{"text":21,"type":17},"",{"type":13,"children":23},[24],{"text":25,"type":17},"Yerelde Strapi güncellemesi tamamlanmıştı. Ama Coolify üzerinden yaptığım deploy, Nuxt’un build aşamasında başarısız oldu.",{"type":13,"children":27},[28],{"text":21,"type":17},{"type":13,"children":30},[31],{"text":32,"type":17},"Bu yazıda güncelleme ile deploy sorununu nasıl ayırdığımı, hangi kontrolleri yaptığımı ve ek swap alanından sonra aldığım sonucu anlatıyorum.",{"type":13,"children":34},[35],{"text":21,"type":17},{"type":37,"level":38,"children":39},"heading",2,[40],{"text":41,"type":17},"Strapi güncellemesi yerelde tamamlandı",{"type":13,"children":43},[44],{"text":21,"type":17},{"type":13,"children":46},[47,49,52],{"text":48,"type":17},"İlk adımda Strapi’yi ",{"bold":50,"text":51,"type":17},true,"5.40.0 sürümünden 5.57.0 sürümüne",{"text":53,"type":17}," güncelledim.",{"type":13,"children":55},[56],{"text":21,"type":17},{"type":13,"children":58},[59],{"text":60,"type":17},"Bağımlılık kurulumundan sonra geliştirme ortamını başlattım:",{"type":62,"children":63,"language":66},"code",[64],{"text":65,"type":17},"npm install\nnpm run develop","plaintext",{"type":13,"children":68},[69],{"text":21,"type":17},{"type":13,"children":71},[72,74,76],{"text":73,"type":17},"Bu komutlar, sürüm güncellemesi için gerekli paket değişikliklerinin ardından çalıştırdığım kurulum ve kontrol adımlarıydı. Tek başına `",{"code":50,"text":75,"type":17},"npm install",{"text":77,"type":17},"`, belirli bir Strapi sürümüne geçiş komutu değildir.",{"type":13,"children":79},[80],{"text":21,"type":17},{"type":13,"children":82},[83,85,87,89,91],{"text":84,"type":17},"Terminalde Strapi’nin 5.57.0 sürümüyle başladığını gördüm. Yerel ortamım ",{"code":50,"text":86,"type":17},"Node.js 22.18.0",{"text":88,"type":17}," ve ",{"code":50,"text":90,"type":17},"SQLite",{"text":92,"type":17}," kullanıyordu. Migration işlemleri tamamlandı ve uygulama başarıyla açıldı.",{"type":13,"children":94},[95],{"text":21,"type":17},{"type":13,"children":97},[98],{"text":99,"type":17},"Yereldeki bu sonuç, güncellemenin ilk doğrulamasıydı. Sunucudaki deploy sürecinin de ayrıca tamamlanması gerekiyordu.",{"type":13,"children":101},[102],{"text":21,"type":17},{"type":37,"level":38,"children":104},[105],{"text":106,"type":17},"Coolify’de deploy neden başarısız oldu?",{"type":13,"children":108},[109],{"text":21,"type":17},{"type":13,"children":111},[112],{"text":113,"type":17},"Deploy sırasında Nuxt’un client ve SSR build adımları, ardından prerender işlemleri tamamlandı. Ancak Nitro build aşamasında işlem durdu.",{"type":13,"children":115},[116],{"text":21,"type":17},{"type":13,"children":118},[119],{"text":120,"type":17},"Loglarda şu ifadeler vardı:",{"type":62,"children":122,"language":66},[123],{"text":124,"type":17},"Killed\ncannot allocate memory\nResourceExhausted",{"type":13,"children":126},[127],{"text":21,"type":17},{"type":13,"children":129},[130],{"text":131,"type":17},"Bu mesajlar, incelemeyi sunucunun bellek durumuna yönlendirdi.",{"type":13,"children":133},[134],{"text":21,"type":17},{"type":13,"children":136},[137],{"text":138,"type":17},"Burada önemli bir ayrım vardı: Hata veren işlem Strapi’nin yerel güncellemesi değil, Nuxt uygulamasının sunucuda derlenmesiydi.",{"type":13,"children":140},[141],{"text":21,"type":17},{"type":13,"children":143},[144],{"bold":50,"text":145,"type":17},"Önce sunucu kaynaklarını kontrol ettim",{"type":13,"children":147},[148],{"text":21,"type":17},{"type":13,"children":150},[151],{"text":152,"type":17},"Aşağıdaki komutları SSH ile bağlandığım sunucuda çalıştırdım:",{"type":62,"children":154,"language":66},[155],{"text":156,"type":17},"free -h",{"type":13,"children":158},[159],{"text":21,"type":17},{"type":13,"children":161},[162],{"text":163,"type":17},"Bu komutla RAM ve swap kullanımına baktım.",{"type":13,"children":165},[166],{"text":21,"type":17},{"type":13,"children":168},[169],{"text":170,"type":17},"Ardından etkin swap alanlarını listeledim:",{"type":62,"children":172,"language":66},[173],{"text":174,"type":17},"swapon --show",{"type":13,"children":176},[177],{"text":21,"type":17},{"type":13,"children":179},[180],{"text":181,"type":17},"Diskte ne kadar boş alan kaldığını kontrol ettim:",{"type":62,"children":183,"language":66},[184],{"text":185,"type":17},"df -h \u002F",{"type":13,"children":187},[188],{"text":21,"type":17},{"type":13,"children":190},[191],{"text":192,"type":17},"Container’ların o andaki kaynak kullanımını görmek için de şu komutu kullandım:",{"type":62,"children":194,"language":66},[195],{"text":196,"type":17},"docker stats --no-stream",{"type":13,"children":198},[199],{"text":21,"type":17},{"type":13,"children":201},[202,204,206,208,210],{"text":203,"type":17},"Kontrol anında sunucuda yaklaşık ",{"bold":50,"text":205,"type":17},"1,9 GiB RAM",{"text":207,"type":17}," vardı. Kullanılabilir bellek yaklaşık ",{"bold":50,"text":209,"type":17},"501 MiB",{"text":211,"type":17}," görünüyordu.",{"type":13,"children":213},[214],{"text":21,"type":17},{"type":13,"children":216},[217],{"text":218,"type":17},"Zaten mevcut bir swap dosyası bulunuyordu:",{"type":62,"children":220,"language":66},[221],{"text":222,"type":17},"\u002Fswapfile — 2 GiB",{"type":13,"children":224},[225],{"text":21,"type":17},{"type":13,"children":227},[228],{"text":229,"type":17},"Bunun yaklaşık 833 MiB’ı kullanılıyordu. BuildKit container’ı ise kontrol anında yaklaşık 463 MiB bellek tüketiyordu.",{"type":13,"children":231},[232],{"text":21,"type":17},{"type":13,"children":234},[235],{"text":236,"type":17},"Bu değerler build’in en yüksek bellek kullanımını ölçen bir kayıt değildi; o andaki durumu gösteriyordu. Ancak hata mesajlarıyla birlikte değerlendirince bellek baskısı üzerinde durmak anlamlıydı.",{"type":13,"children":238},[239],{"text":21,"type":17},{"type":37,"level":38,"children":241},[242],{"bold":50,"text":243,"type":17},"Mevcut swap alanına 2 GiB ekledim",{"type":13,"children":245},[246],{"text":21,"type":17},{"type":13,"children":248},[249],{"text":250,"type":17},"Mevcut swap dosyasını değiştirmek yerine ayrı bir dosya oluşturdum:",{"type":62,"children":252,"language":66},[253],{"text":254,"type":17},"\u002Fswapfile-extra",{"type":13,"children":256},[257],{"text":21,"type":17},{"type":13,"children":259},[260],{"text":261,"type":17},"Bu işlem diskte alan ayırdığı için önce boş disk alanını kontrol etmiştim.",{"type":13,"children":263},[264],{"text":21,"type":17},{"type":13,"children":266},[267,269,271],{"text":268,"type":17},"Aşağıdaki komutları sunucuda ",{"code":50,"text":270,"type":17},"root",{"text":272,"type":17}," kullanıcısıyla çalıştırdım. Bunlar yeni bir swap dosyası oluşturmak içindir; aynı yolda mevcut bir dosya varsa doğrudan tekrar çalıştırılmamalıdır.",{"type":62,"children":274,"language":66},[275],{"text":276,"type":17},"fallocate -l 2G \u002Fswapfile-extra\nchmod 600 \u002Fswapfile-extra\nmkswap \u002Fswapfile-extra\nswapon \u002Fswapfile-extra",{"type":13,"children":278},[279],{"text":21,"type":17},{"type":13,"children":281},[282],{"text":283,"type":17},"Komutların görevleri şöyleydi:",{"type":13,"children":285},[286],{"text":21,"type":17},{"type":13,"children":288},[289,291,293],{"text":290,"type":17},"- ",{"code":50,"text":292,"type":17},"fallocate",{"text":294,"type":17},": Dosya için 2 GiB disk alanı ayırdı.",{"type":13,"children":296},[297,298,300],{"text":290,"type":17},{"code":50,"text":299,"type":17},"chmod 600",{"text":301,"type":17},": Dosyaya erişimi sahibiyle sınırladı.",{"type":13,"children":303},[304,305,307],{"text":290,"type":17},{"code":50,"text":306,"type":17},"mkswap",{"text":308,"type":17},": Dosyayı swap alanı olarak hazırladı.",{"type":13,"children":310},[311,312,314],{"text":290,"type":17},{"code":50,"text":313,"type":17},"swapon",{"text":315,"type":17},": Yeni swap alanını etkinleştirdi.",{"type":13,"children":317},[318],{"text":21,"type":17},{"type":13,"children":320},[321],{"text":322,"type":17},"Swap dosyası oluşturma yöntemi dosya sistemine göre değişebilir. Buradaki yöntem benim sunucumda çalıştı.",{"type":13,"children":324},[325],{"text":21,"type":17},{"type":13,"children":327},[328],{"text":329,"type":17},"Sonrasında tekrar kontrol ettim:",{"type":62,"children":331,"language":66},[332],{"text":333,"type":17},"swapon --show\nfree -h",{"type":13,"children":335},[336],{"text":21,"type":17},{"type":13,"children":338},[339,341,343],{"text":340,"type":17},"Mevcut 2 GiB swap’a ek olarak yeni 2 GiB alan da görünüyordu. Toplam swap kapasitesi ",{"bold":50,"text":342,"type":17},"4 GiB ",{"text":344,"type":17},"olmuştu.",{"type":13,"children":346},[347],{"text":21,"type":17},{"type":37,"level":38,"children":349},[350],{"text":351,"type":17},"Deploy’u yeniden denedim",{"type":13,"children":353},[354],{"text":21,"type":17},{"type":13,"children":356},[357],{"text":358,"type":17},"Ek swap alanını etkinleştirdikten sonra Coolify üzerinden tek bir deploy başlattım.",{"type":13,"children":360},[361],{"text":21,"type":17},{"type":13,"children":363},[364],{"text":365,"type":17},"Bu denemede deploy başarıyla tamamlandı.",{"type":13,"children":367},[368],{"text":21,"type":17},{"type":13,"children":370},[371],{"text":372,"type":17},"Build yapılandırmasını değiştirmeden ve çalışan container’ları durdurmadan aldığım bu sonuç, ek swap alanının bu denemede bellek baskısını karşılamaya yardımcı olduğuyla uyumluydu.",{"type":13,"children":374},[375],{"text":21,"type":17},{"type":13,"children":377},[378],{"text":379,"type":17},"Yine de yalnızca bu sonuçtan hareketle, gelecekte bütün build işlemlerinin sorunsuz tamamlanacağını söylemek mümkün değil.",{"type":13,"children":381},[382],{"text":21,"type":17},{"type":37,"level":38,"children":384},[385],{"text":386,"type":17},"Swap ayarını kalıcı hale getirdim",{"type":13,"children":388},[389],{"text":21,"type":17},{"type":13,"children":391},[392,393,395,397],{"code":50,"text":313,"type":17},{"text":394,"type":17}," ile etkinleştirmek, tek başına swap dosyasının yeniden başlatma sonrasında otomatik açılmasını sağlamıyor. Bunun için ",{"code":50,"text":396,"type":17},"\u002Fetc\u002Ffstab",{"text":398,"type":17}," dosyasına kayıt ekledim.",{"type":13,"children":400},[401],{"text":21,"type":17},{"type":13,"children":403},[404],{"text":405,"type":17},"Aynı kaydı tekrar eklememek için önce mevcut olup olmadığını kontrol ettim:",{"type":62,"children":407,"language":66},[408],{"text":409,"type":17},"grep -qE '^\u002Fswapfile-extra[[:space:]]' \u002Fetc\u002Ffstab || \\\n  echo '\u002Fswapfile-extra none swap sw 0 0' >> \u002Fetc\u002Ffstab",{"type":13,"children":411},[412],{"text":21,"type":17},{"type":13,"children":414},[415],{"text":416,"type":17},"Ardından etkin swap alanlarını ve disk durumunu tekrar kontrol ettim:",{"type":62,"children":418,"language":66},[419],{"text":420,"type":17},"swapon --show\ndf -h \u002F",{"type":13,"children":422},[423],{"text":21,"type":17},{"type":13,"children":425},[426],{"text":427,"type":17},"Bu aşamada iki swap dosyası da etkin görünüyordu. Yeniden başlatma için gerekli kayıt da eklenmişti; bu çalışma sırasında ayrıca reboot testi yapmadım.",{"type":13,"children":429},[430],{"text":21,"type":17},{"type":37,"level":38,"children":432},[433],{"text":434,"type":17},"Swap eklemek uzun vadede yeterli mi?",{"type":13,"children":436},[437],{"text":21,"type":17},{"type":13,"children":439},[440],{"text":441,"type":17},"Bu işlemden sonra deploy tamamlandı, ama fiziksel RAM miktarı değişmedi.",{"type":13,"children":443},[444],{"text":21,"type":17},{"type":13,"children":446},[447],{"text":448,"type":17},"Swap, diski bellek yönetiminde kullanır. Disk erişimi RAM’den daha yavaş olduğu için yoğun swap kullanımı performansı etkileyebilir. Bu nedenle ek swap alanını, sunucunun kapasitesini değerlendirmenin yerine koymamak gerekiyor.",{"type":13,"children":450},[451],{"text":21,"type":17},{"type":13,"children":453},[454],{"text":455,"type":17},"Benim için burada iki farklı ihtiyacı ayırmak önemliydi:",{"type":13,"children":457},[458],{"text":21,"type":17},{"type":13,"children":460},[461,463,464,466],{"bold":50,"text":462,"type":17},"Uygulamanın çalışırken kullandığı kaynaklar",{"text":88,"type":17},{"bold":50,"text":465,"type":17},"build sırasında ihtiyaç duyduğu kaynaklar",{"text":467,"type":17}," aynı olmayabiliyor.",{"type":13,"children":469},[470],{"text":21,"type":17},{"type":13,"children":472},[473],{"text":474,"type":17},"Yeni projeler eklendiğinde yalnızca uygulamaların normal kullanımına değil, deploy sırasında oluşan yükün de sunucuya sığıp sığmadığına bakmam gerekecek.",{"type":13,"children":476},[477],{"text":21,"type":17},{"type":13,"children":479},[480],{"text":481,"type":17},"Bu yüzden ileride takip edeceğim noktalar:",{"type":13,"children":483},[484],{"text":21,"type":17},{"type":486,"format":487,"children":488},"list","unordered",[489,494,498,502,506],{"type":490,"children":491},"list-item",[492],{"text":493,"type":17},"Normal kullanımda RAM ve swap durumu.",{"type":490,"children":495},[496],{"text":497,"type":17},"Build sırasında kaynak kullanımı.",{"type":490,"children":499},[500],{"text":501,"type":17},"Aynı anda çalışan deploy sayısı.",{"type":490,"children":503},[504],{"text":505,"type":17},"Diskte kalan alan.",{"type":490,"children":507},[508],{"text":509,"type":17},"Proje sayısı arttıkça sunucu kapasitesi.",{"type":13,"children":511},[512],{"text":21,"type":17},{"type":13,"children":514},[515],{"text":516,"type":17},"Build’i ayrı bir ortamda yapmak veya sunucu kaynaklarını artırmak da ihtiyaç büyüdüğünde değerlendirebileceğim seçenekler.",{"type":13,"children":518},[519],{"text":21,"type":17},{"type":37,"level":38,"children":521},[522],{"text":523,"type":17},"Bu süreçten ne öğrendim?",{"type":13,"children":525},[526],{"text":21,"type":17},{"type":13,"children":528},[529],{"text":530,"type":17},"Başlangıçta yaptığım iş bir Strapi güncellemesiydi. Ancak deploy sırasında karşıma çıkan sorun, Nuxt’un build işlemi ve sunucu kaynaklarıyla ilgiliydi.",{"type":13,"children":532},[533],{"text":21,"type":17},{"type":13,"children":535},[536],{"text":537,"type":17},"Bu deneyim, aynı gün içinde yaşanan iki sorunun otomatik olarak aynı nedenden kaynaklandığını varsaymamam gerektiğini hatırlattı.",{"type":13,"children":539},[540],{"text":21,"type":17},{"type":13,"children":542},[543],{"text":544,"type":17},"Önce hatanın hangi aşamada çıktığını belirledim. Ardından RAM, swap, disk ve container kullanımını kontrol ettim. Mevcut swap alanına 2 GiB ekledikten sonra deploy’u yeniden çalıştırdım ve bu deneme başarıyla tamamlandı.",{"type":13,"children":546},[547],{"text":21,"type":17},{"type":13,"children":549},[550],{"bold":50,"text":551,"type":17},"Benim için asıl kazanım, deploy hatasını yalnızca yeniden denemek yerine, sunucunun hangi noktada zorlandığını incelemek oldu.","2026-10-10","Aleyna","2026-10-09T17:30:45.641Z","2026-10-09T17:30:56.831Z","2026-10-09T17:30:57.132Z","https:\u002F\u002Fmedium.com\u002F@aleynaertin1\u002Fstrapiyi-g%C3%BCncelledim-deploy-belle%C4%9Fe-tak%C4%B1ld%C4%B1-hetzner-ve-coolify-deneyimim-87409c6a2517","Strapi, Nuxt, Nitro, Hetzner, Coolify, Docker, Linux, Swap",false,"Nuxt Deploy Bellek Hatası: Hetzner’de Swap Deneyimim","Coolify’de Nuxt build sırasında karşılaştığım bellek hatasını, RAM ve disk kontrollerini ve Hetzner sunucuma ek swap alanı oluşturma sürecini anlattım.","Yazılım",{"id":564,"documentId":565,"name":566,"alternativeText":567,"caption":567,"focalPoint":567,"width":568,"height":569,"formats":570,"hash":610,"ext":572,"mime":575,"size":611,"url":612,"previewUrl":567,"provider":613,"provider_metadata":614,"createdAt":615,"updatedAt":615,"publishedAt":615},8,"mzoba1qg5awj5ecbywyy163i","strapi-guncelleme-nuxt-deploy-bellek-sorunu.webp",null,1672,941,{"large":571,"small":583,"medium":592,"thumbnail":601},{"ext":572,"url":573,"hash":574,"mime":575,"name":576,"path":567,"size":577,"width":578,"height":579,"sizeInBytes":580,"provider_metadata":581},".webp","https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791566510\u002Flarge_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04.webp","large_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04","image\u002Fwebp","large_strapi-guncelleme-nuxt-deploy-bellek-sorunu.webp",42.22,1000,563,42222,{"public_id":574,"resource_type":582},"image",{"ext":572,"url":584,"hash":585,"mime":575,"name":586,"path":567,"size":587,"width":588,"height":589,"sizeInBytes":590,"provider_metadata":591},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791566510\u002Fsmall_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04.webp","small_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04","small_strapi-guncelleme-nuxt-deploy-bellek-sorunu.webp",15.17,500,281,15174,{"public_id":585,"resource_type":582},{"ext":572,"url":593,"hash":594,"mime":575,"name":595,"path":567,"size":596,"width":597,"height":598,"sizeInBytes":599,"provider_metadata":600},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791566510\u002Fmedium_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04.webp","medium_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04","medium_strapi-guncelleme-nuxt-deploy-bellek-sorunu.webp",28.72,750,422,28722,{"public_id":594,"resource_type":582},{"ext":572,"url":602,"hash":603,"mime":575,"name":604,"path":567,"size":605,"width":606,"height":607,"sizeInBytes":608,"provider_metadata":609},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791566509\u002Fthumbnail_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04.webp","thumbnail_strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04","thumbnail_strapi-guncelleme-nuxt-deploy-bellek-sorunu.webp",4.97,245,138,4968,{"public_id":603,"resource_type":582},"strapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04",87.67,"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791566509\u002Fstrapi_guncelleme_nuxt_deploy_bellek_sorunu_a587cc6c04.webp","cloudinary",{"public_id":610,"resource_type":582},"2026-10-09T17:21:51.221Z",{"id":617,"documentId":618,"title":619,"slug":620,"description":621,"content":622,"date":1385,"admin":553,"createdAt":1386,"updatedAt":1387,"publishedAt":1388,"mediumUrl":1389,"tags":1390,"featured":50,"seoTitle":1391,"seoDescription":1392,"category":562,"image":1393},18,"d1dagpnt4p3r58sjwmpflme8","Yedek Almak Yetmez: Coolify ve R2 Yedeklerimi Geri Yükleyerek Test Ettim","coolify-r2-yedek-geri-yukleme-testi","Coolify ve Cloudflare R2’deki Directus yedeklerimi ayrı bir ortamda geri yükledim. PostgreSQL, dosya bütünlüğü ve panel kontrollerini kullandığım komutlarla anlattım.",[623,627,630,634,637,641,644,648,651,655,658,662,665,669,672,676,680,683,687,690,694,697,701,704,708,711,715,718,722,725,729,732,736,739,743,746,750,753,757,760,764,767,771,774,778,781,785,788,792,795,799,802,806,809,813,816,828,831,835,838,842,845,849,852,856,859,863,866,870,873,877,880,884,887,891,894,898,901,909,912,916,919,923,926,930,933,937,940,944,947,951,954,958,961,965,968,972,976,979,983,986,990,993,997,1000,1004,1007,1011,1014,1018,1021,1025,1028,1032,1035,1039,1042,1046,1049,1053,1056,1060,1064,1067,1075,1078,1082,1085,1089,1092,1100,1103,1107,1110,1114,1117,1125,1128,1132,1135,1139,1142,1146,1149,1153,1156,1160,1163,1167,1170,1174,1177,1181,1184,1188,1191,1199,1202,1206,1209,1213,1216,1220,1223,1227,1230,1234,1237,1241,1244,1248,1251,1255,1258,1262,1265,1269,1272,1280,1283,1287,1290,1294,1297,1301,1304,1308,1311,1315,1318,1322,1325,1329,1332,1350,1353,1357,1360,1364,1367,1371,1374,1378,1381],{"type":13,"children":624},[625],{"text":626,"type":17},"Web sitemi Vercel’den Hetzner’e taşıdıktan sonra, sunucu tarafında ilgilenmem gereken işler de arttı. Bunlardan biri yedeklemeydi.",{"type":13,"children":628},[629],{"text":21,"type":17},{"type":13,"children":631},[632],{"text":633,"type":17},"Coolify üzerinden yedekleri ayarladım, Cloudflare R2’ye gönderildiklerini gördüm. İlk bakışta her şey tamam gibi görünüyordu. Ama aklımda bir soru vardı:",{"type":13,"children":635},[636],{"text":21,"type":17},{"type":13,"children":638},[639],{"bold":50,"text":640,"type":17},"Bir sorun yaşarsam bu yedeklerle gerçekten geri dönebilecek miyim?",{"type":13,"children":642},[643],{"text":21,"type":17},{"type":13,"children":645},[646],{"text":647,"type":17},"Bunu öğrenmek için bir sorun çıkmasını beklemek yerine, yedekleri ayrı bir test ortamında geri yükledim. Bu yazıda hem süreci hem de kontrollerde kullandığım komutları paylaşıyorum.",{"type":13,"children":649},[650],{"text":21,"type":17},{"type":37,"level":38,"children":652},[653],{"text":654,"type":17},"Veritabanı yedeği tek başına yeterli mi?",{"type":13,"children":656},[657],{"text":21,"type":17},{"type":13,"children":659},[660],{"text":661,"type":17},"Studioae’de içerik yönetimi için Directus, veritabanı olarak PostgreSQL kullanıyorum. Yazılar ve içerik kayıtları veritabanında, yüklediğim görseller ise ayrı bir Docker volume’ünde tutuluyor.",{"type":13,"children":663},[664],{"text":21,"type":17},{"type":13,"children":666},[667],{"text":668,"type":17},"Bu yüzden iki parçayı birlikte kontrol etmem gerekiyordu:",{"type":13,"children":670},[671],{"text":21,"type":17},{"type":13,"children":673},[674],{"bold":50,"text":675,"type":17},"- PostgreSQL veritabanı yedeği.",{"type":13,"children":677},[678],{"bold":50,"text":679,"type":17},"- Directus’a yüklenen dosyaların yedeği.",{"type":13,"children":681},[682],{"text":21,"type":17},{"type":13,"children":684},[685],{"text":686,"type":17},"Veritabanında bir görselin kaydı bulunması, görsel dosyasının da yedeklendiği anlamına gelmiyor.",{"type":13,"children":688},[689],{"text":21,"type":17},{"type":13,"children":691},[692],{"text":693,"type":17},"Aşağıdaki komutlarda gerçek sunucu adresi ve kaynak adları yerine örnek değerler kullandım. Tablo adları ve dosya yolları kendi kurulumunuza göre değişebilir.",{"type":13,"children":695},[696],{"text":21,"type":17},{"type":13,"children":698},[699],{"bold":50,"text":700,"type":17},"SSH tüneli ve Mac’teki hash kontrolü dışındaki komutları sunucuda çalıştırdım.",{"type":13,"children":702},[703],{"text":21,"type":17},{"type":37,"level":38,"children":705},[706],{"text":707,"type":17},"1. Test için kaynakları belirledim",{"type":13,"children":709},[710],{"text":21,"type":17},{"type":13,"children":712},[713],{"text":714,"type":17},"Önce çalışan container’ları ve Directus’un bağlı olduğu volume’leri listeledim:",{"type":13,"children":716},[717],{"text":21,"type":17},{"type":62,"children":719,"language":66},[720],{"text":721,"type":17},"docker ps --format 'table {{.Names}}\\t{{.Image}}'\n\ndocker inspect DIRECTUS_CONTAINER_ADI \\\n  --format '{{range .Mounts}}{{println .Source \"->\" .Destination}}{{end}}'",{"type":13,"children":723},[724],{"text":21,"type":17},{"type":13,"children":726},[727],{"text":728,"type":17},"Ardından aynı terminal oturumunda kullanacağım değişkenleri tanımladım:",{"type":13,"children":730},[731],{"text":21,"type":17},{"type":62,"children":733,"language":66},[734],{"text":735,"type":17},"PG_CONTAINER=\"POSTGRES_CONTAINER_ADI\"\nDIRECTUS_CONTAINER=\"DIRECTUS_CONTAINER_ADI\"\n\nDB_USER=\"directus\"\nTEST_DB=\"directus_restore_test_20261009\"\n\nDB_BACKUP=\"\u002FYEDEK_KLASORU\u002Fveritabani.dmp\"\nUPLOADS_BACKUP=\"\u002FYEDEK_KLASORU\u002Fuploads.tar.gz\"\n\nTEST_DIR=\"\u002Froot\u002Fstudioae-restore-test-20261009\"\nTEST_CONTAINER=\"studioae-directus-restore-test\"\nDOCKER_NETWORK=\"coolify\"",{"type":13,"children":737},[738],{"text":21,"type":17},{"type":13,"children":740},[741],{"text":742,"type":17},"Buradaki büyük harfli örnek değerlerin kendi ortamınızdaki karşılıklarıyla değiştirilmesi gerekiyor. Test veritabanının adını da canlı veritabanından açıkça farklı seçtim.",{"type":13,"children":744},[745],{"text":21,"type":17},{"type":37,"level":38,"children":747},[748],{"text":749,"type":17},"2. Veritabanı yedeğinin içeriğine baktım",{"type":13,"children":751},[752],{"text":21,"type":17},{"type":13,"children":754},[755],{"text":756,"type":17},"Elimdeki yedek, PostgreSQL’in custom arşiv biçimindeydi. İçeriğini `pg_restore` ile listeledim:",{"type":13,"children":758},[759],{"text":21,"type":17},{"type":62,"children":761,"language":66},[762],{"text":763,"type":17},"docker exec -i \"$PG_CONTAINER\" pg_restore --list \\\n  \u003C \"$DB_BACKUP\" | head -n 25",{"type":13,"children":765},[766],{"text":21,"type":17},{"type":13,"children":768},[769],{"text":770,"type":17},"Çıktıda yedeğin oluşturulma zamanı, PostgreSQL sürümü ve tablo kayıtları görünüyordu.",{"type":13,"children":772},[773],{"text":21,"type":17},{"type":13,"children":775},[776],{"text":777,"type":17},"Bu kontrol arşivin okunabildiğini gösterdi. Tam olarak geri yüklenebildiğini anlamak için bir sonraki adıma geçtim.",{"type":13,"children":779},[780],{"text":21,"type":17},{"type":37,"level":38,"children":782},[783],{"text":784,"type":17},"3. Ayrı bir test veritabanına geri yükledim",{"type":13,"children":786},[787],{"text":21,"type":17},{"type":13,"children":789},[790],{"text":791,"type":17},"Önce boş bir test veritabanı oluşturdum:",{"type":13,"children":793},[794],{"text":21,"type":17},{"type":62,"children":796,"language":66},[797],{"text":798,"type":17},"docker exec \"$PG_CONTAINER\" \\\n  createdb -U \"$DB_USER\" -T template0 \"$TEST_DB\"",{"type":13,"children":800},[801],{"text":21,"type":17},{"type":13,"children":803},[804],{"text":805,"type":17},"Sonra yedeği bu veritabanına geri yükledim:",{"type":13,"children":807},[808],{"text":21,"type":17},{"type":62,"children":810,"language":66},[811],{"text":812,"type":17},"docker exec -i \"$PG_CONTAINER\" \\\n  pg_restore \\\n    -U \"$DB_USER\" \\\n    -d \"$TEST_DB\" \\\n    --no-owner \\\n    --no-privileges \\\n    --exit-on-error \\\n    --single-transaction \\\n  \u003C \"$DB_BACKUP\" &&\necho \"Geri yükleme başarılı\"",{"type":13,"children":814},[815],{"text":21,"type":17},{"type":13,"children":817},[818,820,822,824,826],{"text":819,"type":17},"Bu testte sahiplik ve yetki kayıtlarını aktarmadım. `",{"code":50,"text":821,"type":17},"--exit-on-error",{"text":823,"type":17},"` ile hata durumunda işlemin durmasını, `",{"code":50,"text":825,"type":17},"--single-transaction",{"text":827,"type":17},"` ile geri yüklemenin tek işlem olarak yürütülmesini sağladım.",{"type":13,"children":829},[830],{"text":21,"type":17},{"type":13,"children":832},[833],{"text":834,"type":17},"Terminalde beklediğim mesajı gördüm:",{"type":13,"children":836},[837],{"text":21,"type":17},{"type":62,"children":839,"language":66},[840],{"text":841,"type":17},"Geri yükleme başarılı",{"type":13,"children":843},[844],{"text":21,"type":17},{"type":37,"level":38,"children":846},[847],{"text":848,"type":17},"4. İçerik kayıtlarını kontrol ettim",{"type":13,"children":850},[851],{"text":21,"type":17},{"type":13,"children":853},[854],{"text":855,"type":17},"Geri yüklenen veritabanındaki birkaç tablonun kayıt sayısına baktım:",{"type":13,"children":857},[858],{"text":21,"type":17},{"type":62,"children":860,"language":66},[861],{"text":862,"type":17},"docker exec \"$PG_CONTAINER\" \\\n  psql -U \"$DB_USER\" -d \"$TEST_DB\" -c \"\n    SELECT 'About' AS tablo, count(*) AS kayit\n    FROM public.\\\"About\\\"\n    UNION ALL\n    SELECT 'Blog', count(*) FROM public.\\\"Blog\\\"\n    UNION ALL\n    SELECT 'Blog_Category', count(*)\n    FROM public.\\\"Blog_Category\\\"\n    UNION ALL\n    SELECT 'directus_files', count(*)\n    FROM public.directus_files;\n  \"",{"type":13,"children":864},[865],{"text":21,"type":17},{"type":13,"children":867},[868],{"text":869,"type":17},"Sonuçta bir About kaydı, üç blog yazısı, iki blog kategorisi ve 26 dosya kaydı vardı.",{"type":13,"children":871},[872],{"text":21,"type":17},{"type":13,"children":874},[875],{"text":876,"type":17},"Bu sayılar, yedekten gelen kayıtları gösteriyordu. Uygulamada doğru görüntülenip görüntülenmediklerini daha sonra panelden kontrol ettim.",{"type":13,"children":878},[879],{"text":21,"type":17},{"type":37,"level":38,"children":881},[882],{"text":883,"type":17},"5. Dosya yedeğini açıp veritabanıyla eşleştirdim",{"type":13,"children":885},[886],{"text":21,"type":17},{"type":13,"children":888},[889],{"text":890,"type":17},"Önce dosyaların hangi depolamada tutulduğuna baktım:",{"type":13,"children":892},[893],{"text":21,"type":17},{"type":62,"children":895,"language":66},[896],{"text":897,"type":17},"docker exec \"$PG_CONTAINER\" \\\n  psql -U \"$DB_USER\" -d \"$TEST_DB\" -c \"\n    SELECT storage, count(*) AS dosya_sayisi\n    FROM public.directus_files\n    GROUP BY storage;\n  \"",{"type":13,"children":899},[900],{"text":21,"type":17},{"type":13,"children":902},[903,905,907],{"text":904,"type":17},"26 dosyanın tamamı `",{"code":50,"text":906,"type":17},"local",{"text":908,"type":17},"` depolama kullanıyordu.",{"type":13,"children":910},[911],{"text":21,"type":17},{"type":13,"children":913},[914],{"text":915,"type":17},"Arşivin gzip bütünlüğünü kontrol edip içeriğine baktım:",{"type":13,"children":917},[918],{"text":21,"type":17},{"type":62,"children":920,"language":66},[921],{"text":922,"type":17},"gzip -t \"$UPLOADS_BACKUP\" &&\necho \"Arşiv sağlam\"\n\ntar -tzf \"$UPLOADS_BACKUP\" | head -n 15",{"type":13,"children":924},[925],{"text":21,"type":17},{"type":13,"children":927},[928],{"text":929,"type":17},"Sonra kendi oluşturduğum yedek arşivini ayrı bir test klasörüne açtım:",{"type":13,"children":931},[932],{"text":21,"type":17},{"type":62,"children":934,"language":66},[935],{"text":936,"type":17},"mkdir -p \"$TEST_DIR\u002Fuploads\"\n\ntar -xzf \"$UPLOADS_BACKUP\" \\\n  -C \"$TEST_DIR\u002Fuploads\" \\\n  --no-same-owner",{"type":13,"children":938},[939],{"text":21,"type":17},{"type":13,"children":941},[942],{"text":943,"type":17},"Veritabanındaki fiziksel dosya adlarını bir listeye aktardım:",{"type":13,"children":945},[946],{"text":21,"type":17},{"type":62,"children":948,"language":66},[949],{"text":950,"type":17},"docker exec \"$PG_CONTAINER\" \\\n  psql -U \"$DB_USER\" -d \"$TEST_DB\" -At -c \"\n    SELECT filename_disk\n    FROM public.directus_files\n    WHERE storage = 'local';\n  \" > \"$TEST_DIR\u002Ffiles.txt\"",{"type":13,"children":952},[953],{"text":21,"type":17},{"type":13,"children":955},[956],{"text":957,"type":17},"Ardından listedeki her dosyanın test klasöründe bulunup bulunmadığını kontrol ettim:",{"type":13,"children":959},[960],{"text":21,"type":17},{"type":62,"children":962,"language":66},[963],{"text":964,"type":17},"(\n  bulunan=0\n  eksik=0\n\n  while IFS= read -r dosya; do\n    if [ -f \"$TEST_DIR\u002Fuploads\u002F$dosya\" ]; then\n      bulunan=$((bulunan + 1))\n    else\n      printf 'Eksik: %s\\n' \"$dosya\"\n      eksik=$((eksik + 1))\n    fi\n  done \u003C \"$TEST_DIR\u002Ffiles.txt\"\n\n  printf 'Bulunan: %s\\nEksik: %s\\n' \"$bulunan\" \"$eksik\"\n)",{"type":13,"children":966},[967],{"text":21,"type":17},{"type":13,"children":969},[970],{"text":971,"type":17},"Sonuç:",{"type":62,"children":973,"language":66},[974],{"text":975,"type":17},"Bulunan: 26\nEksik: 0",{"type":13,"children":977},[978],{"text":21,"type":17},{"type":13,"children":980},[981],{"text":982,"type":17},"Bu kontrol, veritabanının referans verdiği 26 yerel dosyanın da yedekte bulunduğunu gösterdi. Dosyaların görüntülenmesini ayrıca Directus üzerinden test ettim.",{"type":13,"children":984},[985],{"text":21,"type":17},{"type":37,"level":38,"children":987},[988],{"text":989,"type":17},"6. R2’deki kopyaları karşılaştırdım",{"type":13,"children":991},[992],{"text":21,"type":17},{"type":13,"children":994},[995],{"text":996,"type":17},"Sunucudaki yedekleri test ettikten sonra Cloudflare R2’deki kopyalarını bilgisayarıma indirdim.",{"type":13,"children":998},[999],{"text":21,"type":17},{"type":13,"children":1001},[1002],{"text":1003,"type":17},"Sunucuda:",{"type":13,"children":1005},[1006],{"text":21,"type":17},{"type":62,"children":1008,"language":66},[1009],{"text":1010,"type":17},"sha256sum \"$DB_BACKUP\"\nsha256sum \"$UPLOADS_BACKUP\"",{"type":13,"children":1012},[1013],{"text":21,"type":17},{"type":13,"children":1015},[1016],{"text":1017,"type":17},"Mac bilgisayarımda:",{"type":13,"children":1019},[1020],{"text":21,"type":17},{"type":62,"children":1022,"language":66},[1023],{"text":1024,"type":17},"shasum -a 256 \"$HOME\u002FDownloads\u002FVERITABANI_YEDEGI.dmp\"\n\nshasum -a 256 \"$HOME\u002FDownloads\u002FDOSYA_YEDEGI.tar.gz\"",{"type":13,"children":1026},[1027],{"text":21,"type":17},{"type":13,"children":1029},[1030],{"text":1031,"type":17},"İki yedeğin de sunucudaki ve bilgisayarımdaki SHA-256 değerleri eşleşti.",{"type":13,"children":1033},[1034],{"text":21,"type":17},{"type":13,"children":1036},[1037],{"text":1038,"type":17},"Böylece R2’den indirdiğim dosyaların, sunucuda test ettiğim yedeklerle birebir aynı olduğunu doğruladım.",{"type":13,"children":1040},[1041],{"text":21,"type":17},{"type":37,"level":38,"children":1043},[1044],{"text":1045,"type":17},"7. Geri yüklenen verilerle Directus’u başlattım",{"type":13,"children":1047},[1048],{"text":21,"type":17},{"type":13,"children":1050},[1051],{"text":1052,"type":17},"Sırada içerikleri uygulama üzerinden görmek vardı.",{"type":13,"children":1054},[1055],{"text":21,"type":17},{"type":13,"children":1057},[1058],{"text":1059,"type":17},"Canlı sistemde kullanılan Docker image kimliğini aldım:",{"type":62,"children":1061,"language":66},[1062],{"text":1063,"type":17},"DIRECTUS_IMAGE=$(docker inspect \"$DIRECTUS_CONTAINER\" \\\n  --format '{{.Image}}')",{"type":13,"children":1065},[1066],{"text":21,"type":17},{"type":13,"children":1068},[1069,1071,1073],{"text":1070,"type":17},"Test ayar dosyasını oluştururken veritabanı şifresini terminale yazdırmadan mevcut container’dan okudum. Dosyaya yalnızca sahibinin erişebilmesi için `",{"code":50,"text":1072,"type":17},"600",{"text":1074,"type":17},"` izni verdim.",{"type":13,"children":1076},[1077],{"text":21,"type":17},{"type":13,"children":1079},[1080],{"text":1081,"type":17},"Bu Python komutu, yukarıdaki değişkenlerle aynı sunucu terminalinde çalıştırılabilir:",{"type":13,"children":1083},[1084],{"text":21,"type":17},{"type":62,"children":1086,"language":66},[1087],{"text":1088,"type":17},"export DIRECTUS_CONTAINER PG_CONTAINER DB_USER TEST_DB TEST_DIR\n\npython3 - \u003C\u003C'PY'\nimport json\nimport os\nimport secrets\nimport subprocess\n\ninfo = json.loads(subprocess.check_output([\n    \"docker\", \"inspect\", os.environ[\"DIRECTUS_CONTAINER\"]\n]))[0]\n\nenv = dict(\n    item.split(\"=\", 1)\n    for item in info[\"Config\"][\"Env\"]\n    if \"=\" in item\n)\n\npassword = env.get(\"DB_PASSWORD\")\nif not password:\n    raise SystemExit(\"DB_PASSWORD bulunamadı.\")\n\nsettings = {\n    \"DB_CLIENT\": \"pg\",\n    \"DB_HOST\": os.environ[\"PG_CONTAINER\"],\n    \"DB_PORT\": \"5432\",\n    \"DB_USER\": os.environ[\"DB_USER\"],\n    \"DB_PASSWORD\": password,\n    \"DB_DATABASE\": os.environ[\"TEST_DB\"],\n    \"SECRET\": secrets.token_hex(32),\n    \"PUBLIC_URL\": \"http:\u002F\u002Flocalhost:18055\",\n    \"STORAGE_LOCATIONS\": \"local\",\n    \"STORAGE_LOCAL_DRIVER\": \"local\",\n    \"STORAGE_LOCAL_ROOT\": \"\u002Fdirectus\u002Fuploads\",\n}\n\nfor key, value in settings.items():\n    if \"\\n\" in value or \"\\r\" in value:\n        raise SystemExit(f\"{key} değeri env dosyası için uygun değil.\")\n\npath = os.path.join(\n    os.environ[\"TEST_DIR\"], \"directus-test.env\"\n)\n\nfd = os.open(\n    path,\n    os.O_WRONLY | os.O_CREAT | os.O_EXCL,\n    0o600,\n)\n\nwith os.fdopen(fd, \"w\") as file:\n    for key, value in settings.items():\n        file.write(f\"{key}={value}\\n\")\n\nprint(\"Test ayarları hazır. Şifre ekrana yazdırılmadı.\")\nPY",{"type":13,"children":1090},[1091],{"text":21,"type":17},{"type":13,"children":1093},[1094,1096,1098],{"text":1095,"type":17},"Bu örnek, benim kurulumum gibi `",{"code":50,"text":1097,"type":17},"DB_PASSWORD",{"text":1099,"type":17},"` ortam değişkeni kullanan ve PostgreSQL container’ına aynı Docker ağı üzerinden erişebilen bir yapı için hazırlanmıştır.",{"type":13,"children":1101},[1102],{"text":21,"type":17},{"type":13,"children":1104},[1105],{"text":1106,"type":17},"Test uygulamasını açmadan önce geri yüklenen veritabanındaki Directus Flow’larını devre dışı bırakmak da önemlidir. Böylece test ortamındaki işlemler e-posta veya webhook gibi dış etkiler oluşturmaz. Aşağıdaki komutun hedefi yalnızca test veritabanıdır:",{"type":13,"children":1108},[1109],{"text":21,"type":17},{"type":62,"children":1111,"language":66},[1112],{"text":1113,"type":17},"docker exec \"$PG_CONTAINER\" \\\n  psql -U \"$DB_USER\" -d \"$TEST_DB\" -c \"\n    UPDATE public.directus_flows\n    SET status = 'inactive'\n    WHERE status = 'active';\n  \"",{"type":13,"children":1115},[1116],{"text":21,"type":17},{"type":13,"children":1118},[1119,1121,1123],{"text":1120,"type":17},"Test dosyalarının sahipliğini image içindeki `",{"code":50,"text":1122,"type":17},"node",{"text":1124,"type":17},"` kullanıcısına ayarladım:",{"type":13,"children":1126},[1127],{"text":21,"type":17},{"type":62,"children":1129,"language":66},[1130],{"text":1131,"type":17},"docker run --rm --user 0 --entrypoint sh \\\n  -v \"$TEST_DIR\u002Fuploads:\u002Frestore-uploads\" \\\n  \"$DIRECTUS_IMAGE\" \\\n  -c 'chown -R node:node \u002Frestore-uploads'",{"type":13,"children":1133},[1134],{"text":21,"type":17},{"type":13,"children":1136},[1137],{"text":1138,"type":17},"Ardından test container’ını başlattım:",{"type":13,"children":1140},[1141],{"text":21,"type":17},{"type":62,"children":1143,"language":66},[1144],{"text":1145,"type":17},"docker run -d \\\n  --name \"$TEST_CONTAINER\" \\\n  --network \"$DOCKER_NETWORK\" \\\n  -p 127.0.0.1:18055:8055 \\\n  --env-file \"$TEST_DIR\u002Fdirectus-test.env\" \\\n  -v \"$TEST_DIR\u002Fuploads:\u002Fdirectus\u002Fuploads\" \\\n  \"$DIRECTUS_IMAGE\"",{"type":13,"children":1147},[1148],{"text":21,"type":17},{"type":13,"children":1150},[1151],{"text":1152,"type":17},"Bu testte veritabanı ve uploads yedeğini kullandım. Özel extension ve template volume’lerini test container’ına bağlamadım.",{"type":13,"children":1154},[1155],{"text":21,"type":17},{"type":13,"children":1157},[1158],{"text":1159,"type":17},"Logları ve sağlık durumunu kontrol ettim:",{"type":13,"children":1161},[1162],{"text":21,"type":17},{"type":62,"children":1164,"language":66},[1165],{"text":1166,"type":17},"docker logs --tail 60 \"$TEST_CONTAINER\"\n\ncurl -sS --max-time 10 \\\n  http:\u002F\u002F127.0.0.1:18055\u002Fserver\u002Fhealth",{"type":13,"children":1168},[1169],{"text":21,"type":17},{"type":13,"children":1171},[1172],{"text":1173,"type":17},"Sağlık kontrolünün sonucu:",{"type":13,"children":1175},[1176],{"text":21,"type":17},{"type":62,"children":1178,"language":66},[1179],{"text":1180,"type":17},"{\"status\":\"ok\"}",{"type":13,"children":1182},[1183],{"text":21,"type":17},{"type":37,"level":38,"children":1185},[1186],{"text":1187,"type":17},"8. SSH tüneliyle test paneline girdim",{"type":13,"children":1189},[1190],{"text":21,"type":17},{"type":13,"children":1192},[1193,1195,1197],{"text":1194,"type":17},"Test portunu sunucuda yalnızca `",{"code":50,"text":1196,"type":17},"127.0.0.1",{"text":1198,"type":17},"` adresine bağlamıştım. Paneli bilgisayarımdan açmak için Mac terminalinde SSH tüneli oluşturdum:",{"type":13,"children":1200},[1201],{"text":21,"type":17},{"type":62,"children":1203,"language":66},[1204],{"text":1205,"type":17},"ssh \\\n  -i ~\u002F.ssh\u002FSUNUCU_SSH_ANAHTARI \\\n  -o IdentitiesOnly=yes \\\n  -o ExitOnForwardFailure=yes \\\n  -N \\\n  -L 18055:127.0.0.1:18055 \\\n  root@SUNUCU_IP_ADRESI",{"type":13,"children":1207},[1208],{"text":21,"type":17},{"type":13,"children":1210},[1211],{"text":1212,"type":17},"Tünel açıkken tarayıcıdan şu adrese girdim:",{"type":13,"children":1214},[1215],{"text":21,"type":17},{"type":62,"children":1217,"language":66},[1218],{"text":1219,"type":17},"http:\u002F\u002Flocalhost:18055\u002Fadmin",{"type":13,"children":1221},[1222],{"text":21,"type":17},{"type":13,"children":1224},[1225],{"text":1226,"type":17},"Panele giriş yapıp blog yazılarını, içerikleri ve görselleri açtım. Kontrol ettiğim içerikler görüntüleniyordu.",{"type":13,"children":1228},[1229],{"text":21,"type":17},{"type":13,"children":1231},[1232],{"text":1233,"type":17},"Benim için testin en rahatlatıcı kısmı buydu: Yedek dosyaları, tekrar kullanılabilen bir uygulamaya dönüşmüştü.",{"type":13,"children":1235},[1236],{"text":21,"type":17},{"type":37,"level":38,"children":1238},[1239],{"text":1240,"type":17},"9. Test ortamını temizledim",{"type":13,"children":1242},[1243],{"text":21,"type":17},{"type":13,"children":1245},[1246],{"text":1247,"type":17},"Kontroller tamamlanınca yalnızca oluşturduğum test kaynaklarını kaldırdım:",{"type":13,"children":1249},[1250],{"text":21,"type":17},{"type":62,"children":1252,"language":66},[1253],{"text":1254,"type":17},"docker rm -f \"$TEST_CONTAINER\"\n\ndocker exec \"$PG_CONTAINER\" \\\n  dropdb -U \"$DB_USER\" \"$TEST_DB\"",{"type":13,"children":1256},[1257],{"text":21,"type":17},{"type":13,"children":1259},[1260],{"text":1261,"type":17},"Geçici klasörü silerken hedefi açıkça kontrol eden bir koşul kullandım:",{"type":13,"children":1263},[1264],{"text":21,"type":17},{"type":62,"children":1266,"language":66},[1267],{"text":1268,"type":17},"if [ \"$TEST_DIR\" = \"\u002Froot\u002Fstudioae-restore-test-20261009\" ]; then\n  rm -rf -- \"$TEST_DIR\"\nelse\n  printf 'Beklenmeyen klasör; silinmedi: %s\\n' \"$TEST_DIR\"\nfi",{"type":13,"children":1270},[1271],{"text":21,"type":17},{"type":13,"children":1273},[1274,1276,1278],{"text":1275,"type":17},"Son olarak Mac’te SSH tünelinin açık olduğu terminalde `",{"code":50,"text":1277,"type":17},"Control + C",{"text":1279,"type":17},"` ile bağlantıyı kapattım.",{"type":13,"children":1281},[1282],{"text":21,"type":17},{"type":37,"level":38,"children":1284},[1285],{"text":1286,"type":17},"Bildirim tarafında da küçük bir sorun çıktı",{"type":13,"children":1288},[1289],{"text":21,"type":17},{"type":13,"children":1291},[1292],{"text":1293,"type":17},"Yedekleme başarısız olursa haberdar olmak için Coolify’de e-posta bildirimlerini ayarladım.",{"type":13,"children":1295},[1296],{"text":21,"type":17},{"type":13,"children":1298},[1299],{"text":1300,"type":17},"Test maili gelmeyince, sunucudan Gmail’e bağlantıyı kontrol ettim. 465 portu zaman aşımına uğrarken 587 portuna bağlantı kurulabildi. Port ve şifreleme ayarını düzelttikten sonra test maili ulaştı.",{"type":13,"children":1302},[1303],{"text":21,"type":17},{"type":13,"children":1305},[1306],{"text":1307,"type":17},"Bu adımda e-posta gönderimini doğruladım ve yedekleme hatası bildirimini etkin bıraktım. Gerçek bir yedekleme hatası oluşturarak ayrıca bildirim testi yapmadım.",{"type":13,"children":1309},[1310],{"text":21,"type":17},{"type":37,"level":38,"children":1312},[1313],{"text":1314,"type":17},"Bu testten ne öğrendim?",{"type":13,"children":1316},[1317],{"text":21,"type":17},{"type":13,"children":1319},[1320],{"text":1321,"type":17},"Yedeklerin listede görünmesi iyi bir başlangıç. Ancak geri yükleme denemesi yapmak, neyi yedeklediğimi ve nasıl geri dönebileceğimi çok daha net anlamamı sağladı.",{"type":13,"children":1323},[1324],{"text":21,"type":17},{"type":13,"children":1326},[1327],{"text":1328,"type":17},"Bu çalışmada:",{"type":13,"children":1330},[1331],{"text":21,"type":17},{"type":486,"format":487,"children":1333},[1334,1338,1342,1346],{"type":490,"children":1335},[1336],{"text":1337,"type":17},"Veritabanını ayrı bir test veritabanına geri yükledim.",{"type":490,"children":1339},[1340],{"text":1341,"type":17},"Veritabanındaki 26 dosya kaydını arşivle eşleştirdim.",{"type":490,"children":1343},[1344],{"text":1345,"type":17},"R2’den indirdiğim kopyaların SHA-256 değerlerini karşılaştırdım.",{"type":490,"children":1347},[1348],{"text":1349,"type":17},"Directus panelinden içerikleri ve görselleri kontrol ettim.",{"type":13,"children":1351},[1352],{"text":21,"type":17},{"type":13,"children":1354},[1355],{"text":1356,"type":17},"Bu, bütün sunucuyu sıfırdan kurduğum kapsamlı bir felaket kurtarma provası değildi. Directus’un veritabanı ve yüklenen dosyaları için yaptığım bir geri yükleme testiydi.",{"type":13,"children":1358},[1359],{"text":21,"type":17},{"type":13,"children":1361},[1362],{"text":1363,"type":17},"Ayrıca veritabanı ve dosya yedekleri farklı saatlerde alındığı için, dosyaların eşleştiğini doğrulamak özellikle değerliydi. Bu testte eksik dosya çıkmadı; aynı sonucun gelecekteki her yedek çifti için geçerli olduğunu varsaymamak gerekiyor.",{"type":13,"children":1365},[1366],{"text":21,"type":17},{"type":13,"children":1368},[1369],{"text":1370,"type":17},"Başlangıçtaki soruma ise somut bir cevap alabildim:",{"type":13,"children":1372},[1373],{"text":21,"type":17},{"type":13,"children":1375},[1376],{"bold":50,"text":1377,"type":17},"Test ettiğim yedeklerle içeriklerimi ve dosyalarımı geri getirebildim.",{"type":13,"children":1379},[1380],{"text":21,"type":17},{"type":13,"children":1382},[1383],{"text":1384,"type":17},"Bundan sonra yedeklemeyi yalnızca “dosya oluştu mu?” diye kontrol etmeyeceğim. Önemli değişikliklerden sonra geri yükleme sürecini de yeniden deneyeceğim.","2026-10-09","2026-10-09T15:52:31.829Z","2026-10-09T16:16:34.032Z","2026-10-09T16:16:34.148Z","https:\u002F\u002Fmedium.com\u002F@aleynaertin1\u002Fyedek-almak-yetmez-coolify-ve-r2-yedeklerimi-geri-y%C3%BCkleyerek-test-ettim-b12d9d277eb3","Coolify, Cloudflare R2, Directus, PostgreSQL, Docker, Yedekleme, Hetzner","Coolify ve R2 Yedeklerini Geri Yükleme Testi","Directus ve PostgreSQL yedeklerini geri yükleme, dosyaları eşleştirme ve R2 kopyalarını SHA-256 ile doğrulama adımlarını komutlarla paylaşıyorum.",{"id":1394,"documentId":1395,"name":1396,"alternativeText":567,"caption":567,"focalPoint":567,"width":568,"height":569,"formats":1397,"hash":1426,"ext":572,"mime":575,"size":1427,"url":1428,"previewUrl":567,"provider":613,"provider_metadata":1429,"createdAt":1430,"updatedAt":1431,"publishedAt":1430},7,"ay0p6gz44stp2ldbwbqdqk63","coolify-r2-yedek-geri-yukleme-testi.webp",{"large":1398,"small":1405,"medium":1412,"thumbnail":1419},{"ext":572,"url":1399,"hash":1400,"mime":575,"name":1401,"path":567,"size":1402,"width":578,"height":579,"sizeInBytes":1403,"provider_metadata":1404},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791562573\u002Flarge_coolify_r2_yedek_geri_yukleme_testi_727151d568.webp","large_coolify_r2_yedek_geri_yukleme_testi_727151d568","large_coolify-r2-yedek-geri-yukleme-testi.webp",26.44,26442,{"public_id":1400,"resource_type":582},{"ext":572,"url":1406,"hash":1407,"mime":575,"name":1408,"path":567,"size":1409,"width":588,"height":589,"sizeInBytes":1410,"provider_metadata":1411},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791562573\u002Fsmall_coolify_r2_yedek_geri_yukleme_testi_727151d568.webp","small_coolify_r2_yedek_geri_yukleme_testi_727151d568","small_coolify-r2-yedek-geri-yukleme-testi.webp",10.78,10776,{"public_id":1407,"resource_type":582},{"ext":572,"url":1413,"hash":1414,"mime":575,"name":1415,"path":567,"size":1416,"width":597,"height":598,"sizeInBytes":1417,"provider_metadata":1418},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791562572\u002Fmedium_coolify_r2_yedek_geri_yukleme_testi_727151d568.webp","medium_coolify_r2_yedek_geri_yukleme_testi_727151d568","medium_coolify-r2-yedek-geri-yukleme-testi.webp",18.19,18188,{"public_id":1414,"resource_type":582},{"ext":572,"url":1420,"hash":1421,"mime":575,"name":1422,"path":567,"size":1423,"width":606,"height":607,"sizeInBytes":1424,"provider_metadata":1425},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791562572\u002Fthumbnail_coolify_r2_yedek_geri_yukleme_testi_727151d568.webp","thumbnail_coolify_r2_yedek_geri_yukleme_testi_727151d568","thumbnail_coolify-r2-yedek-geri-yukleme-testi.webp",4.19,4192,{"public_id":1421,"resource_type":582},"coolify_r2_yedek_geri_yukleme_testi_727151d568",51.26,"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791562571\u002Fcoolify_r2_yedek_geri_yukleme_testi_727151d568.webp",{"public_id":1426,"resource_type":582},"2026-10-09T16:16:13.509Z","2026-10-09T16:16:27.427Z",{"id":1433,"documentId":1434,"title":1435,"slug":1436,"description":1437,"content":1438,"date":1794,"admin":1795,"createdAt":1796,"updatedAt":1797,"publishedAt":1798,"mediumUrl":1799,"tags":1800,"featured":50,"seoTitle":1801,"seoDescription":1802,"category":1803,"image":1804},21,"ifbgrabwoqxm96rdge42h7ie","Nuxt Portföyümü Vercel’den Hetzner’e Taşıdım: Coolify ile Yaşadığım Deneyim","nuxt-portfoyumu-vercel-den-hetzner-e-tasidim-coolify-ile-yasadigim-deneyim","Nuxt portföyümü Vercel’den Hetzner’e taşıma deneyimim: Coolify, Docker, karşılaştığım sorunlar ve yedekleme süreci.",[1439,1443,1446,1450,1453,1457,1460,1464,1467,1471,1474,1478,1481,1485,1488,1492,1495,1502,1509,1516,1523,1530,1533,1537,1540,1544,1547,1567,1570,1574,1577,1585,1588,1592,1595,1599,1602,1606,1609,1613,1616,1620,1623,1627,1630,1638,1641,1657,1660,1675,1678,1682,1685,1689,1692,1696,1699,1707,1710,1714,1717,1721,1724,1731,1738,1741,1745,1748,1752,1755,1759,1762,1766,1769,1773,1776,1780,1783,1787,1790],{"type":37,"level":38,"children":1440},[1441],{"text":1442,"type":17},"Neden kendi sunucuma geçmeye karar verdim?",{"type":13,"children":1444},[1445],{"text":21,"type":17},{"type":13,"children":1447},[1448],{"text":1449,"type":17},"Nuxt ile geliştirdiğim kişisel portföy sitem aleynaertin.com’u Vercel’de yayınlıyordum. Kendi sunucuma geçme kararımın arkasında iki hedef vardı: projelerimi tek bir sunucuda toplamak ve sunucu yönetimini öğrenmek.",{"type":13,"children":1451},[1452],{"text":21,"type":17},{"type":13,"children":1454},[1455],{"text":1456,"type":17},"Uygulama geliştirdikten sonra onu yayına alırken arka planda neler olduğunu da daha iyi anlamak istiyordum. Dağıtım süreci, alan adı ayarları, HTTPS ve yedekleme gibi konularda kendi projem üzerinden deneyim kazanmak, bunun için iyi bir başlangıçtı.",{"type":13,"children":1458},[1459],{"text":21,"type":17},{"type":13,"children":1461},[1462],{"text":1463,"type":17},"Bu hedeflerle Hetzner üzerinde bir sunucu kurup uygulamalarımı Coolify ile yönetmeye karar verdim. İlk adım olarak portföy sitemi taşıdım. Bu yazıda taşıma sürecini, karşılaştığım sorunları ve öğrendiklerimi paylaşacağım.",{"type":13,"children":1465},[1466],{"text":21,"type":17},{"type":37,"level":38,"children":1468},[1469],{"text":1470,"type":17},"Kullandığım yapı ve sunucu seçimi",{"type":13,"children":1472},[1473],{"text":21,"type":17},{"type":13,"children":1475},[1476],{"text":1477,"type":17},"Sunucu seçerken başlangıç maliyetini düşük tutmak ve kendi projelerim üzerinden deneyim kazanabileceğim bir ortam oluşturmak istedim. Uygun maliyeti ve Almanya’daki sunucu lokasyonu nedeniyle Hetzner’i tercih ettim. Başlangıç için 2 GB RAM’li bir sunucuyla yola çıktım.",{"type":13,"children":1479},[1480],{"text":21,"type":17},{"type":13,"children":1482},[1483],{"text":1484,"type":17},"Hedefim, projelerimi tek sunucuda toplamanın yanında sunucu yönetimini ve Docker’ı öğrenmekti. Uygulamaların container içinde nasıl çalıştığını, sunucu kaynaklarını nasıl kullandığını ve dağıtım sırasında çıkan sorunlara nasıl müdahale edebileceğimi kendi projelerim üzerinden anlamak istiyordum. İleride küçük müşteri sitelerini de aynı yapı üzerinde barındırabilmek, bu tercihin bir diğer nedeniydi.",{"type":13,"children":1486},[1487],{"text":21,"type":17},{"type":13,"children":1489},[1490],{"text":1491,"type":17},"Kurduğum yapıda araçların görevleri şöyleydi:",{"type":13,"children":1493},[1494],{"text":21,"type":17},{"type":13,"children":1496},[1497,1498,1500],{"text":290,"type":17},{"bold":50,"text":1499,"type":17},"Nuxt:",{"text":1501,"type":17}," Kişisel portföy sitemi geliştirdiğim framework.",{"type":13,"children":1503},[1504,1505,1507],{"text":290,"type":17},{"bold":50,"text":1506,"type":17},"Hetzner:",{"text":1508,"type":17}," Uygulamamın çalıştığı bulut sunucusu.",{"type":13,"children":1510},[1511,1512,1514],{"text":290,"type":17},{"bold":50,"text":1513,"type":17},"Docker: ",{"text":1515,"type":17},"Uygulamayı ve ihtiyaç duyduğu çalışma ortamını container içinde çalıştıran teknoloji.",{"type":13,"children":1517},[1518,1519,1521],{"text":290,"type":17},{"bold":50,"text":1520,"type":17},"Coolify:",{"text":1522,"type":17}," Docker tabanlı uygulamalarımı ve dağıtım sürecini bir arayüz üzerinden yönetmemi sağlayan araç.",{"type":13,"children":1524},[1525,1526,1528],{"text":290,"type":17},{"bold":50,"text":1527,"type":17},"GitHub:",{"text":1529,"type":17}," Kaynak kodumu tuttuğum ve Coolify üzerinden dağıtıma bağladığım platform.",{"type":13,"children":1531},[1532],{"text":21,"type":17},{"type":13,"children":1534},[1535],{"text":1536,"type":17},"Bu yapı sayesinde uygulamalarımı Coolify arayüzünden yönetirken arka plandaki Docker ve sunucu süreçlerini de adım adım öğrenebileceğim bir ortam oluşturdum. İlk uygulama olarak kişisel portföy sitem aleynaertin.com’u taşıyarak başladım.",{"type":13,"children":1538},[1539],{"text":21,"type":17},{"type":37,"level":38,"children":1541},[1542],{"text":1543,"type":17},"İlk dağıtım ve karşılaştığım sorunlar",{"type":13,"children":1545},[1546],{"text":21,"type":17},{"type":13,"children":1548},[1549,1551,1553,1555,1557,1559,1561,1563,1565],{"text":1550,"type":17},"İlk adım olarak portföy sitemin GitHub reposunu Coolify’a bağladım ve dağıtım için `",{"code":50,"text":1552,"type":17},"main",{"text":1554,"type":17},"` branch’ini seçtim. Ardından Nuxt uygulamasının build komutunu `",{"code":50,"text":1556,"type":17},"npm run build",{"text":1558,"type":17},"`, çalıştırma komutunu ise `",{"code":50,"text":1560,"type":17},"node .output\u002Fserver\u002Findex.mjs",{"text":1562,"type":17},"` olarak ayarladım. Uygulamanın kullanacağı portu da `",{"code":50,"text":1564,"type":17},"3000",{"text":1566,"type":17},"` olarak belirledim.",{"type":13,"children":1568},[1569],{"text":21,"type":17},{"type":13,"children":1571},[1572],{"text":1573,"type":17},"İlk dağıtım denemeleri sorunsuz ilerlemedi. Build kayıtlarını incelerken Node.js sürümü ve bağımlılıklarla ilgili hatalarla karşılaştım. Bu süreç, geliştirme ortamımda çalışan bir projenin sunucuda da aynı şekilde çalışabilmesi için çalışma ortamının doğru hazırlanması gerektiğini gösterdi.",{"type":13,"children":1575},[1576],{"text":21,"type":17},{"type":13,"children":1578},[1579,1581,1583],{"text":1580,"type":17},"Bu aşamayı geçtikten sonra build sırasında başka bir hata ortaya çıktı: `",{"code":50,"text":1582,"type":17},"JavaScript heap out of memory",{"text":1584,"type":17},"`. Derleme işlemi, Node.js’in kullanabildiği heap belleğinin sınırına ulaşıyordu.",{"type":13,"children":1586},[1587],{"text":21,"type":17},{"type":13,"children":1589},[1590],{"text":1591,"type":17},"Sunucunun kaynaklarını kontrol ettiğimde yaklaşık 2 GB RAM ve mevcut 2 GB swap alanı olduğunu gördüm. Böylece fiziksel RAM, swap ve Node.js’in bellek sınırının ayrı ayrı değerlendirilmesi gerektiğini öğrenmeye başladım.",{"type":13,"children":1593},[1594],{"text":21,"type":17},{"type":13,"children":1596},[1597],{"text":1598,"type":17},"Bellek hatasının ardından Coolify’da şu ortam değişkenini tanımladım:",{"type":13,"children":1600},[1601],{"text":21,"type":17},{"type":62,"children":1603,"language":66},[1604],{"text":1605,"type":17},"NODE_OPTIONS=--max-old-space-size=1536",{"type":13,"children":1607},[1608],{"text":21,"type":17},{"type":13,"children":1610},[1611],{"text":1612,"type":17},"Bu ayarla Node.js’in old-space bellek sınırını 1536 MB olarak belirledim. Değişkeni yalnızca build sırasında kullanılacak şekilde ayarladım; çalışan uygulamanın runtime ortamına eklemedim. Bu, sunucunun RAM’ini artırmak yerine derleme sürecindeki Node.js bellek sınırını düzenleyen bir değişiklikti.",{"type":13,"children":1614},[1615],{"text":21,"type":17},{"type":13,"children":1617},[1618],{"text":1619,"type":17},"Bu deneyim, sunucu yönetimini öğrenme hedefimin ilk somut adımlarından biri oldu. Coolify üzerinden dağıtımı başlatmanın yanında, hata kayıtlarını okuyup hangi aşamada sorun yaşandığını anlamam da gerekiyordu.",{"type":13,"children":1621},[1622],{"text":21,"type":17},{"type":37,"level":38,"children":1624},[1625],{"text":1626,"type":17},"Alan adını yönlendirme ve HTTPS",{"type":13,"children":1628},[1629],{"text":21,"type":17},{"type":13,"children":1631},[1632,1634,1636],{"text":1633,"type":17},"Dağıtımın ardından sıra, aleynaertin.com alan adını yeni sunucuya yönlendirmeye geldi. DNS kayıtlarımı Cloudflare üzerinden yönetiyordum. Ana alan adının A kaydını Hetzner sunucumun IP adresine yönlendirdim. `",{"code":50,"text":1635,"type":17},"www",{"text":1637,"type":17},"` için de ana alan adını işaret eden bir CNAME kaydı kullandım.",{"type":13,"children":1639},[1640],{"text":21,"type":17},{"type":13,"children":1642},[1643,1645,1647,1649,1651,1653,1655],{"text":1644,"type":17},"Bu aşamada yalnızca siteyi tarayıcıda açıp kontrol etmekle kalmadım. `",{"code":50,"text":1646,"type":17},"dig",{"text":1648,"type":17},"` komutuyla hem `",{"code":50,"text":1650,"type":17},"aleynaertin.com",{"text":1652,"type":17},"` hem de `",{"code":50,"text":1654,"type":17},"www.aleynaertin.com",{"text":1656,"type":17},"` adreslerinin yeni sunucuya çözümlendiğini kontrol ettim. Böylece DNS tarafındaki değişikliğin sonucunu görebildim.",{"type":13,"children":1658},[1659],{"text":21,"type":17},{"type":13,"children":1661},[1662,1664,1666,1668,1670,1672,1673],{"text":1663,"type":17},"Ardından HTTPS erişimini kontrol ettim. `",{"code":50,"text":1665,"type":17},"curl -I",{"text":1667,"type":17},"` ile yaptığım kontrollerde iki adres de `",{"code":50,"text":1669,"type":17},"HTTP\u002F2 200",{"text":1671,"type":17},"` yanıtı verdi. Yanıt başlıklarında Nuxt bilgisini de görmek, uygulamanın alan adı üzerinden erişilebilir olduğunu doğrulamama yardımcı oldu. Tarayıcıda ise `",{"code":50,"text":1635,"type":17},{"text":1674,"type":17},"` adresinin ana alan adına yönlendiğini kontrol ettim.",{"type":13,"children":1676},[1677],{"text":21,"type":17},{"type":13,"children":1679},[1680],{"text":1681,"type":17},"Bu süreçte dağıtım, DNS ve HTTPS’nin ayrı ayrı kontrol edilmesi gereken adımlar olduğunu öğrendim. Uygulamanın sunucuda çalışması ilk aşamaydı; ziyaretçilerin doğru alan adı üzerinden güvenli şekilde erişebilmesiyle taşımanın bu kısmı tamamlandı.",{"type":13,"children":1683},[1684],{"text":21,"type":17},{"type":37,"level":38,"children":1686},[1687],{"text":1688,"type":17},"StudioAE için veritabanı yedekleme",{"type":13,"children":1690},[1691],{"text":21,"type":17},{"type":13,"children":1693},[1694],{"text":1695,"type":17},"Portföy sitemin taşıma işleminin ardından, studioae.com.tr projemde kullandığım Directus’un veritabanı yedeklerini düzenledim. Bu, aleynaertin.com’un dağıtımından ayrı olarak yaptığım bir çalışmaydı.",{"type":13,"children":1697},[1698],{"text":21,"type":17},{"type":13,"children":1700},[1701,1703,1705],{"text":1702,"type":17},"Coolify üzerinden Directus’un PostgreSQL veritabanı için günlük otomatik yedeklemeyi etkinleştirdim. Yedekleme saatini Türkiye saatiyle 05.00 olarak ayarladım. İlk yedeği kontrol ettiğimde yaklaşık 427 KB büyüklüğünde bir dosyanın oluştuğunu ve “",{"bold":50,"text":1704,"type":17},"Local Available",{"text":1706,"type":17},"” durumunda olduğunu gördüm. Bu, yedeğin sunucuya kaydedildiğini gösteriyordu.",{"type":13,"children":1708},[1709],{"text":21,"type":17},{"type":13,"children":1711},[1712],{"text":1713,"type":17},"Yedeklerin yalnızca uygulamanın çalıştığı sunucuda kalmasını istemedim. Sunucuya erişimi kaybedersem yedeklere başka bir yerden ulaşabilmek için Cloudflare R2’yi Coolify’a harici yedekleme alanı olarak bağladım ve veritabanı yedeklerini buraya da gönderdim.",{"type":13,"children":1715},[1716],{"text":21,"type":17},{"type":13,"children":1718},[1719],{"text":1720,"type":17},"Eski yedeklerin sürekli birikmesini önlemek için iki ayrı saklama süresi belirledim:",{"type":13,"children":1722},[1723],{"text":21,"type":17},{"type":13,"children":1725},[1726,1727,1729],{"text":290,"type":17},{"bold":50,"text":1728,"type":17},"Sunucuda, Coolify’ın yönettiği yerel yedekler:",{"text":1730,"type":17}," 7 gün.",{"type":13,"children":1732},[1733,1734,1736],{"text":290,"type":17},{"bold":50,"text":1735,"type":17},"Sunucu dışında, Cloudflare R2’deki yedekler: ",{"text":1737,"type":17},"30 gün.",{"type":13,"children":1739},[1740],{"text":21,"type":17},{"type":13,"children":1742},[1743],{"text":1744,"type":17},"Böylece yakın tarihli yedekleri sunucuda tutarken daha uzun bir yedek geçmişini R2’de saklayabileceğim bir düzen oluşturdum.",{"type":13,"children":1746},[1747],{"text":21,"type":17},{"type":13,"children":1749},[1750],{"text":1751,"type":17},"Bu yedekleme PostgreSQL veritabanını kapsıyordu. Directus’a yüklenen görseller ve diğer dosyalar için ayrıca dosya yedeklemesi gerekiyordu. Geri yükleme testini ise henüz yapmadım; bir sonraki adımım, aldığım yedeği ayrı bir ortamda geri yükleyerek kullanılabilir olduğunu doğrulamak.",{"type":13,"children":1753},[1754],{"text":21,"type":17},{"type":37,"level":38,"children":1756},[1757],{"text":1758,"type":17}," Bu süreçte öğrendiklerim ve sonraki adımlar",{"type":13,"children":1760},[1761],{"text":21,"type":17},{"type":13,"children":1763},[1764],{"text":1765,"type":17},"Başlamadan önce sunucuya taşıma işlemi gözümü korkutuyordu. Uygulamanın çalışması, Docker, DNS ayarları ve HTTPS gibi konuları bir arada düşününce süreç karmaşık görünüyordu. Ancak adımları tek tek ele alıp karşılaştığım hataları inceledikçe, düşündüğüm kadar göz korkutucu olmadığını gördüm.",{"type":13,"children":1767},[1768],{"text":21,"type":17},{"type":13,"children":1770},[1771],{"text":1772,"type":17},"Projelerimi aynı sunucuda toplamak da yönetim açısından rahatlık sağladı. Coolify üzerinden uygulamalarımı, dağıtım durumlarını ve hata kayıtlarını aynı yerden takip edebilmek, kurmak istediğim düzenin somut bir karşılığı oldu.",{"type":13,"children":1774},[1775],{"text":21,"type":17},{"type":13,"children":1777},[1778],{"text":1779,"type":17},"Bu süreçte benim için en önemli konulardan biri yedeklemeydi. Bir uygulamayı yayına almak kadar, bir sorun yaşandığında verilerini nasıl geri getireceğini düşünmek de gerekiyordu. Yedekleri sunucunun dışında da tutmak ve saklama sürelerini belirlemek, veri kaybına karşı hazırlıklı olmanın bir parçasıydı.",{"type":13,"children":1781},[1782],{"text":21,"type":17},{"type":13,"children":1784},[1785],{"text":1786,"type":17},"Henüz tamamlamadığım adımlar da var. Veritabanı yedeğini ayrı bir ortamda geri yükleyerek test etmek, Directus’a yüklenen dosyalar için yedekleme düzeni oluşturmak ve Render’daki Strapi uygulamamı taşımak sıradaki işlerim.",{"type":13,"children":1788},[1789],{"text":21,"type":17},{"type":13,"children":1791},[1792],{"text":1793,"type":17},"Portföy sitemi taşımak, sunucu yönetimini ve Docker’ı kendi projem üzerinden öğrenmek için iyi bir başlangıç oldu. Her şeyi tek seferde öğrenmem gerekmediğini; küçük adımlarla ilerleyip her adımın sonucunu kontrol ederek bu yapıyı yönetebileceğimi gördüm.","2026-10-08","Aleyna ","2026-10-08T14:34:47.043Z","2026-10-09T17:35:47.335Z","2026-10-09T17:35:48.637Z","https:\u002F\u002Fmedium.com\u002F@aleynaertin1\u002Fnuxt-portf%C3%B6y%C3%BCm%C3%BC-vercelden-hetzner-e-ta%C5%9F%C4%B1d%C4%B1m-coolify-ile-ya%C5%9Fad%C4%B1%C4%9F%C4%B1m-deneyim-f3920fe7b2c4?sharedUserId=aleynaertin1","Nuxt, Vercel, Hetzner, Coolify, Deployment, Kişisel Portföy","Nuxt Portföyümü Hetzner’e Taşıdım: Coolify Deneyimim","Nuxt portföyümü Vercel’den Hetzner’e nasıl taşıdım? Coolify kurulumu, yayın süreci ve kendi sunucumu yönetirken edindiğim deneyimler.","Deneyimler",{"id":1805,"documentId":1806,"name":1807,"alternativeText":567,"caption":567,"focalPoint":567,"width":568,"height":569,"formats":1808,"hash":1837,"ext":572,"mime":575,"size":1838,"url":1839,"previewUrl":567,"provider":613,"provider_metadata":1840,"createdAt":1841,"updatedAt":1841,"publishedAt":1841},9,"l9rgo9jzw7755ezavh9yhyma","nuxt-portfoyumu-vercel-den-hetzner-e-tasidim-coolify-ile-yasadigim-deneyim.webp",{"large":1809,"small":1816,"medium":1823,"thumbnail":1830},{"ext":572,"url":1810,"hash":1811,"mime":575,"name":1812,"path":567,"size":1813,"width":578,"height":579,"sizeInBytes":1814,"provider_metadata":1815},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791567341\u002Flarge_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949.webp","large_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949","large_nuxt-portfoyumu-vercel-den-hetzner-e-tasidim-coolify-ile-yasadigim-deneyim.webp",32.17,32166,{"public_id":1811,"resource_type":582},{"ext":572,"url":1817,"hash":1818,"mime":575,"name":1819,"path":567,"size":1820,"width":588,"height":589,"sizeInBytes":1821,"provider_metadata":1822},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791567341\u002Fsmall_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949.webp","small_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949","small_nuxt-portfoyumu-vercel-den-hetzner-e-tasidim-coolify-ile-yasadigim-deneyim.webp",12.64,12644,{"public_id":1818,"resource_type":582},{"ext":572,"url":1824,"hash":1825,"mime":575,"name":1826,"path":567,"size":1827,"width":597,"height":598,"sizeInBytes":1828,"provider_metadata":1829},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791567341\u002Fmedium_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949.webp","medium_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949","medium_nuxt-portfoyumu-vercel-den-hetzner-e-tasidim-coolify-ile-yasadigim-deneyim.webp",22.36,22362,{"public_id":1825,"resource_type":582},{"ext":572,"url":1831,"hash":1832,"mime":575,"name":1833,"path":567,"size":1834,"width":606,"height":607,"sizeInBytes":1835,"provider_metadata":1836},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791567340\u002Fthumbnail_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949.webp","thumbnail_nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949","thumbnail_nuxt-portfoyumu-vercel-den-hetzner-e-tasidim-coolify-ile-yasadigim-deneyim.webp",4.6,4602,{"public_id":1832,"resource_type":582},"nuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949",63.35,"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791567340\u002Fnuxt_portfoyumu_vercel_den_hetzner_e_tasidim_coolify_ile_yasadigim_deneyim_4ba7d75949.webp",{"public_id":1837,"resource_type":582},"2026-10-09T17:35:42.134Z",{"id":1843,"documentId":1844,"title":1845,"slug":1846,"description":1847,"content":1848,"date":2097,"admin":553,"createdAt":2098,"updatedAt":2099,"publishedAt":2100,"mediumUrl":567,"tags":2101,"featured":559,"seoTitle":2102,"seoDescription":2103,"category":562,"image":2104},22,"yoha4gprd1i65027e2cu3zc9","Nuxt 3 Portföyümde Performansı İyileştirme Süreci","nuxt-3-portfoyumde-performansi-iyilestirme-suereci","Nuxt 3 portföyümde yükleme sürelerini iyileştirmek için sunucu konumu, önbellekleme ve görsel optimizasyonu üzerine yaptığım çalışmalar.",[1849,1852,1856,1859,1863,1866,1870,1873,1877,1880,1884,1887,1891,1894,1898,1901,1905,1908,1912,1915,1919,1922,1926,1929,1933,1936,1940,1943,1947,1950,1956,1962,1968,1974,1977,1981,1984,1988,1991,1995,1998,2002,2005,2009,2012,2016,2019,2023,2026,2030,2069,2072,2076,2079,2083,2086,2090,2093],{"type":13,"children":1850},[1851],{"text":21,"type":17},{"type":13,"children":1853},[1854],{"text":1855,"type":17},"Portföy sitem aleynaertin.com’u ilk geliştirdiğimde daha çok tasarıma ve içeriklere odaklanmıştım. Ancak siteyi kullanırken bazı sayfaların beklediğimden yavaş açıldığını fark ettim. Bunun üzerine yükleme sürecini inceleyerek gecikmenin hangi aşamalarda oluştuğunu anlamaya çalıştım.",{"type":13,"children":1857},[1858],{"text":21,"type":17},{"type":13,"children":1860},[1861],{"text":1862,"type":17},"Nuxt 3, Strapi ve Vercel kullandığım bu yapıda; sunucu yanıt süresi, içeriklerin alınması ve görsellerin yüklenmesi üzerinde durdum. Bu yazıda performansı iyileştirirken ele aldığım noktaları paylaşacağım.",{"type":13,"children":1864},[1865],{"text":21,"type":17},{"type":37,"level":38,"children":1867},[1868],{"text":1869,"type":17},"Sunucu konumunu değerlendirmek",{"type":13,"children":1871},[1872],{"text":21,"type":17},{"type":13,"children":1874},[1875],{"text":1876,"type":17},"İlk incelediğim konulardan biri, uygulamanın çalıştığı sunucunun konumuydu. Türkiye’den yapılan ziyaretlerde, sunucuda işlenen isteklerin uzak bir bölgeye gitmesi yanıt süresini etkileyebiliyordu.",{"type":13,"children":1878},[1879],{"text":21,"type":17},{"type":13,"children":1881},[1882],{"text":1883,"type":17},"Bu nedenle Vercel’de uygulamanın sunucu tarafında çalıştığı bölgeyi Frankfurt olarak ayarladım. Buradaki amacım, ziyaretçilerim ile uygulamanın istekleri işlediği sunucu arasındaki mesafeyi azaltmaktı.",{"type":13,"children":1885},[1886],{"text":21,"type":17},{"type":13,"children":1888},[1889],{"text":1890,"type":17},"Bu değişikliği değerlendirirken özellikle TTFB’ye, yani tarayıcının ilk yanıt baytını alana kadar geçen süreye odaklandım.",{"type":13,"children":1892},[1893],{"text":21,"type":17},{"type":37,"level":38,"children":1895},[1896],{"text":1897,"type":17},"Her istekte yeniden oluşturulmayan sayfalar",{"type":13,"children":1899},[1900],{"text":21,"type":17},{"type":13,"children":1902},[1903],{"text":1904,"type":17},"Bir diğer konu, Strapi’den alınan içeriklerin sayfa yüklenmesine etkisiydi. Sık değişmeyen içerikler için her istekte yeniden veri almak ve sayfayı sunucuda oluşturmak yerine, önbellekten sunulabilecek bir yapı üzerinde çalıştım.",{"type":13,"children":1906},[1907],{"text":21,"type":17},{"type":13,"children":1909},[1910],{"text":1911,"type":17},"Nuxt’un route rules özelliğiyle ana sayfa, proje ve blog sayfaları için farklı yenileme kuralları tanımladım. ISR kullanarak oluşturulan sayfaların belirli sürelerle yeniden üretilmesini hedefledim.",{"type":13,"children":1913},[1914],{"text":21,"type":17},{"type":13,"children":1916},[1917],{"text":1918,"type":17},"Böylece içeriklerin güncellenme sıklığına göre karar verebildim. Blog yazıları gibi daha seyrek değişen sayfalar ile daha sık güncellenebilecek alanların aynı kurala bağlı olması gerekmiyordu.",{"type":13,"children":1920},[1921],{"text":21,"type":17},{"type":37,"level":38,"children":1923},[1924],{"text":1925,"type":17},"Görsellerin yüklenmesini düzenlemek",{"type":13,"children":1927},[1928],{"text":21,"type":17},{"type":13,"children":1930},[1931],{"text":1932,"type":17},"Görseller, özellikle ilk ekranda büyük yer kaplayan hero görseli, performans çalışmasının önemli bir parçasıydı.",{"type":13,"children":1934},[1935],{"text":21,"type":17},{"type":13,"children":1937},[1938],{"text":1939,"type":17},"NuxtImg ve Cloudinary ile görsellerin boyutlarını ve sunulma biçimini düzenledim. İlk ekranda görünen görselin erken yüklenmesine öncelik verirken, sayfanın aşağısındaki görseller için lazy loading kullandım.",{"type":13,"children":1941},[1942],{"text":21,"type":17},{"type":13,"children":1944},[1945],{"text":1946,"type":17},"Burada dikkat ettiğim noktalar şunlardı:",{"type":13,"children":1948},[1949],{"text":21,"type":17},{"type":486,"format":487,"children":1951},[1952],{"type":490,"children":1953},[1954],{"text":1955,"type":17},"Görseli, görüntüleneceği alana uygun boyutta sunmak.",{"type":486,"format":487,"children":1957},[1958],{"type":490,"children":1959},[1960],{"text":1961,"type":17},"Desteklenen modern görsel formatlarından yararlanmak.",{"type":486,"format":487,"children":1963},[1964],{"type":490,"children":1965},[1966],{"text":1967,"type":17},"Ana görselin yüklenmesini gereksiz yere geciktirmemek.",{"type":486,"format":487,"children":1969},[1970],{"type":490,"children":1971},[1972],{"text":1973,"type":17},"Genişlik ve yükseklik bilgilerini belirterek sayfa düzeninin yükleme sırasında kaymasını azaltmak.",{"type":13,"children":1975},[1976],{"text":21,"type":17},{"type":13,"children":1978},[1979],{"text":1980,"type":17},"Bu adımlar, sayfanın en büyük görünür öğesinin yüklenme süresini ifade eden LCP’yi iyileştirmek için önemliydi.",{"type":13,"children":1982},[1983],{"text":21,"type":17},{"type":37,"level":38,"children":1985},[1986],{"text":1987,"type":17},"Strapi tarafındaki bekleme süresi",{"type":13,"children":1989},[1990],{"text":21,"type":17},{"type":13,"children":1992},[1993],{"text":1994,"type":17},"Frontend tarafındaki değişikliklerin yanında, içeriklerin geldiği Strapi uygulamasını da değerlendirdim.",{"type":13,"children":1996},[1997],{"text":21,"type":17},{"type":13,"children":1999},[2000],{"text":2001,"type":17},"Render’ın ücretsiz planında uygulamanın bir süre kullanılmadığında uykuya geçmesi, sonraki istekte yeniden başlamasını beklememe neden oluyordu. Bu gecikme, içeriklerin alınmasını ve dolayısıyla sayfanın açılmasını etkileyebiliyordu.",{"type":13,"children":2003},[2004],{"text":21,"type":17},{"type":13,"children":2006},[2007],{"text":2008,"type":17},"Bu durumu ele almak için düzenli erişim kontrollerini, uygulamanın sürekli çalışacağı bir planı ve önbellekleme seçeneklerini değerlendirdim. Burada önemli olan, gecikmenin hangi katmanda oluştuğunu ayırabilmekti. Frontend’de yapılan bir iyileştirme, içerik servisinin geç yanıt vermesini tek başına çözmeyebilirdi.",{"type":13,"children":2010},[2011],{"text":21,"type":17},{"type":37,"level":38,"children":2013},[2014],{"text":2015,"type":17},"Sonuçları ölçmek",{"type":13,"children":2017},[2018],{"text":21,"type":17},{"type":13,"children":2020},[2021],{"text":2022,"type":17},"Değişikliklerin etkisini anlamak için Lighthouse sonuçlarını ve yükleme sürelerini karşılaştırdım. Yalnızca toplam performans puanına bakmak yerine, hangi metriğin nasıl değiştiğini değerlendirmeye çalıştım.",{"type":13,"children":2024},[2025],{"text":21,"type":17},{"type":13,"children":2027},[2028],{"text":2029,"type":17},"Karşılaştırdığım ölçümlerde sonuçlar şöyleydi:",{"type":582,"image":2031,"children":2067},{"ext":2032,"url":2033,"hash":2034,"mime":2035,"name":2036,"size":2037,"width":578,"height":2038,"caption":567,"formats":2039,"provider":613,"createdAt":2064,"updatedAt":2065,"previewUrl":567,"alternativeText":2036,"provider_metadata":2066},".png","https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791493040\u002Flarge_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5.png","large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5","image\u002Fpng","large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee.png",69.47,566,{"small":2040,"medium":2048,"thumbnail":2056},{"ext":2032,"url":2041,"hash":2042,"mime":2035,"name":2043,"path":567,"size":2044,"width":588,"height":2045,"sizeInBytes":2046,"provider_metadata":2047},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791493040\u002Fsmall_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5.png","small_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5","small_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee.png",98.91,283,98914,{"public_id":2042,"resource_type":582},{"ext":2032,"url":2049,"hash":2050,"mime":2035,"name":2051,"path":567,"size":2052,"width":597,"height":2053,"sizeInBytes":2054,"provider_metadata":2055},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791493040\u002Fmedium_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5.png","medium_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5","medium_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee.png",217.21,425,217214,{"public_id":2050,"resource_type":582},{"ext":2032,"url":2057,"hash":2058,"mime":2035,"name":2059,"path":567,"size":2060,"width":606,"height":2061,"sizeInBytes":2062,"provider_metadata":2063},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791493040\u002Fthumbnail_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5.png","thumbnail_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee_facacc92c5","thumbnail_large_nuxt_3_performans_iyilesme_tablosu_c8fd60e0ee.png",31.28,139,31282,{"public_id":2058,"resource_type":582},"2026-10-08T20:57:21.029Z","2026-10-08T20:57:46.424Z",{"public_id":2034,"resource_type":582},[2068],{"text":21,"type":17},{"type":13,"children":2070},[2071],{"text":21,"type":17},{"type":13,"children":2073},[2074],{"text":2075,"type":17},"Bu değerler karşılaştırılan testlere ait sonuçlardı. Cihaz, bağlantı ve önbellek durumu değiştiğinde farklı sonuçlar görülebileceği için bunları her ziyaretçi için sabit bir yükleme süresi olarak değerlendirmedim.",{"type":13,"children":2077},[2078],{"text":21,"type":17},{"type":37,"level":38,"children":2080},[2081],{"text":2082,"type":17},"Bu süreçte öğrendiklerim",{"type":13,"children":2084},[2085],{"text":21,"type":17},{"type":13,"children":2087},[2088],{"text":2089,"type":17},"Performans sorunlarını incelerken tek bir değişikliğin bütün gecikmeleri çözmesini beklememek gerektiğini gördüm. Sunucu konumu, sayfaların oluşturulma biçimi, görseller ve içerik servisinin yanıt süresi birlikte değerlendirilmesi gereken konulardı.",{"type":13,"children":2091},[2092],{"text":21,"type":17},{"type":13,"children":2094},[2095],{"text":2096,"type":17},"Benim için en faydalı yaklaşım, önce gecikmenin kaynağını anlamak, ardından yaptığım değişikliklerin etkisini ölçmek oldu. Böylece yalnızca daha yüksek bir performans puanı elde etmeye değil, sitenin yükleme sürecini daha iyi anlamaya da odaklandım.","2026-04-18","2026-10-08T21:00:00.813Z","2026-10-09T17:48:13.639Z","2026-10-09T17:48:13.746Z","Nuxt 3, Vue, Web Performansı, Lighthouse, Optimizasyon, Kişisel Portföy","Nuxt 3 Portföyümde Web Performansını Nasıl İyileştirdim?","Nuxt 3 portföyümde performans sorunlarını inceleme ve iyileştirme deneyimim: Lighthouse ölçümleri, uyguladığım değişiklikler ve öğrendiklerim.",{"id":2105,"documentId":2106,"name":2107,"alternativeText":567,"caption":567,"focalPoint":567,"width":568,"height":569,"formats":2108,"hash":2137,"ext":572,"mime":575,"size":2138,"url":2139,"previewUrl":567,"provider":613,"provider_metadata":2140,"createdAt":2141,"updatedAt":2142,"publishedAt":2141},10,"wz09jj2dazctqind4g1r1x2e","nuxt-3-portfoyumde-performansi-iyilestirme-suereci.webp",{"large":2109,"small":2116,"medium":2123,"thumbnail":2130},{"ext":572,"url":2110,"hash":2111,"mime":575,"name":2112,"path":567,"size":2113,"width":578,"height":579,"sizeInBytes":2114,"provider_metadata":2115},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791568078\u002Flarge_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1.webp","large_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1","large_nuxt-3-portfoyumde-performansi-iyilestirme-suereci.webp",41.81,41810,{"public_id":2111,"resource_type":582},{"ext":572,"url":2117,"hash":2118,"mime":575,"name":2119,"path":567,"size":2120,"width":588,"height":589,"sizeInBytes":2121,"provider_metadata":2122},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791568078\u002Fsmall_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1.webp","small_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1","small_nuxt-3-portfoyumde-performansi-iyilestirme-suereci.webp",17.64,17638,{"public_id":2118,"resource_type":582},{"ext":572,"url":2124,"hash":2125,"mime":575,"name":2126,"path":567,"size":2127,"width":597,"height":598,"sizeInBytes":2128,"provider_metadata":2129},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791568078\u002Fmedium_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1.webp","medium_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1","medium_nuxt-3-portfoyumde-performansi-iyilestirme-suereci.webp",29.36,29362,{"public_id":2125,"resource_type":582},{"ext":572,"url":2131,"hash":2132,"mime":575,"name":2133,"path":567,"size":2134,"width":606,"height":607,"sizeInBytes":2135,"provider_metadata":2136},"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791568076\u002Fthumbnail_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1.webp","thumbnail_nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1","thumbnail_nuxt-3-portfoyumde-performansi-iyilestirme-suereci.webp",6.82,6818,{"public_id":2132,"resource_type":582},"nuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1",82.33,"https:\u002F\u002Fres.cloudinary.com\u002Fdgbo8yrxg\u002Fimage\u002Fupload\u002Fv1791568076\u002Fnuxt_3_portfoyumde_performansi_iyilestirme_suereci_bc41c6d4a1.webp",{"public_id":2137,"resource_type":582},"2026-10-09T17:47:58.867Z","2026-10-09T17:48:09.036Z",{"pagination":2144},{"page":2145,"pageSize":2146,"pageCount":2145,"total":2147},1,25,4]