Docker Compose ile Next.js ve NestJS production deploy rehberi
Docker Compose ile Next.js ve NestJS production deploy rehberi
Bir uygulamayı yerelde çalıştırmak ile production'da güvenilir biçimde çalıştırmak farklı problemlerdir. Yerelde localhost, açık portlar ve yeniden başlatılan geliştirme sunucuları yeterlidir. Sunucuda ise servislerin birbirini nasıl bulduğu, gizli bilgilerin nerede saklandığı, image'ın ne kadar küçük olduğu ve hata anında nereden bakılacağı önem kazanır.
Bu rehber; Next.js web uygulaması, NestJS API ve Vite ile üretilen yönetim panelinden oluşan bir sistemi Docker Compose ile yayına almak için kullandığım yaklaşımı anlatıyor.
Önce servisleri ayırın
Her uygulamanın kendi sorumluluğu ve container'ı olmalı:
- web: Ziyaretçinin gördüğü Next.js uygulaması.
- api: Veritabanı ve dosya depolamayla konuşan NestJS servisi.
- admin: Yönetim arayüzünün statik çıktısını sunan Nginx container'ı.
Bu ayrım, bir API güncellemesinin web image'ını gereksiz yere değiştirmesini engeller. Ayrıca loglar, kaynak tüketimi ve yeniden başlatmalar servis bazında takip edilir.
localhost tuzağından kaçının
Container içinde localhost, aynı makinedeki başka bir container değil, o container'ın kendisidir. Web uygulamanızın API'ye http://localhost:3901 ile bağlanmaya çalışması bu yüzden production'da hata verir. Compose ağında servis adı DNS adı olur:
services:
api:
ports:
- "3901:3901"
web:
environment:
API_ORIGIN: http://api:3901
depends_on:
- api
Web container'ı API'ye http://api:3901 üzerinden gider. Tarayıcı tarafında ise kullanıcı aynı origin'den /api/* çağrısı yapabilir; Next.js rewrites bu isteği iç ağdaki API'ye iletir. Böylece gereksiz CORS karmaşası kullanıcı deneyimine taşınmaz.
Build-time ile runtime sırlarını ayırın
Production yapılandırmasında iki tür değer vardır. APP_URL gibi statik HTML'e ve metadata'ya giren değerler build-time'da gerekir. DATABASE_URL, MinIO erişim anahtarları ve admin token gibi gizli değerler ise runtime'da container'a verilmelidir.
web:
build:
args:
APP_URL: ${APP_URL}
API_ORIGIN: http://api:3901
env_file:
- .env.production
.env.production dosyasını repoya koymayın. Örnek dosyayı (.env.production.example) versiyonlayın, gerçek değerleri sadece sunucuda tutun. Image'a kopyalanan bir sır, image registry'sine veya build cache'ine sızabilir.
Küçük image, daha temiz runtime
Multi-stage Dockerfile, build araçlarını final image'dan ayırır. İlk katman pnpm, TypeScript ve Prisma CLI gibi araçları içerir. Final katman yalnızca derlenmiş çıktı ile production bağımlılıklarını taşır.
API için pnpm deploy --prod, çalışma anında gerekli bağımlılıkları izole eder. Next.js için output: "standalone", izlenmiş server çıktısını üretir. Buna ek olarak .next/static ve public klasörlerinin runtime image'a kopyalandığını doğrulayın; bunlar standalone paketin parçası değildir.
Bu yapı yalnızca disk alanı tasarrufu sağlamaz. Daha az paket, daha az saldırı yüzeyi ve daha hızlı image transferi anlamına gelir.
Deploy komutu ve temel kontroller
Sunucuda ilk kez kurarken:
cp .env.production.example .env.production
# Gerçek değerleri girin
docker compose --env-file .env.production up -d --build
Sonraki yayınlarda aynı komut güncel image'ları üretir ve container'ları yeniden oluşturur. Ancak komutu “başarılı” kabul etmeden önce şu kontrolleri yapın:
docker compose ps
docker compose logs --tail=100 api
docker compose logs --tail=100 web
ps her servisin çalıştığını gösterir; loglar ise veritabanı bağlantısı, eksik environment değişkeni veya port hatasını doğrudan gösterir. API'nin sağlık endpoint'i yoksa en azından beklenen public endpoint'i sunucunun içinden curl ile çağırın.
Veriyi Compose'tan bağımsız tutmak
MariaDB ve MinIO gibi veri servisleri uygulama container'larından ayrı yönetildiğinde, uygulama deploy'u ile veri yaşam döngüsü birbirine karışmaz. Bunun karşılığında yedekleme sorumluluğu daha görünür olur: düzenli veritabanı dump'ı, obje deposu yedeği ve geri yükleme denemesi gerekir.
Production'da başarı, yalnızca container'ların “up” görünmesi değildir. Bir yazının kaydedilmesi, webde görünmesi, görselin yüklenmesi ve admin oturumunun çalışması gibi gerçek kullanıcı akışlarını test edin.
Sık görülen üç hata
API için localhost kullanmak: Servis adına geçin; api:3901 gibi.
Yanlış APP_URL ile build almak: Canonical URL ve Open Graph verisi hatalı kalır. Değeri build argümanı olarak verin ve yeniden build edin.
Sırları Dockerfile'a COPY etmek: Sırlar image geçmişinde kalabilir. env_file ya da güvenli bir secret yönetimi kullanın.
Bu yaklaşım, Turborepo ve Docker ile üretime hazır bir monorepo kurmak yazısındaki monorepo build düzeninin production tarafındaki karşılığıdır. Önce sınırları netleştirin, sonra servis adları ve environment sözleşmeleriyle uygulayın.